diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..eaf4170 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,38 @@ +kind: pipeline +name: default + +steps: + - name: Build Bullseye + image: debian:bullseye + volumes: + - name: finished_files + path: /deb_files + commands: + - apt update + - apt -y upgrade + - apt -y install --no-install-recommends build-essential equivs devscripts git rename + - git clean -f -d -x + - mk-build-deps --install --tool='apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends --yes' debian/control + - dpkg-buildpackage -b -uc + - rename 's/\.deb/_bullseye\.deb/' ../*.deb + - mkdir -p /deb_files/bullseye/ + - cp ../*.deb /deb_files/bullseye/ + - find /deb_files/ + + - name: gitea_release + image: plugins/gitea-release + volumes: + - name: finished_files + path: /deb_files + settings: + api_key: + from_secret: GITEA_KEY + base_url: https://git.neulandlabor.de/ + files: + - /deb_files/bullseye/* + when: + event: tag + +volumes: + - name: finished_files + temp: {} diff --git a/AUTHORS b/AUTHORS index 320efbe..c179b0c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -170,3 +170,7 @@ D: pwmib support for snmp-ups N: Kjell Claesson E: Kjell.claesson@epost.tidanet.se D: Author of bcmxcp driver, 3-phase work. + +N: Giuseppe Corbelli +E: giuseppe.corbelli@copanitalia.com +D: Author of asem driver diff --git a/ChangeLog b/ChangeLog index ceb1271..669274b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,2688 +1,23389 @@ -2012-01-04 Arnaud Quette - - * [r3371] docs/website/news.txt: Update 2.6.3 release date - -2012-01-03 Arnaud Quette - - * [r3370] NEWS, UPGRADING, configure.in, docs/website/news.txt: - Final update for 2.6.3 release - -2012-01-03 Frederic Bohe - - * [r3369] docs/config-notes.txt: Add a note about file names with - space characters in the documentation. - -2012-01-03 Arnaud Quette - - * [r3368] drivers/dummy-ups.c: Add a comment for further processing - -2012-01-03 Frederic Bohe - - * [r3367] tools/nut-scanner/nut-scanner.c: Fix nut scanner SNMP v3 - help message. - * [r3366] docs/man/nut-scanner.txt: Fix nut scanner SNMP v3 - documentation. - -2011-12-17 Arnaud Quette - - * [r3364] drivers/blazer_usb.c: Actually apply the langid_fix - value, when calling usb_get_string (reported by Charles Lepple) - * [r3363] data/driver.list.in, docs/man/blazer.txt, - drivers/blazer_usb.c: Try to fix language ID support for USB - units from LDLC, Dynamix and no names in blazer_usb (reworked - patch, from Brian R. Smith and Aurélien Grenotton) - -2011-12-16 Arnaud Quette - - * [r3362] scripts/HP-UX: Add the generated HP-UX packaging script - (.psf) to the list of Subversion ignored files - * [r3361] drivers/eaton-mib.c, drivers/snmp-ups.c: Fix newer Eaton - ePDU OIDs declaration ordering, so that it better supports the - various models (switched or advanced monitored) - -2011-12-15 Arnaud Quette - - * [r3360] clients/upsclient.c, drivers/belkinunv.c, - drivers/etapro.c, drivers/gamatronic.c, drivers/isbmex.c, - drivers/libshut.c, drivers/metasys.c, drivers/mge-shut.c, - drivers/microdowell.c, drivers/nut-libfreeipmi.c, - drivers/rhino.c, drivers/solis.c: Fix set but not used variables - [-Wunused-but-set-variable] (including patch from Regid Ichira) - -2011-12-13 Arnaud Quette - - * [r3359] Makefile.am, configure.in, docs/man, - docs/man/Makefile.am, docs/man/nutscan_init.txt, - docs/man/nutscan_scan_avahi.txt, docs/man/nutscan_scan_ipmi.txt, - docs/man/nutscan_scan_nut.txt, docs/man/nutscan_scan_snmp.txt, - docs/man/nutscan_scan_usb.txt, - docs/man/nutscan_scan_xml_http.txt, drivers/nut-libfreeipmi.c, - m4/nut_check_libfreeipmi.m4, m4/nut_check_libltdl.m4, - tools/nut-scanner/Makefile.am, tools/nut-scanner/nut-scan.h, - tools/nut-scanner/nut-scanner.c, - tools/nut-scanner/nutscan-init.c, - tools/nut-scanner/nutscan-init.h, tools/nut-scanner/scan_avahi.c, - tools/nut-scanner/scan_ipmi.c, tools/nut-scanner/scan_nut.c, - tools/nut-scanner/scan_snmp.c, tools/nut-scanner/scan_usb.c, - tools/nut-scanner/scan_xml_http.c: Merge nut-scanner_dlopen - branch, at revision 3358. This brings weak runtime dependencies - to nut-scanner, allowing to compile with all options and to run - according to the available dependencies. It also adds IPMI scan - * [r3358] compile: Update compile script, as per the latest version - available in Automake (1.11) - -2011-12-05 Arnaud Quette - - * [r3341] data/driver.list.in, docs/man/snmp-ups.txt, - drivers/Makefile.am, drivers/cyberpower-mib.c, - drivers/cyberpower-mib.h, drivers/snmp-ups.c: Add Cyber Power - Systems with SNMP RMCARD (100, 201, 202 and 301) to the list of - snmp-ups supported models (patch from Eric Schultz) - * [r3340] drivers/snmp-ups.c: Remove the Eaton Marlin hook, for - base outlet index, since it has been fixed by a new firmware - revision - -2011-11-29 Arnaud Quette - - * [r3338] tools/nut-snmpinfo.py: Fix indentation and use open() - instead of file(), to better support Python 3 (Alioth bug - #313446, patch from Bohdan Popovych) - -2011-11-25 Arnaud Quette - - * [r3335] drivers/nut-ipmi.h: Fix wrong prototype declaration, that - was causing a compilation warning (implicit declaration of - function ‘nut_ipmi_get_sensors_status’) - -2011-11-24 Prachi Gandhi - - * [r3328] Makefile.am, configure.in, scripts/Makefile.am, - scripts/Solaris: Reverted changes made for Solaris packaging - files(revision 3326), added files in NUT_packaging branch - * [r3326] Makefile.am, configure.in, scripts/Makefile.am, - scripts/Solaris, scripts/Solaris/Makefile.am, - scripts/Solaris/makelocal.sh, scripts/Solaris/pkginfo.in, - scripts/Solaris/prototype: Solaris NUT package files added - -2011-11-15 Arnaud Quette - - * [r3321] drivers/blazer_usb.c, drivers/usb-common.h: Revert commit - r2993 and r2994. This enables again inclusion of buggy USB Device - and Vendor IDs, supported by blazer_usb, in udev rules file - (Reported by Stanislav Brabec, from Suse) - * [r3320] tools/Makefile.am: Add missing comment - -2011-11-12 Arnaud Quette - - * [r3318] drivers/mge-mib.c: Add upsmgBatteryLowCondition OID to - the status mapping (reported by Kiss Gabor (Bitman)) - -2011-11-11 Praveen Kumar - - * [r3317] scripts/HP-UX/makedepot.sh: script file to create package - (depot) for HP-UX - -2011-11-08 Arnaud Quette - - * [r3316] docs/website/web-layout.conf: Complete search keywords - * [r3315] docs/website/web-layout.conf: Final fix for the - displaying of the legend, to work on all browsers, while still - being conforming to W3C validation - -2011-11-07 Arnaud Quette - - * [r3314] docs/website/web-layout.conf: Fix displaying of the - legend - -2011-11-05 Michal Soltys - - * [r3313] docs/cables.txt, docs/man/apcsmart.txt: apcsmart.txt: - minor documentation update - - Broken diagram link and 940-0024E cable mention (reported by - Jonathan - Laventhol). - -2011-11-05 Arnaud Quette - - * [r3312] conf/upsd.conf.sample, server/upsd.c: Don't fail to start - if at least one of the listening interface is available. This is - needed on systems where either IPv4 or IPv6 is disabled, and no - explicit LISTEN directive has been specified (Reported by Pavel - Zubkou, Debian bug #598741) - -2011-11-02 Praveen Kumar - - * [r3310] configure.in, scripts/HP-UX/nut.psf.in: Adding packaging - script for HPUX - -2011-10-31 Arnaud Quette - - * [r3308] docs/download.txt, docs/website/news.txt: Publish update - jNut 0.2, along with the new jNutWebAPI (contributed by Emilien - Kia, from Eaton) - -2011-10-28 Emilien Kia - - * [r3306] scripts/java/README, scripts/java/jNutWebAPI, - scripts/java/jNutWebAPI/README, scripts/java/jNutWebAPI/pom.xml, - scripts/java/jNutWebAPI/src, scripts/java/jNutWebAPI/src/main, - scripts/java/jNutWebAPI/src/main/java, - scripts/java/jNutWebAPI/src/main/java/org, - scripts/java/jNutWebAPI/src/main/java/org/networkupstools, - scripts/java/jNutWebAPI/src/main/java/org/networkupstools/jnutwebapi, - scripts/java/jNutWebAPI/src/main/java/org/networkupstools/jnutwebapi/NutRestProvider.java, - scripts/java/jNutWebAPI/src/main/java/org/networkupstools/jnutwebapi/RestWSApplication.java, - scripts/java/jNutWebAPI/src/main/java/org/networkupstools/jnutwebapi/ScannerProvider.java, - scripts/java/jNutWebAPI/src/main/resources, - scripts/java/jNutWebAPI/src/main/webapp, - scripts/java/jNutWebAPI/src/main/webapp/WEB-INF, - scripts/java/jNutWebAPI/src/main/webapp/WEB-INF/web.xml, - scripts/java/jNutWebAPI/src/test, - scripts/java/jNutWebAPI/src/test/java, - scripts/java/jNutWebAPI/src/test/java/org, - scripts/java/jNutWebAPI/src/test/java/org/networkupstools, - scripts/java/jNutWebAPI/src/test/java/org/networkupstools/jnutwebapi: - Initial commit of jNutWebAPI. - * [r3305] scripts/java/jNut, - scripts/java/jNut/src/main/java/org/networkupstools/jnut/Scanner.java: - Fix a little bug with function namming (get instead of set). - -2011-10-27 Arnaud Quette - - * [r3304] docs/maintainer-guide.txt: Create a basic NUT maintainer - guide, to start tracking and improving maintenance workflow - * [r3303] drivers/bcmxcp_usb.c: Handle disconnection issues and - reconnection mechanism (bug reported by Rich Wrenn) - -2011-10-25 Emilien Kia - - * [r3302] - scripts/java/jNut/src/test/java/org/networkupstools/jnut/ClientTest.java: - Add licence information. - * [r3301] scripts/java/jNut/README, scripts/java/jNut/pom.xml, - scripts/java/jNut/src/main/java/org/networkupstools/jnut/Scanner.java, - scripts/java/jNut/src/test/java/org/networkupstools/jnut/ScannerTest.java: - Add nut-scanner. - -2011-10-25 Frederic Bohe - - * [r3300] tools/nut-scanner/scan_snmp.c: Add sanity checks - * [r3299] tools/nut-scanner/scan_avahi.c: Remove unused variable - -2011-10-25 Michal Soltys - - * [r3298] drivers/apcsmart.c: drivers/apcsmart.c: minor correction - - Assigning 0 directly was wrong (actually, a leftover from earlier - version of the code that was removed) - ser_set_speed() prepares - the - port initially, we only adjust certain options. - -2011-10-24 Frederic Bohe - - * [r3297] docs/man/nut-scanner.txt, - tools/nut-scanner/nut-scanner.c: Forgot to document "-q" option - (thanks to Emilien Kia for reporting this) - -2011-10-21 Frederic Bohe - - * [r3296] tools/nut-scanner/nut-scanner.c: Trivial layout - consistency - * [r3295] docs/man/nut-scanner.txt: Update man page with -V and -a - option - * [r3294] tools/nut-scanner/nut-scanner.c: Add an option to display - available bus (usefull for wrapper). - * [r3293] tools/nut-scanner/nut-scanner.c: return -1 on bad command - line option - -2011-10-19 Frederic Bohe - - * [r3292] tools/nut-scanner/nutscan-ip.c: Fix crash on 32bits - systems - * [r3291] tools/nut-scanner/nut-scanner.c: Add a switch to display - NUT version. - -2011-10-19 Charles Lepple - - * [r3290] docs/FAQ.txt: Add FAQ entry for LISTEN directive - -2011-10-18 Arnaud Quette - - * [r3289] drivers/eaton-mib.c: Fix outlets reported current, which - were off by 100 in aphel_genesisII MIB, and bump MIB version to - 0.46 (patch from Sven Nilsson) - -2011-10-14 Arnaud Quette - - * [r3288] docs/Makefile.am, docs/images/nut-logo.png, - docs/images/nut.svg, docs/website/Makefile.am, - docs/website/css/web-layout.css, docs/website/faviconut.ico, - docs/website/faviconut.png, docs/website/web-layout.conf: Add the - new NUT logo (contributed by Luc Descotils, from Eaton) - * [r3287] docs/website/web-layout.conf: Fix another W3C validator - error (there is no attribute "language" anymore for - -endif::toc[] -ifdef::hcl[] - - - -endif::hcl[] - - - - -
-
- -
Network UPS Tools
-
Power Devices support
-
-
- -
-
-# Article, book header. -ifndef::doctype-manpage[] - -ifdef::toc[] -
-
Table of Contents
- -
-endif::toc[] -endif::doctype-manpage[] -# Man page header. -ifdef::doctype-manpage[] - -endif::doctype-manpage[] - -[footer] - -
-
- - -endif::analytics[] - - diff --git a/docs/website/website.txt b/docs/website/website.txt deleted file mode 100644 index 18ff58a..0000000 --- a/docs/website/website.txt +++ /dev/null @@ -1,19 +0,0 @@ -Welcome -======= - -The primary goal of the Network UPS Tools (NUT) project is to provide support -for Power Devices, such as Uninterruptible Power Supplies, Power Distribution -Units and Solar Controllers. - -NUT provides many control and monitoring link:features.html[features], with a -uniform control and management interface. - -More than 100 different manufacturers, and several thousands models are -link:stable-hcl.html[compatible]. - -This software is the combined effort of many -link:acknowledgements.html[individuals and companies]. - -News ----- -include::news.txt[] diff --git a/docs/xhtml.xsl b/docs/xhtml.xsl new file mode 100644 index 0000000..e8dc331 --- /dev/null +++ b/docs/xhtml.xsl @@ -0,0 +1,14 @@ + + + + + diff --git a/drivers/Makefile.am b/drivers/Makefile.am index 72630d9..98fe667 100644 --- a/drivers/Makefile.am +++ b/drivers/Makefile.am @@ -1,12 +1,17 @@ # Network UPS Tools: drivers +# Make sure out-of-dir dependencies exist (especially when dev-building parts): +$(top_builddir)/common/libcommon.la \ +$(top_builddir)/common/libparseconf.la \ +$(top_builddir)/clients/libupsclient.la: dummy + @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + # by default, link programs in this directory with libcommon.la # (libtool version of the static lib, in order to access LTLIBOBJS) #FIXME: SERLIBS is only useful for LDADD_DRIVERS_SERIAL not for LDADD_COMMON -LDADD_COMMON = ../common/libcommon.la ../common/libparseconf.la -LDADD_DRIVERS = $(LDADD_COMMON) main.o dstate.o -LDADD_HAL_DRIVERS = $(LDADD_COMMON) libnuthalmain.a -LDADD_DRIVERS_SERIAL = $(LDADD_DRIVERS) $(SERLIBS) serial.o +LDADD_COMMON = $(top_builddir)/common/libcommon.la $(top_builddir)/common/libparseconf.la +LDADD_DRIVERS = libdummy.la $(LDADD_COMMON) +LDADD_DRIVERS_SERIAL = libdummy_serial.la $(LDADD_DRIVERS) $(SERLIBS) # most targets are drivers, so make this the default LDADD = $(LDADD_DRIVERS_SERIAL) @@ -14,15 +19,9 @@ LDADD = $(LDADD_DRIVERS_SERIAL) # files. In any case, CFLAGS are only -I options, so there is no harm, # but only add them if we really use the target. AM_CFLAGS = -I$(top_srcdir)/include -if WITH_HAL - AM_CFLAGS += $(LIBHAL_CFLAGS) -endif if WITH_USB AM_CFLAGS += $(LIBUSB_CFLAGS) endif -if WITH_SNMP - AM_CFLAGS += $(LIBNETSNMP_CFLAGS) -endif if WITH_NEON AM_CFLAGS += $(LIBNEON_CFLAGS) endif @@ -32,23 +31,34 @@ endif if WITH_IPMI AM_CFLAGS += $(LIBIPMI_CFLAGS) endif +if WITH_MODBUS + AM_CFLAGS += $(LIBMODBUS_CFLAGS) +endif -SERIAL_DRIVERLIST = bcmxcp belkin belkinunv bestfcom \ +SERIAL_DRIVERLIST = al175 bcmxcp belkin belkinunv bestfcom \ bestfortress bestuferrups bestups dummy-ups etapro everups \ gamatronic genericups isbmex liebert liebert-esp2 masterguard metasys \ - mge-shut mge-utalk microdowell newmge-shut oneac optiups powercom rhino \ - safenet skel solis tripplite tripplitesu upscode2 victronups powerpanel \ - blazer_ser clone clone-outlet ivtscd apcsmart apcsmart-old + mge-utalk microdowell microsol-apc mge-shut oneac optiups powercom rhino \ + safenet nutdrv_siemens-sitop skel solis tripplite tripplitesu upscode2 victronups powerpanel \ + blazer_ser clone clone-outlet ivtscd apcsmart apcsmart-old apcupsd-ups riello_ser SNMP_DRIVERLIST = snmp-ups USB_LIBUSB_DRIVERLIST = usbhid-ups bcmxcp_usb tripplite_usb \ - blazer_usb richcomm_usb + blazer_usb richcomm_usb riello_usb \ + nutdrv_atcl_usb USB_DRIVERLIST = $(USB_LIBUSB_DRIVERLIST) -HAL_DRIVERLIST = hald-addon-usbhid-ups hald-addon-bcmxcp_usb \ - hald-addon-tripplite_usb hald-addon-blazer_usb +SERIAL_USB_DRIVERLIST = \ + nutdrv_qx NEONXML_DRIVERLIST = netxml-ups +MACOSX_DRIVERLIST = macosx-ups +MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 socomec_jbus adelsystem_cbi +LINUX_I2C_DRIVERLIST = asem pijuice +POWERMAN_DRIVERLIST = powerman-pdu +IPMI_DRIVERLIST = nut-ipmipsu # distribute all drivers, even ones that are not built by default -EXTRA_PROGRAMS = $(SERIAL_DRIVERLIST) $(SNMP_DRIVERLIST) $(USB_DRIVERLIST) $(NEONXML_DRIVERLIST) +EXTRA_PROGRAMS = $(SERIAL_DRIVERLIST) $(USB_DRIVERLIST) $(SERIAL_USB_DRIVERLIST) +EXTRA_PROGRAMS += $(SNMP_DRIVERLIST) $(NEONXML_DRIVERLIST) $(MACOSX_DRIVERLIST) +EXTRA_PROGRAMS += $(LINUX_I2C_DRIVERLIST) # construct the list of drivers to build if SOME_DRIVERS @@ -56,7 +66,11 @@ if SOME_DRIVERS else driverexec_PROGRAMS = if WITH_SERIAL - driverexec_PROGRAMS += $(SERIAL_DRIVERLIST) + driverexec_PROGRAMS += $(SERIAL_DRIVERLIST) $(SERIAL_USB_DRIVERLIST) +else +if WITH_USB + driverexec_PROGRAMS += $(SERIAL_USB_DRIVERLIST) +endif endif if WITH_SNMP driverexec_PROGRAMS += $(SNMP_DRIVERLIST) @@ -64,25 +78,30 @@ endif if WITH_USB driverexec_PROGRAMS += $(USB_LIBUSB_DRIVERLIST) endif -if WITH_HAL - halexecdir = $(HAL_CALLOUTS_PATH) - halexec_PROGRAMS = $(HAL_DRIVERLIST) -endif if WITH_NEON driverexec_PROGRAMS += $(NEONXML_DRIVERLIST) endif if WITH_LIBPOWERMAN - driverexec_PROGRAMS += powerman-pdu + driverexec_PROGRAMS += $(POWERMAN_DRIVERLIST) endif if WITH_IPMI - driverexec_PROGRAMS += nut-ipmipsu + driverexec_PROGRAMS += $(IPMI_DRIVERLIST) +endif +if WITH_MACOSX + driverexec_PROGRAMS += $(MACOSX_DRIVERLIST) +endif +if WITH_LINUX_I2C + driverexec_PROGRAMS += $(LINUX_I2C_DRIVERLIST) +endif +if WITH_MODBUS + driverexec_PROGRAMS += $(MODBUS_DRIVERLIST) endif else driverexec_PROGRAMS += skel endif # always build upsdrvctl -driverexec_PROGRAMS += upsdrvctl +sbin_PROGRAMS = upsdrvctl # ========================================================================== # Driver build details @@ -92,6 +111,7 @@ upsdrvctl_SOURCES = upsdrvctl.c upsdrvctl_LDADD = $(LDADD_COMMON) # serial drivers: all of them use standard LDADD and CFLAGS +al175_SOURCES = al175.c apcsmart_SOURCES = apcsmart.c apcsmart_tabs.c apcsmart_old_SOURCES = apcsmart-old.c bcmxcp_SOURCES = bcmxcp.c bcmxcp_ser.c @@ -99,6 +119,7 @@ bcmxcp_LDADD = $(LDADD) -lm belkin_SOURCES = belkin.c belkinunv_SOURCES = belkinunv.c bestfcom_SOURCES = bestfcom.c +bestfortress_SOURCES = bestfortress.c bestuferrups_SOURCES = bestuferrups.c bestups_SOURCES = bestups.c blazer_ser_SOURCES = blazer.c blazer_ser.c @@ -114,9 +135,11 @@ liebert_SOURCES = liebert.c liebert_esp2_SOURCES = liebert-esp2.c masterguard_SOURCES = masterguard.c metasys_SOURCES = metasys.c -mge_shut_SOURCES = mge-shut.c hidparser.c +metasys_LDADD = $(LDADD) -lm mge_utalk_SOURCES = mge-utalk.c microdowell_SOURCES = microdowell.c +microsol_apc_SOURCES = microsol-apc.c microsol-common.c +microsol_apc_LDADD = $(LDADD) -lm oneac_SOURCES = oneac.c optiups_SOURCES = optiups.c powercom_SOURCES = powercom.c @@ -126,20 +149,24 @@ powerpanel_LDADD = $(LDADD) -lm rhino_SOURCES = rhino.c rhino_LDADD = $(LDADD) -lm safenet_SOURCES = safenet.c +nutdrv_siemens_sitop_SOURCES = nutdrv_siemens_sitop.c solis_SOURCES = solis.c +solis_LDADD = $(LDADD) -lm tripplite_SOURCES = tripplite.c tripplite_LDADD = $(LDADD) -lm tripplitesu_SOURCES = tripplitesu.c upscode2_SOURCES = upscode2.c upscode2_LDADD = $(LDADD) -lm victronups_SOURCES = victronups.c +riello_ser_SOURCES = riello.c riello_ser.c +riello_ser_LDADD = $(LDADD) -lm # non-serial drivers: these use custom LDADD and/or CFLAGS # dummy dummy_ups_SOURCES = dummy-ups.c dummy_ups_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/clients -dummy_ups_LDADD = $(LDADD_DRIVERS) ../clients/libupsclient.la +dummy_ups_LDADD = $(LDADD_DRIVERS) $(top_builddir)/clients/libupsclient.la if WITH_SSL dummy_ups_CFLAGS += $(LIBSSL_CFLAGS) dummy_ups_LDADD += $(LIBSSL_LIBS) @@ -149,55 +176,76 @@ endif clone_SOURCES = clone.c clone_outlet_SOURCES = clone-outlet.c +# apcupsd client driver +apcupsd_ups_SOURCES = apcupsd-ups.c +apcupsd_ups_CFLAGS = $(AM_CFLAGS) +apcupsd_ups_LDADD = $(LDADD_DRIVERS) + # sample skeleton driver skel_SOURCES = skel.c skel_LDADD = $(LDADD_DRIVERS) # USB -USBHID_UPS_SUBDRIVERS = apc-hid.c belkin-hid.c cps-hid.c explore-hid.c \ - liebert-hid.c mge-hid.c powercom-hid.c tripplite-hid.c idowell-hid.c -usbhid_ups_SOURCES = usbhid-ups.c libhid.c libusb.c hidparser.c \ +if WITH_LIBUSB_0_1 +LIBUSB_IMPL = libusb0.c +endif +if WITH_LIBUSB_1_0 +LIBUSB_IMPL = libusb1.c +endif +USBHID_UPS_SUBDRIVERS = apc-hid.c arduino-hid.c belkin-hid.c cps-hid.c explore-hid.c \ + liebert-hid.c mge-hid.c powercom-hid.c tripplite-hid.c idowell-hid.c \ + openups-hid.c powervar-hid.c delta_ups-hid.c ever-hid.c legrand-hid.c salicru-hid.c +usbhid_ups_SOURCES = usbhid-ups.c libhid.c $(LIBUSB_IMPL) hidparser.c \ usb-common.c $(USBHID_UPS_SUBDRIVERS) -usbhid_ups_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) +usbhid_ups_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm -tripplite_usb_SOURCES = tripplite_usb.c libusb.c usb-common.c +tripplite_usb_SOURCES = tripplite_usb.c $(LIBUSB_IMPL) usb-common.c tripplite_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm bcmxcp_usb_SOURCES = bcmxcp_usb.c bcmxcp.c usb-common.c -bcmxcp_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) +bcmxcp_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm -blazer_usb_SOURCES = blazer.c blazer_usb.c libusb.c usb-common.c +blazer_usb_SOURCES = blazer.c blazer_usb.c $(LIBUSB_IMPL) usb-common.c blazer_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm +nutdrv_atcl_usb_SOURCES = nutdrv_atcl_usb.c usb-common.c +nutdrv_atcl_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) + richcomm_usb_SOURCES = richcomm_usb.c usb-common.c richcomm_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) +riello_usb_SOURCES = riello.c riello_usb.c $(LIBUSB_IMPL) usb-common.c +riello_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm + # HID-over-serial -newmge_shut_SOURCES = usbhid-ups.c libshut.c libhid.c hidparser.c mge-hid.c +mge_shut_SOURCES = usbhid-ups.c libshut.c libhid.c hidparser.c mge-hid.c # per-target CFLAGS are necessary here -newmge_shut_CFLAGS = $(AM_CFLAGS) -DSHUT_MODE -newmge_shut_LDADD = $(LDADD) +mge_shut_CFLAGS = $(AM_CFLAGS) -DSHUT_MODE=1 +mge_shut_LDADD = $(LDADD) -lm # SNMP -snmp_ups_SOURCES = snmp-ups.c apc-mib.c baytech-mib.c compaq-mib.c eaton-mib.c \ - ietf-mib.c mge-mib.c netvision-mib.c powerware-mib.c raritan-pdu-mib.c \ - bestpower-mib.c cyberpower-mib.c -snmp_ups_LDADD = $(LDADD_DRIVERS) $(LIBNETSNMP_LIBS) - -# HAL -hald_addon_usbhid_ups_SOURCES = usbhid-ups.c libhid.c libusb.c hidparser.c \ - $(USBHID_UPS_SUBDRIVERS) -hald_addon_usbhid_ups_LDADD = $(LDADD_HAL_DRIVERS) $(LIBUSB_LIBS) $(LIBHAL_LIBS) - -hald_addon_tripplite_usb_SOURCES = tripplite_usb.c libusb.c -hald_addon_tripplite_usb_LDADD = $(LDADD_HAL_DRIVERS) $(LIBUSB_LIBS) $(LIBHAL_LIBS) -lm - -hald_addon_bcmxcp_usb_SOURCES = bcmxcp_usb.c bcmxcp.c -hald_addon_bcmxcp_usb_LDADD = $(LDADD_HAL_DRIVERS) $(LIBUSB_LIBS) $(LIBHAL_LIBS) - -hald_addon_blazer_usb_SOURCES = blazer.c blazer_usb.c libusb.c -hald_addon_blazer_usb_LDADD = $(LDADD_HAL_DRIVERS) $(LIBUSB_LIBS) $(LIBHAL_LIBS) -lm +# Please keep the MIB table below sorted roughly alphabetically (incidentally +# by vendor too) to ease maintenance and codebase fork resynchronisations +snmp_ups_SOURCES = snmp-ups.c snmp-ups-helpers.c \ + apc-mib.c apc-pdu-mib.c \ + baytech-mib.c bestpower-mib.c \ + compaq-mib.c cyberpower-mib.c \ + delta_ups-mib.c \ + eaton-pdu-genesis2-mib.c eaton-pdu-marlin-mib.c eaton-pdu-marlin-helpers.c \ + eaton-pdu-pulizzi-mib.c eaton-pdu-revelation-mib.c \ + eaton-ats16-nmc-mib.c eaton-ats16-nm2-mib.c apc-ats-mib.c eaton-ats30-mib.c \ + emerson-avocent-pdu-mib.c \ + hpe-pdu-mib.c huawei-mib.c \ + ietf-mib.c \ + mge-mib.c \ + netvision-mib.c \ + powerware-mib.c \ + raritan-pdu-mib.c raritan-px2-mib.c \ + xppc-mib.c +snmp_ups_CFLAGS = $(AM_CFLAGS) +snmp_ups_CFLAGS += $(LIBNETSNMP_CFLAGS) +snmp_ups_LDADD = $(LDADD_DRIVERS) $(LIBNETSNMP_LIBS) -lm # NEON XML/HTTP netxml_ups_SOURCES = netxml-ups.c mge-xml.c @@ -210,38 +258,106 @@ powerman_pdu_LDADD = $(LDADD) $(LIBPOWERMAN_LIBS) # IPMI PSU nut_ipmipsu_SOURCES = nut-ipmipsu.c if WITH_FREEIPMI - nut_ipmipsu_SOURCES += nut-libfreeipmi.c + nut_ipmipsu_SOURCES += nut-libfreeipmi.c endif +# FIXME: Hacky hot-fix for build agents of varying OS generations: +# Different versions of IPMI libs requested 'unsigned int *' or 'int *' args: +#nut_ipmipsu_CFLAGS = $(AM_CFLAGS) -Wno-pointer-sign nut_ipmipsu_LDADD = $(LDADD) $(LIBIPMI_LIBS) +# Mac OS X metadriver +macosx_ups_LDADD = $(LDADD_DRIVERS) +macosx_ups_LDFLAGS = $(LDFLAGS) -framework IOKit -framework CoreFoundation +macosx_ups_SOURCES = macosx-ups.c + +# Modbus drivers +phoenixcontact_modbus_SOURCES = phoenixcontact_modbus.c +phoenixcontact_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) +generic_modbus_SOURCES = generic_modbus.c +generic_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) +adelsystem_cbi_SOURCES = adelsystem_cbi.c +adelsystem_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) + +# Huawei UPS2000 driver +# (this is both a Modbus and a serial driver) +huawei_ups2000_SOURCES = huawei-ups2000.c +huawei_ups2000_LDADD = $(LDADD_DRIVERS_SERIAL) $(LIBMODBUS_LIBS) + +# Socomec JBUS driver +# (this is a Modbus driver) +socomec_jbus_SOURCES = socomec_jbus.c +socomec_jbus_LDADD = $(LDADD_DRIVERS_SERIAL) $(LIBMODBUS_LIBS) + +# Linux I2C drivers +asem_LDADD = $(LDADD_DRIVERS) +asem_SOURCES = asem.c +pijuice_LDADD = $(LDADD_DRIVERS) +pijuice_SOURCES = pijuice.c + +# nutdrv_qx USB/Serial +nutdrv_qx_SOURCES = nutdrv_qx.c +nutdrv_qx_LDADD = $(LDADD_DRIVERS) -lm +nutdrv_qx_CFLAGS = $(AM_CFLAGS) +if WITH_SERIAL +nutdrv_qx_CFLAGS += -DQX_SERIAL +nutdrv_qx_LDADD += libdummy_serial.la $(SERLIBS) +endif +if WITH_USB +nutdrv_qx_CFLAGS += -DQX_USB +nutdrv_qx_SOURCES += $(LIBUSB_IMPL) usb-common.c +nutdrv_qx_LDADD += $(LIBUSB_LIBS) +endif +NUTDRV_QX_SUBDRIVERS = nutdrv_qx_bestups.c nutdrv_qx_blazer-common.c \ + nutdrv_qx_masterguard.c \ + nutdrv_qx_mecer.c nutdrv_qx_megatec.c nutdrv_qx_megatec-old.c \ + nutdrv_qx_mustek.c nutdrv_qx_q1.c nutdrv_qx_voltronic.c \ + nutdrv_qx_voltronic-qs.c nutdrv_qx_voltronic-qs-hex.c nutdrv_qx_zinto.c \ + nutdrv_qx_hunnox.c nutdrv_qx_ablerex.c +nutdrv_qx_SOURCES += $(NUTDRV_QX_SUBDRIVERS) + # ---------------------------------------------------------------------- # List of header files. The purpose of this list is not dependency # tracking (which is automatic), but to ensure these files are # distributed by "make dist". -dist_noinst_HEADERS = apc-mib.h apc-hid.h baytech-mib.h bcmxcp.h \ - bcmxcp_io.h belkin.h belkin-hid.h bestpower-mib.h blazer.h cps-hid.h dstate.h \ - dstate-hal.h dummy-ups.h eaton-mib.h explore-hid.h gamatronic.h genericups.h \ - hidparser.h hidtypes.h ietf-mib.h libhid.h libshut.h libusb.h liebert-hid.h \ - main.h main-hal.h mge-hid.h mge-mib.h mge-shut.h mge-utalk.h \ - mge-xml.h microdowell.h netvision-mib.h netxml-ups.h nut-ipmi.h oneac.h \ +dist_noinst_HEADERS = apc-mib.h apc-iem-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h bcmxcp_ser.h \ + bcmxcp_io.h belkin.h belkin-hid.h bestpower-mib.h blazer.h cps-hid.h dstate.h \ + dummy-ups.h explore-hid.h gamatronic.h genericups.h \ + hidparser.h hidtypes.h ietf-mib.h libhid.h libshut.h nut_libusb.h liebert-hid.h \ + main.h mge-hid.h mge-mib.h mge-utalk.h \ + mge-xml.h microdowell.h microsol-apc.h microsol-common.h netvision-mib.h netxml-ups.h nut-ipmi.h oneac.h \ powercom.h powerpanel.h powerp-bin.h powerp-txt.h powerware-mib.h raritan-pdu-mib.h \ safenet.h serial.h snmp-ups.h solis.h tripplite.h tripplite-hid.h \ upshandler.h usb-common.h usbhid-ups.h powercom-hid.h compaq-mib.h idowell-hid.h \ - apcsmart.h apcsmart_tabs.h apcsmart-old.h cyberpower-mib.h + apcsmart.h apcsmart_tabs.h apcsmart-old.h apcupsd-ups.h cyberpower-mib.h riello.h openups-hid.h \ + delta_ups-mib.h nutdrv_qx.h nutdrv_qx_bestups.h nutdrv_qx_blazer-common.h \ + nutdrv_qx_masterguard.h \ + nutdrv_qx_mecer.h nutdrv_qx_ablerex.h \ + nutdrv_qx_megatec.h nutdrv_qx_megatec-old.h nutdrv_qx_mustek.h nutdrv_qx_q1.h nutdrv_qx_hunnox.h \ + nutdrv_qx_voltronic.h nutdrv_qx_voltronic-qs.h nutdrv_qx_voltronic-qs-hex.h nutdrv_qx_zinto.h \ + xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ + apc-pdu-mib.h ever-hid.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h eaton-pdu-marlin-helpers.h \ + eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h legrand-hid.h \ + hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adelsystem_cbi.h # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, -EXTRA_LIBRARIES = libdummy.a -libdummy_a_SOURCES = main.c dstate.c serial.c - -# the nuthalmain library combines the code for main-hal.c and -# dstate-hal.c. It is necessary for Automake-technical reasons, +# as a final product. It was necessary for Automake-technical reasons, # because per-object CFLAGS can only be specified for libraries, not # for object files. This library is used during the build process, # and is not meant to be installed. +EXTRA_LTLIBRARIES = libdummy.la libdummy_serial.la +libdummy_la_SOURCES = main.c dstate.c +libdummy_la_LDFLAGS = -no-undefined -static +libdummy_serial_la_SOURCES = serial.c +libdummy_serial_la_LDFLAGS = -no-undefined -static -EXTRA_LIBRARIES += libnuthalmain.a -libnuthalmain_a_SOURCES = main-hal.c dstate-hal.c usb-common.c +dummy: -MOSTLYCLEANFILES = libnuthalmain.a +CLEANFILES = $(EXTRA_LTLIBRARIES) $(EXTRA_PROGRAMS) +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +# NOTE: Do not clean ".deps" in SUBDIRS of the main project, +# the root Makefile.am takes care of that! +#clean-local: +# rm -rf $(builddir)/.deps diff --git a/drivers/Makefile.in b/drivers/Makefile.in index 20f18c3..0f9df89 100644 --- a/drivers/Makefile.in +++ b/drivers/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -19,6 +18,61 @@ VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -38,120 +92,179 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ -@WITH_HAL_TRUE@am__append_1 = $(LIBHAL_CFLAGS) -@WITH_USB_TRUE@am__append_2 = $(LIBUSB_CFLAGS) -@WITH_SNMP_TRUE@am__append_3 = $(LIBNETSNMP_CFLAGS) -@WITH_NEON_TRUE@am__append_4 = $(LIBNEON_CFLAGS) -@WITH_LIBPOWERMAN_TRUE@am__append_5 = $(LIBPOWERMAN_CFLAGS) -@WITH_IPMI_TRUE@am__append_6 = $(LIBIPMI_CFLAGS) -EXTRA_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_2) $(am__EXEEXT_4) \ - $(am__EXEEXT_5) -@SOME_DRIVERS_FALSE@driverexec_PROGRAMS = $(am__EXEEXT_6) \ -@SOME_DRIVERS_FALSE@ $(am__EXEEXT_7) $(am__EXEEXT_8) \ -@SOME_DRIVERS_FALSE@ $(am__EXEEXT_9) $(am__EXEEXT_10) \ -@SOME_DRIVERS_FALSE@ $(am__EXEEXT_11) upsdrvctl$(EXEEXT) +@WITH_USB_TRUE@am__append_1 = $(LIBUSB_CFLAGS) +@WITH_NEON_TRUE@am__append_2 = $(LIBNEON_CFLAGS) +@WITH_LIBPOWERMAN_TRUE@am__append_3 = $(LIBPOWERMAN_CFLAGS) +@WITH_IPMI_TRUE@am__append_4 = $(LIBIPMI_CFLAGS) +@WITH_MODBUS_TRUE@am__append_5 = $(LIBMODBUS_CFLAGS) +EXTRA_PROGRAMS = $(am__EXEEXT_1) $(am__EXEEXT_3) $(am__EXEEXT_4) \ + $(am__EXEEXT_5) $(am__EXEEXT_6) $(am__EXEEXT_7) \ + $(am__EXEEXT_8) +@SOME_DRIVERS_FALSE@driverexec_PROGRAMS = $(am__EXEEXT_9) \ +@SOME_DRIVERS_FALSE@ $(am__EXEEXT_10) $(am__EXEEXT_11) \ +@SOME_DRIVERS_FALSE@ $(am__EXEEXT_12) $(am__EXEEXT_13) \ +@SOME_DRIVERS_FALSE@ $(am__EXEEXT_15) $(am__EXEEXT_17) \ +@SOME_DRIVERS_FALSE@ $(am__EXEEXT_18) $(am__EXEEXT_19) \ +@SOME_DRIVERS_FALSE@ $(am__EXEEXT_21) @SOME_DRIVERS_TRUE@driverexec_PROGRAMS = $(DRIVER_BUILD_LIST) \ -@SOME_DRIVERS_TRUE@ $(am__EXEEXT_6) $(am__EXEEXT_7) \ -@SOME_DRIVERS_TRUE@ $(am__EXEEXT_8) $(am__EXEEXT_9) \ -@SOME_DRIVERS_TRUE@ $(am__EXEEXT_10) $(am__EXEEXT_11) \ -@SOME_DRIVERS_TRUE@ skel$(EXEEXT) upsdrvctl$(EXEEXT) -@SOME_DRIVERS_FALSE@@WITH_SERIAL_TRUE@am__append_7 = $(SERIAL_DRIVERLIST) +@SOME_DRIVERS_TRUE@ $(am__EXEEXT_9) $(am__EXEEXT_10) \ +@SOME_DRIVERS_TRUE@ $(am__EXEEXT_11) $(am__EXEEXT_12) \ +@SOME_DRIVERS_TRUE@ $(am__EXEEXT_13) $(am__EXEEXT_15) \ +@SOME_DRIVERS_TRUE@ $(am__EXEEXT_17) $(am__EXEEXT_18) \ +@SOME_DRIVERS_TRUE@ $(am__EXEEXT_19) $(am__EXEEXT_21) \ +@SOME_DRIVERS_TRUE@ skel$(EXEEXT) +@SOME_DRIVERS_FALSE@@WITH_SERIAL_TRUE@am__append_6 = $(SERIAL_DRIVERLIST) $(SERIAL_USB_DRIVERLIST) +@SOME_DRIVERS_FALSE@@WITH_SERIAL_FALSE@@WITH_USB_TRUE@am__append_7 = $(SERIAL_USB_DRIVERLIST) @SOME_DRIVERS_FALSE@@WITH_SNMP_TRUE@am__append_8 = $(SNMP_DRIVERLIST) @SOME_DRIVERS_FALSE@@WITH_USB_TRUE@am__append_9 = $(USB_LIBUSB_DRIVERLIST) -@SOME_DRIVERS_FALSE@@WITH_HAL_TRUE@halexec_PROGRAMS = \ -@SOME_DRIVERS_FALSE@@WITH_HAL_TRUE@ $(am__EXEEXT_12) @SOME_DRIVERS_FALSE@@WITH_NEON_TRUE@am__append_10 = $(NEONXML_DRIVERLIST) -@SOME_DRIVERS_FALSE@@WITH_LIBPOWERMAN_TRUE@am__append_11 = powerman-pdu -@SOME_DRIVERS_FALSE@@WITH_IPMI_TRUE@am__append_12 = nut-ipmipsu -@WITH_SSL_TRUE@am__append_13 = $(LIBSSL_CFLAGS) -@WITH_SSL_TRUE@am__append_14 = $(LIBSSL_LIBS) -@WITH_FREEIPMI_TRUE@am__append_15 = nut-libfreeipmi.c +@SOME_DRIVERS_FALSE@@WITH_LIBPOWERMAN_TRUE@am__append_11 = $(POWERMAN_DRIVERLIST) +@SOME_DRIVERS_FALSE@@WITH_IPMI_TRUE@am__append_12 = $(IPMI_DRIVERLIST) +@SOME_DRIVERS_FALSE@@WITH_MACOSX_TRUE@am__append_13 = $(MACOSX_DRIVERLIST) +@SOME_DRIVERS_FALSE@@WITH_LINUX_I2C_TRUE@am__append_14 = $(LINUX_I2C_DRIVERLIST) +@SOME_DRIVERS_FALSE@@WITH_MODBUS_TRUE@am__append_15 = $(MODBUS_DRIVERLIST) +sbin_PROGRAMS = upsdrvctl$(EXEEXT) +@WITH_SSL_TRUE@am__append_16 = $(LIBSSL_CFLAGS) +@WITH_SSL_TRUE@am__append_17 = $(LIBSSL_LIBS) +@WITH_FREEIPMI_TRUE@am__append_18 = nut-libfreeipmi.c +@WITH_SERIAL_TRUE@am__append_19 = -DQX_SERIAL +@WITH_SERIAL_TRUE@am__append_20 = libdummy_serial.la $(SERLIBS) +@WITH_USB_TRUE@am__append_21 = -DQX_USB +@WITH_USB_TRUE@am__append_22 = $(LIBUSB_IMPL) usb-common.c +@WITH_USB_TRUE@am__append_23 = $(LIBUSB_LIBS) subdir = drivers -DIST_COMMON = $(dist_noinst_HEADERS) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_noinst_HEADERS) \ + $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = -ARFLAGS = cru -libdummy_a_AR = $(AR) $(ARFLAGS) -libdummy_a_LIBADD = -am_libdummy_a_OBJECTS = main.$(OBJEXT) dstate.$(OBJEXT) \ - serial.$(OBJEXT) -libdummy_a_OBJECTS = $(am_libdummy_a_OBJECTS) -libnuthalmain_a_AR = $(AR) $(ARFLAGS) -libnuthalmain_a_LIBADD = -am_libnuthalmain_a_OBJECTS = main-hal.$(OBJEXT) dstate-hal.$(OBJEXT) \ - usb-common.$(OBJEXT) -libnuthalmain_a_OBJECTS = $(am_libnuthalmain_a_OBJECTS) -am__EXEEXT_1 = bcmxcp$(EXEEXT) belkin$(EXEEXT) belkinunv$(EXEEXT) \ - bestfcom$(EXEEXT) bestfortress$(EXEEXT) bestuferrups$(EXEEXT) \ - bestups$(EXEEXT) dummy-ups$(EXEEXT) etapro$(EXEEXT) \ - everups$(EXEEXT) gamatronic$(EXEEXT) genericups$(EXEEXT) \ - isbmex$(EXEEXT) liebert$(EXEEXT) liebert-esp2$(EXEEXT) \ - masterguard$(EXEEXT) metasys$(EXEEXT) mge-shut$(EXEEXT) \ - mge-utalk$(EXEEXT) microdowell$(EXEEXT) newmge-shut$(EXEEXT) \ - oneac$(EXEEXT) optiups$(EXEEXT) powercom$(EXEEXT) \ - rhino$(EXEEXT) safenet$(EXEEXT) skel$(EXEEXT) solis$(EXEEXT) \ +am__EXEEXT_1 = al175$(EXEEXT) bcmxcp$(EXEEXT) belkin$(EXEEXT) \ + belkinunv$(EXEEXT) bestfcom$(EXEEXT) bestfortress$(EXEEXT) \ + bestuferrups$(EXEEXT) bestups$(EXEEXT) dummy-ups$(EXEEXT) \ + etapro$(EXEEXT) everups$(EXEEXT) gamatronic$(EXEEXT) \ + genericups$(EXEEXT) isbmex$(EXEEXT) liebert$(EXEEXT) \ + liebert-esp2$(EXEEXT) masterguard$(EXEEXT) metasys$(EXEEXT) \ + mge-utalk$(EXEEXT) microdowell$(EXEEXT) microsol-apc$(EXEEXT) \ + mge-shut$(EXEEXT) oneac$(EXEEXT) optiups$(EXEEXT) \ + powercom$(EXEEXT) rhino$(EXEEXT) safenet$(EXEEXT) \ + nutdrv_siemens-sitop$(EXEEXT) skel$(EXEEXT) solis$(EXEEXT) \ tripplite$(EXEEXT) tripplitesu$(EXEEXT) upscode2$(EXEEXT) \ victronups$(EXEEXT) powerpanel$(EXEEXT) blazer_ser$(EXEEXT) \ clone$(EXEEXT) clone-outlet$(EXEEXT) ivtscd$(EXEEXT) \ - apcsmart$(EXEEXT) apcsmart-old$(EXEEXT) -am__EXEEXT_2 = snmp-ups$(EXEEXT) -am__EXEEXT_3 = usbhid-ups$(EXEEXT) bcmxcp_usb$(EXEEXT) \ + apcsmart$(EXEEXT) apcsmart-old$(EXEEXT) apcupsd-ups$(EXEEXT) \ + riello_ser$(EXEEXT) +am__EXEEXT_2 = usbhid-ups$(EXEEXT) bcmxcp_usb$(EXEEXT) \ tripplite_usb$(EXEEXT) blazer_usb$(EXEEXT) \ - richcomm_usb$(EXEEXT) -am__EXEEXT_4 = $(am__EXEEXT_3) -am__EXEEXT_5 = netxml-ups$(EXEEXT) -@SOME_DRIVERS_FALSE@@WITH_SERIAL_TRUE@am__EXEEXT_6 = $(am__EXEEXT_1) -@SOME_DRIVERS_FALSE@@WITH_SNMP_TRUE@am__EXEEXT_7 = $(am__EXEEXT_2) -@SOME_DRIVERS_FALSE@@WITH_USB_TRUE@am__EXEEXT_8 = $(am__EXEEXT_3) -@SOME_DRIVERS_FALSE@@WITH_NEON_TRUE@am__EXEEXT_9 = $(am__EXEEXT_5) -@SOME_DRIVERS_FALSE@@WITH_LIBPOWERMAN_TRUE@am__EXEEXT_10 = powerman-pdu$(EXEEXT) -@SOME_DRIVERS_FALSE@@WITH_IPMI_TRUE@am__EXEEXT_11 = \ -@SOME_DRIVERS_FALSE@@WITH_IPMI_TRUE@ nut-ipmipsu$(EXEEXT) -am__installdirs = "$(DESTDIR)$(driverexecdir)" \ - "$(DESTDIR)$(halexecdir)" -am__EXEEXT_12 = hald-addon-usbhid-ups$(EXEEXT) \ - hald-addon-bcmxcp_usb$(EXEEXT) \ - hald-addon-tripplite_usb$(EXEEXT) \ - hald-addon-blazer_usb$(EXEEXT) -PROGRAMS = $(driverexec_PROGRAMS) $(halexec_PROGRAMS) + richcomm_usb$(EXEEXT) riello_usb$(EXEEXT) \ + nutdrv_atcl_usb$(EXEEXT) +am__EXEEXT_3 = $(am__EXEEXT_2) +am__EXEEXT_4 = nutdrv_qx$(EXEEXT) +am__EXEEXT_5 = snmp-ups$(EXEEXT) +am__EXEEXT_6 = netxml-ups$(EXEEXT) +am__EXEEXT_7 = macosx-ups$(EXEEXT) +am__EXEEXT_8 = asem$(EXEEXT) pijuice$(EXEEXT) +@SOME_DRIVERS_FALSE@@WITH_SERIAL_TRUE@am__EXEEXT_9 = $(am__EXEEXT_1) \ +@SOME_DRIVERS_FALSE@@WITH_SERIAL_TRUE@ $(am__EXEEXT_4) +@SOME_DRIVERS_FALSE@@WITH_SERIAL_FALSE@@WITH_USB_TRUE@am__EXEEXT_10 = $(am__EXEEXT_4) +@SOME_DRIVERS_FALSE@@WITH_SNMP_TRUE@am__EXEEXT_11 = $(am__EXEEXT_5) +@SOME_DRIVERS_FALSE@@WITH_USB_TRUE@am__EXEEXT_12 = $(am__EXEEXT_2) +@SOME_DRIVERS_FALSE@@WITH_NEON_TRUE@am__EXEEXT_13 = $(am__EXEEXT_6) +am__EXEEXT_14 = powerman-pdu$(EXEEXT) +@SOME_DRIVERS_FALSE@@WITH_LIBPOWERMAN_TRUE@am__EXEEXT_15 = \ +@SOME_DRIVERS_FALSE@@WITH_LIBPOWERMAN_TRUE@ $(am__EXEEXT_14) +am__EXEEXT_16 = nut-ipmipsu$(EXEEXT) +@SOME_DRIVERS_FALSE@@WITH_IPMI_TRUE@am__EXEEXT_17 = $(am__EXEEXT_16) +@SOME_DRIVERS_FALSE@@WITH_MACOSX_TRUE@am__EXEEXT_18 = $(am__EXEEXT_7) +@SOME_DRIVERS_FALSE@@WITH_LINUX_I2C_TRUE@am__EXEEXT_19 = \ +@SOME_DRIVERS_FALSE@@WITH_LINUX_I2C_TRUE@ $(am__EXEEXT_8) +am__EXEEXT_20 = phoenixcontact_modbus$(EXEEXT) generic_modbus$(EXEEXT) \ + huawei-ups2000$(EXEEXT) socomec_jbus$(EXEEXT) \ + adelsystem_cbi$(EXEEXT) +@SOME_DRIVERS_FALSE@@WITH_MODBUS_TRUE@am__EXEEXT_21 = \ +@SOME_DRIVERS_FALSE@@WITH_MODBUS_TRUE@ $(am__EXEEXT_20) +am__installdirs = "$(DESTDIR)$(driverexecdir)" "$(DESTDIR)$(sbindir)" +PROGRAMS = $(driverexec_PROGRAMS) $(sbin_PROGRAMS) +libdummy_la_LIBADD = +am_libdummy_la_OBJECTS = main.lo dstate.lo +libdummy_la_OBJECTS = $(am_libdummy_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libdummy_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(libdummy_la_LDFLAGS) $(LDFLAGS) -o $@ +libdummy_serial_la_LIBADD = +am_libdummy_serial_la_OBJECTS = serial.lo +libdummy_serial_la_OBJECTS = $(am_libdummy_serial_la_OBJECTS) +libdummy_serial_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \ + $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \ + $(AM_CFLAGS) $(CFLAGS) $(libdummy_serial_la_LDFLAGS) \ + $(LDFLAGS) -o $@ +am_adelsystem_cbi_OBJECTS = adelsystem_cbi.$(OBJEXT) +adelsystem_cbi_OBJECTS = $(am_adelsystem_cbi_OBJECTS) +am__DEPENDENCIES_1 = +adelsystem_cbi_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) +am_al175_OBJECTS = al175.$(OBJEXT) +al175_OBJECTS = $(am_al175_OBJECTS) +al175_LDADD = $(LDADD) +am__DEPENDENCIES_2 = libdummy_serial.la $(LDADD_DRIVERS) \ + $(am__DEPENDENCIES_1) +al175_DEPENDENCIES = $(am__DEPENDENCIES_2) am_apcsmart_OBJECTS = apcsmart.$(OBJEXT) apcsmart_tabs.$(OBJEXT) apcsmart_OBJECTS = $(am_apcsmart_OBJECTS) apcsmart_LDADD = $(LDADD) -am__DEPENDENCIES_1 = -am__DEPENDENCIES_2 = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) serial.o apcsmart_DEPENDENCIES = $(am__DEPENDENCIES_2) am_apcsmart_old_OBJECTS = apcsmart-old.$(OBJEXT) apcsmart_old_OBJECTS = $(am_apcsmart_old_OBJECTS) apcsmart_old_LDADD = $(LDADD) apcsmart_old_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_apcupsd_ups_OBJECTS = apcupsd_ups-apcupsd-ups.$(OBJEXT) +apcupsd_ups_OBJECTS = $(am_apcupsd_ups_OBJECTS) +apcupsd_ups_DEPENDENCIES = $(LDADD_DRIVERS) +apcupsd_ups_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(apcupsd_ups_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am_asem_OBJECTS = asem.$(OBJEXT) +asem_OBJECTS = $(am_asem_OBJECTS) +asem_DEPENDENCIES = $(LDADD_DRIVERS) am_bcmxcp_OBJECTS = bcmxcp.$(OBJEXT) bcmxcp_ser.$(OBJEXT) bcmxcp_OBJECTS = $(am_bcmxcp_OBJECTS) am__DEPENDENCIES_3 = $(am__DEPENDENCIES_2) @@ -172,8 +285,8 @@ am_bestfcom_OBJECTS = bestfcom.$(OBJEXT) bestfcom_OBJECTS = $(am_bestfcom_OBJECTS) bestfcom_LDADD = $(LDADD) bestfcom_DEPENDENCIES = $(am__DEPENDENCIES_2) -bestfortress_SOURCES = bestfortress.c -bestfortress_OBJECTS = bestfortress.$(OBJEXT) +am_bestfortress_OBJECTS = bestfortress.$(OBJEXT) +bestfortress_OBJECTS = $(am_bestfortress_OBJECTS) bestfortress_LDADD = $(LDADD) bestfortress_DEPENDENCIES = $(am__DEPENDENCIES_2) am_bestuferrups_OBJECTS = bestuferrups.$(OBJEXT) @@ -187,8 +300,12 @@ bestups_DEPENDENCIES = $(am__DEPENDENCIES_2) am_blazer_ser_OBJECTS = blazer.$(OBJEXT) blazer_ser.$(OBJEXT) blazer_ser_OBJECTS = $(am_blazer_ser_OBJECTS) blazer_ser_DEPENDENCIES = $(am__DEPENDENCIES_3) +am__blazer_usb_SOURCES_DIST = blazer.c blazer_usb.c libusb0.c \ + libusb1.c usb-common.c +@WITH_LIBUSB_0_1_FALSE@@WITH_LIBUSB_1_0_TRUE@am__objects_1 = libusb1.$(OBJEXT) +@WITH_LIBUSB_0_1_TRUE@am__objects_1 = libusb0.$(OBJEXT) am_blazer_usb_OBJECTS = blazer.$(OBJEXT) blazer_usb.$(OBJEXT) \ - libusb.$(OBJEXT) usb-common.$(OBJEXT) + $(am__objects_1) usb-common.$(OBJEXT) blazer_usb_OBJECTS = $(am_blazer_usb_OBJECTS) blazer_usb_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am_clone_OBJECTS = clone.$(OBJEXT) @@ -202,9 +319,9 @@ clone_outlet_DEPENDENCIES = $(am__DEPENDENCIES_2) am_dummy_ups_OBJECTS = dummy_ups-dummy-ups.$(OBJEXT) dummy_ups_OBJECTS = $(am_dummy_ups_OBJECTS) @WITH_SSL_TRUE@am__DEPENDENCIES_4 = $(am__DEPENDENCIES_1) -dummy_ups_DEPENDENCIES = $(LDADD_DRIVERS) ../clients/libupsclient.la \ - $(am__DEPENDENCIES_4) -dummy_ups_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ +dummy_ups_DEPENDENCIES = $(LDADD_DRIVERS) \ + $(top_builddir)/clients/libupsclient.la $(am__DEPENDENCIES_4) +dummy_ups_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(dummy_ups_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ am_etapro_OBJECTS = etapro.$(OBJEXT) @@ -219,36 +336,17 @@ am_gamatronic_OBJECTS = gamatronic.$(OBJEXT) gamatronic_OBJECTS = $(am_gamatronic_OBJECTS) gamatronic_LDADD = $(LDADD) gamatronic_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_generic_modbus_OBJECTS = generic_modbus.$(OBJEXT) +generic_modbus_OBJECTS = $(am_generic_modbus_OBJECTS) +generic_modbus_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am_genericups_OBJECTS = genericups.$(OBJEXT) genericups_OBJECTS = $(am_genericups_OBJECTS) genericups_LDADD = $(LDADD) genericups_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_hald_addon_bcmxcp_usb_OBJECTS = bcmxcp_usb.$(OBJEXT) \ - bcmxcp.$(OBJEXT) -hald_addon_bcmxcp_usb_OBJECTS = $(am_hald_addon_bcmxcp_usb_OBJECTS) -hald_addon_bcmxcp_usb_DEPENDENCIES = $(LDADD_HAL_DRIVERS) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -am_hald_addon_blazer_usb_OBJECTS = blazer.$(OBJEXT) \ - blazer_usb.$(OBJEXT) libusb.$(OBJEXT) -hald_addon_blazer_usb_OBJECTS = $(am_hald_addon_blazer_usb_OBJECTS) -hald_addon_blazer_usb_DEPENDENCIES = $(LDADD_HAL_DRIVERS) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -am_hald_addon_tripplite_usb_OBJECTS = tripplite_usb.$(OBJEXT) \ - libusb.$(OBJEXT) -hald_addon_tripplite_usb_OBJECTS = \ - $(am_hald_addon_tripplite_usb_OBJECTS) -hald_addon_tripplite_usb_DEPENDENCIES = $(LDADD_HAL_DRIVERS) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) -am__objects_1 = apc-hid.$(OBJEXT) belkin-hid.$(OBJEXT) \ - cps-hid.$(OBJEXT) explore-hid.$(OBJEXT) liebert-hid.$(OBJEXT) \ - mge-hid.$(OBJEXT) powercom-hid.$(OBJEXT) \ - tripplite-hid.$(OBJEXT) idowell-hid.$(OBJEXT) -am_hald_addon_usbhid_ups_OBJECTS = usbhid-ups.$(OBJEXT) \ - libhid.$(OBJEXT) libusb.$(OBJEXT) hidparser.$(OBJEXT) \ - $(am__objects_1) -hald_addon_usbhid_ups_OBJECTS = $(am_hald_addon_usbhid_ups_OBJECTS) -hald_addon_usbhid_ups_DEPENDENCIES = $(LDADD_HAL_DRIVERS) \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) +am_huawei_ups2000_OBJECTS = huawei-ups2000.$(OBJEXT) +huawei_ups2000_OBJECTS = $(am_huawei_ups2000_OBJECTS) +huawei_ups2000_DEPENDENCIES = $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_1) am_isbmex_OBJECTS = isbmex.$(OBJEXT) isbmex_OBJECTS = $(am_isbmex_OBJECTS) isbmex_DEPENDENCIES = $(am__DEPENDENCIES_3) @@ -264,18 +362,27 @@ am_liebert_esp2_OBJECTS = liebert-esp2.$(OBJEXT) liebert_esp2_OBJECTS = $(am_liebert_esp2_OBJECTS) liebert_esp2_LDADD = $(LDADD) liebert_esp2_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_macosx_ups_OBJECTS = macosx-ups.$(OBJEXT) +macosx_ups_OBJECTS = $(am_macosx_ups_OBJECTS) +macosx_ups_DEPENDENCIES = $(LDADD_DRIVERS) +macosx_ups_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(macosx_ups_LDFLAGS) $(LDFLAGS) -o $@ am_masterguard_OBJECTS = masterguard.$(OBJEXT) masterguard_OBJECTS = $(am_masterguard_OBJECTS) masterguard_LDADD = $(LDADD) masterguard_DEPENDENCIES = $(am__DEPENDENCIES_2) am_metasys_OBJECTS = metasys.$(OBJEXT) metasys_OBJECTS = $(am_metasys_OBJECTS) -metasys_LDADD = $(LDADD) -metasys_DEPENDENCIES = $(am__DEPENDENCIES_2) -am_mge_shut_OBJECTS = mge-shut.$(OBJEXT) hidparser.$(OBJEXT) +metasys_DEPENDENCIES = $(am__DEPENDENCIES_3) +am_mge_shut_OBJECTS = mge_shut-usbhid-ups.$(OBJEXT) \ + mge_shut-libshut.$(OBJEXT) mge_shut-libhid.$(OBJEXT) \ + mge_shut-hidparser.$(OBJEXT) mge_shut-mge-hid.$(OBJEXT) mge_shut_OBJECTS = $(am_mge_shut_OBJECTS) -mge_shut_LDADD = $(LDADD) -mge_shut_DEPENDENCIES = $(am__DEPENDENCIES_2) +mge_shut_DEPENDENCIES = $(am__DEPENDENCIES_3) +mge_shut_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(mge_shut_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ am_mge_utalk_OBJECTS = mge-utalk.$(OBJEXT) mge_utalk_OBJECTS = $(am_mge_utalk_OBJECTS) mge_utalk_LDADD = $(LDADD) @@ -284,22 +391,62 @@ am_microdowell_OBJECTS = microdowell.$(OBJEXT) microdowell_OBJECTS = $(am_microdowell_OBJECTS) microdowell_LDADD = $(LDADD) microdowell_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_microsol_apc_OBJECTS = microsol-apc.$(OBJEXT) \ + microsol-common.$(OBJEXT) +microsol_apc_OBJECTS = $(am_microsol_apc_OBJECTS) +microsol_apc_DEPENDENCIES = $(am__DEPENDENCIES_3) am_netxml_ups_OBJECTS = netxml-ups.$(OBJEXT) mge-xml.$(OBJEXT) netxml_ups_OBJECTS = $(am_netxml_ups_OBJECTS) netxml_ups_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) -am_newmge_shut_OBJECTS = newmge_shut-usbhid-ups.$(OBJEXT) \ - newmge_shut-libshut.$(OBJEXT) newmge_shut-libhid.$(OBJEXT) \ - newmge_shut-hidparser.$(OBJEXT) newmge_shut-mge-hid.$(OBJEXT) -newmge_shut_OBJECTS = $(am_newmge_shut_OBJECTS) -newmge_shut_DEPENDENCIES = $(am__DEPENDENCIES_3) -newmge_shut_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ - $(LIBTOOLFLAGS) --mode=link $(CCLD) $(newmge_shut_CFLAGS) \ - $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ am__nut_ipmipsu_SOURCES_DIST = nut-ipmipsu.c nut-libfreeipmi.c @WITH_FREEIPMI_TRUE@am__objects_2 = nut-libfreeipmi.$(OBJEXT) am_nut_ipmipsu_OBJECTS = nut-ipmipsu.$(OBJEXT) $(am__objects_2) nut_ipmipsu_OBJECTS = $(am_nut_ipmipsu_OBJECTS) nut_ipmipsu_DEPENDENCIES = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_1) +am_nutdrv_atcl_usb_OBJECTS = nutdrv_atcl_usb.$(OBJEXT) \ + usb-common.$(OBJEXT) +nutdrv_atcl_usb_OBJECTS = $(am_nutdrv_atcl_usb_OBJECTS) +nutdrv_atcl_usb_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) +am__nutdrv_qx_SOURCES_DIST = nutdrv_qx.c libusb0.c libusb1.c \ + usb-common.c nutdrv_qx_bestups.c nutdrv_qx_blazer-common.c \ + nutdrv_qx_masterguard.c nutdrv_qx_mecer.c nutdrv_qx_megatec.c \ + nutdrv_qx_megatec-old.c nutdrv_qx_mustek.c nutdrv_qx_q1.c \ + nutdrv_qx_voltronic.c nutdrv_qx_voltronic-qs.c \ + nutdrv_qx_voltronic-qs-hex.c nutdrv_qx_zinto.c \ + nutdrv_qx_hunnox.c nutdrv_qx_ablerex.c +@WITH_LIBUSB_0_1_FALSE@@WITH_LIBUSB_1_0_TRUE@am__objects_3 = nutdrv_qx-libusb1.$(OBJEXT) +@WITH_LIBUSB_0_1_TRUE@am__objects_3 = nutdrv_qx-libusb0.$(OBJEXT) +@WITH_USB_TRUE@am__objects_4 = $(am__objects_3) \ +@WITH_USB_TRUE@ nutdrv_qx-usb-common.$(OBJEXT) +am__objects_5 = nutdrv_qx-nutdrv_qx_bestups.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_blazer-common.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_masterguard.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_mecer.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_megatec.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_megatec-old.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_mustek.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_q1.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_voltronic.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_voltronic-qs.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_voltronic-qs-hex.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_zinto.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_hunnox.$(OBJEXT) \ + nutdrv_qx-nutdrv_qx_ablerex.$(OBJEXT) +am_nutdrv_qx_OBJECTS = nutdrv_qx-nutdrv_qx.$(OBJEXT) $(am__objects_4) \ + $(am__objects_5) +nutdrv_qx_OBJECTS = $(am_nutdrv_qx_OBJECTS) +@WITH_SERIAL_TRUE@am__DEPENDENCIES_5 = libdummy_serial.la \ +@WITH_SERIAL_TRUE@ $(am__DEPENDENCIES_1) +@WITH_USB_TRUE@am__DEPENDENCIES_6 = $(am__DEPENDENCIES_1) +nutdrv_qx_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_5) \ + $(am__DEPENDENCIES_6) +nutdrv_qx_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(nutdrv_qx_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am_nutdrv_siemens_sitop_OBJECTS = nutdrv_siemens_sitop.$(OBJEXT) +nutdrv_siemens_sitop_OBJECTS = $(am_nutdrv_siemens_sitop_OBJECTS) +nutdrv_siemens_sitop_LDADD = $(LDADD) +nutdrv_siemens_sitop_DEPENDENCIES = $(am__DEPENDENCIES_2) am_oneac_OBJECTS = oneac.$(OBJEXT) oneac_OBJECTS = $(am_oneac_OBJECTS) oneac_LDADD = $(LDADD) @@ -308,6 +455,13 @@ am_optiups_OBJECTS = optiups.$(OBJEXT) optiups_OBJECTS = $(am_optiups_OBJECTS) optiups_LDADD = $(LDADD) optiups_DEPENDENCIES = $(am__DEPENDENCIES_2) +am_phoenixcontact_modbus_OBJECTS = phoenixcontact_modbus.$(OBJEXT) +phoenixcontact_modbus_OBJECTS = $(am_phoenixcontact_modbus_OBJECTS) +phoenixcontact_modbus_DEPENDENCIES = $(LDADD_DRIVERS) \ + $(am__DEPENDENCIES_1) +am_pijuice_OBJECTS = pijuice.$(OBJEXT) +pijuice_OBJECTS = $(am_pijuice_OBJECTS) +pijuice_DEPENDENCIES = $(LDADD_DRIVERS) am_powercom_OBJECTS = powercom.$(OBJEXT) powercom_OBJECTS = $(am_powercom_OBJECTS) powercom_DEPENDENCIES = $(am__DEPENDENCIES_3) @@ -325,6 +479,15 @@ rhino_DEPENDENCIES = $(am__DEPENDENCIES_3) am_richcomm_usb_OBJECTS = richcomm_usb.$(OBJEXT) usb-common.$(OBJEXT) richcomm_usb_OBJECTS = $(am_richcomm_usb_OBJECTS) richcomm_usb_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) +am_riello_ser_OBJECTS = riello.$(OBJEXT) riello_ser.$(OBJEXT) +riello_ser_OBJECTS = $(am_riello_ser_OBJECTS) +riello_ser_DEPENDENCIES = $(am__DEPENDENCIES_3) +am__riello_usb_SOURCES_DIST = riello.c riello_usb.c libusb0.c \ + libusb1.c usb-common.c +am_riello_usb_OBJECTS = riello.$(OBJEXT) riello_usb.$(OBJEXT) \ + $(am__objects_1) usb-common.$(OBJEXT) +riello_usb_OBJECTS = $(am_riello_usb_OBJECTS) +riello_usb_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am_safenet_OBJECTS = safenet.$(OBJEXT) safenet_OBJECTS = $(am_safenet_OBJECTS) safenet_LDADD = $(LDADD) @@ -332,21 +495,46 @@ safenet_DEPENDENCIES = $(am__DEPENDENCIES_2) am_skel_OBJECTS = skel.$(OBJEXT) skel_OBJECTS = $(am_skel_OBJECTS) skel_DEPENDENCIES = $(LDADD_DRIVERS) -am_snmp_ups_OBJECTS = snmp-ups.$(OBJEXT) apc-mib.$(OBJEXT) \ - baytech-mib.$(OBJEXT) compaq-mib.$(OBJEXT) eaton-mib.$(OBJEXT) \ - ietf-mib.$(OBJEXT) mge-mib.$(OBJEXT) netvision-mib.$(OBJEXT) \ - powerware-mib.$(OBJEXT) raritan-pdu-mib.$(OBJEXT) \ - bestpower-mib.$(OBJEXT) cyberpower-mib.$(OBJEXT) +am_snmp_ups_OBJECTS = snmp_ups-snmp-ups.$(OBJEXT) \ + snmp_ups-snmp-ups-helpers.$(OBJEXT) snmp_ups-apc-mib.$(OBJEXT) \ + snmp_ups-apc-pdu-mib.$(OBJEXT) snmp_ups-baytech-mib.$(OBJEXT) \ + snmp_ups-bestpower-mib.$(OBJEXT) snmp_ups-compaq-mib.$(OBJEXT) \ + snmp_ups-cyberpower-mib.$(OBJEXT) \ + snmp_ups-delta_ups-mib.$(OBJEXT) \ + snmp_ups-eaton-pdu-genesis2-mib.$(OBJEXT) \ + snmp_ups-eaton-pdu-marlin-mib.$(OBJEXT) \ + snmp_ups-eaton-pdu-marlin-helpers.$(OBJEXT) \ + snmp_ups-eaton-pdu-pulizzi-mib.$(OBJEXT) \ + snmp_ups-eaton-pdu-revelation-mib.$(OBJEXT) \ + snmp_ups-eaton-ats16-nmc-mib.$(OBJEXT) \ + snmp_ups-eaton-ats16-nm2-mib.$(OBJEXT) \ + snmp_ups-apc-ats-mib.$(OBJEXT) \ + snmp_ups-eaton-ats30-mib.$(OBJEXT) \ + snmp_ups-emerson-avocent-pdu-mib.$(OBJEXT) \ + snmp_ups-hpe-pdu-mib.$(OBJEXT) snmp_ups-huawei-mib.$(OBJEXT) \ + snmp_ups-ietf-mib.$(OBJEXT) snmp_ups-mge-mib.$(OBJEXT) \ + snmp_ups-netvision-mib.$(OBJEXT) \ + snmp_ups-powerware-mib.$(OBJEXT) \ + snmp_ups-raritan-pdu-mib.$(OBJEXT) \ + snmp_ups-raritan-px2-mib.$(OBJEXT) snmp_ups-xppc-mib.$(OBJEXT) snmp_ups_OBJECTS = $(am_snmp_ups_OBJECTS) snmp_ups_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) +snmp_ups_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(snmp_ups_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am_socomec_jbus_OBJECTS = socomec_jbus.$(OBJEXT) +socomec_jbus_OBJECTS = $(am_socomec_jbus_OBJECTS) +socomec_jbus_DEPENDENCIES = $(am__DEPENDENCIES_2) \ + $(am__DEPENDENCIES_1) am_solis_OBJECTS = solis.$(OBJEXT) solis_OBJECTS = $(am_solis_OBJECTS) -solis_LDADD = $(LDADD) -solis_DEPENDENCIES = $(am__DEPENDENCIES_2) +solis_DEPENDENCIES = $(am__DEPENDENCIES_3) am_tripplite_OBJECTS = tripplite.$(OBJEXT) tripplite_OBJECTS = $(am_tripplite_OBJECTS) tripplite_DEPENDENCIES = $(am__DEPENDENCIES_3) -am_tripplite_usb_OBJECTS = tripplite_usb.$(OBJEXT) libusb.$(OBJEXT) \ +am__tripplite_usb_SOURCES_DIST = tripplite_usb.c libusb0.c libusb1.c \ + usb-common.c +am_tripplite_usb_OBJECTS = tripplite_usb.$(OBJEXT) $(am__objects_1) \ usb-common.$(OBJEXT) tripplite_usb_OBJECTS = $(am_tripplite_usb_OBJECTS) tripplite_usb_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) @@ -360,83 +548,249 @@ upscode2_DEPENDENCIES = $(am__DEPENDENCIES_3) am_upsdrvctl_OBJECTS = upsdrvctl.$(OBJEXT) upsdrvctl_OBJECTS = $(am_upsdrvctl_OBJECTS) upsdrvctl_DEPENDENCIES = $(LDADD_COMMON) +am__usbhid_ups_SOURCES_DIST = usbhid-ups.c libhid.c libusb0.c \ + libusb1.c hidparser.c usb-common.c apc-hid.c arduino-hid.c \ + belkin-hid.c cps-hid.c explore-hid.c liebert-hid.c mge-hid.c \ + powercom-hid.c tripplite-hid.c idowell-hid.c openups-hid.c \ + powervar-hid.c delta_ups-hid.c ever-hid.c legrand-hid.c \ + salicru-hid.c +am__objects_6 = apc-hid.$(OBJEXT) arduino-hid.$(OBJEXT) \ + belkin-hid.$(OBJEXT) cps-hid.$(OBJEXT) explore-hid.$(OBJEXT) \ + liebert-hid.$(OBJEXT) mge-hid.$(OBJEXT) powercom-hid.$(OBJEXT) \ + tripplite-hid.$(OBJEXT) idowell-hid.$(OBJEXT) \ + openups-hid.$(OBJEXT) powervar-hid.$(OBJEXT) \ + delta_ups-hid.$(OBJEXT) ever-hid.$(OBJEXT) \ + legrand-hid.$(OBJEXT) salicru-hid.$(OBJEXT) am_usbhid_ups_OBJECTS = usbhid-ups.$(OBJEXT) libhid.$(OBJEXT) \ - libusb.$(OBJEXT) hidparser.$(OBJEXT) usb-common.$(OBJEXT) \ - $(am__objects_1) + $(am__objects_1) hidparser.$(OBJEXT) usb-common.$(OBJEXT) \ + $(am__objects_6) usbhid_ups_OBJECTS = $(am_usbhid_ups_OBJECTS) usbhid_ups_DEPENDENCIES = $(LDADD_DRIVERS) $(am__DEPENDENCIES_1) am_victronups_OBJECTS = victronups.$(OBJEXT) victronups_OBJECTS = $(am_victronups_OBJECTS) victronups_LDADD = $(LDADD) victronups_DEPENDENCIES = $(am__DEPENDENCIES_2) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/adelsystem_cbi.Po \ + ./$(DEPDIR)/al175.Po ./$(DEPDIR)/apc-hid.Po \ + ./$(DEPDIR)/apcsmart-old.Po ./$(DEPDIR)/apcsmart.Po \ + ./$(DEPDIR)/apcsmart_tabs.Po \ + ./$(DEPDIR)/apcupsd_ups-apcupsd-ups.Po \ + ./$(DEPDIR)/arduino-hid.Po ./$(DEPDIR)/asem.Po \ + ./$(DEPDIR)/bcmxcp.Po ./$(DEPDIR)/bcmxcp_ser.Po \ + ./$(DEPDIR)/bcmxcp_usb.Po ./$(DEPDIR)/belkin-hid.Po \ + ./$(DEPDIR)/belkin.Po ./$(DEPDIR)/belkinunv.Po \ + ./$(DEPDIR)/bestfcom.Po ./$(DEPDIR)/bestfortress.Po \ + ./$(DEPDIR)/bestuferrups.Po ./$(DEPDIR)/bestups.Po \ + ./$(DEPDIR)/blazer.Po ./$(DEPDIR)/blazer_ser.Po \ + ./$(DEPDIR)/blazer_usb.Po ./$(DEPDIR)/clone-outlet.Po \ + ./$(DEPDIR)/clone.Po ./$(DEPDIR)/cps-hid.Po \ + ./$(DEPDIR)/delta_ups-hid.Po ./$(DEPDIR)/dstate.Plo \ + ./$(DEPDIR)/dummy_ups-dummy-ups.Po ./$(DEPDIR)/etapro.Po \ + ./$(DEPDIR)/ever-hid.Po ./$(DEPDIR)/everups.Po \ + ./$(DEPDIR)/explore-hid.Po ./$(DEPDIR)/gamatronic.Po \ + ./$(DEPDIR)/generic_modbus.Po ./$(DEPDIR)/genericups.Po \ + ./$(DEPDIR)/hidparser.Po ./$(DEPDIR)/huawei-ups2000.Po \ + ./$(DEPDIR)/idowell-hid.Po ./$(DEPDIR)/isbmex.Po \ + ./$(DEPDIR)/ivtscd.Po ./$(DEPDIR)/legrand-hid.Po \ + ./$(DEPDIR)/libhid.Po ./$(DEPDIR)/libusb0.Po \ + ./$(DEPDIR)/libusb1.Po ./$(DEPDIR)/liebert-esp2.Po \ + ./$(DEPDIR)/liebert-hid.Po ./$(DEPDIR)/liebert.Po \ + ./$(DEPDIR)/macosx-ups.Po ./$(DEPDIR)/main.Plo \ + ./$(DEPDIR)/masterguard.Po ./$(DEPDIR)/metasys.Po \ + ./$(DEPDIR)/mge-hid.Po ./$(DEPDIR)/mge-utalk.Po \ + ./$(DEPDIR)/mge-xml.Po ./$(DEPDIR)/mge_shut-hidparser.Po \ + ./$(DEPDIR)/mge_shut-libhid.Po ./$(DEPDIR)/mge_shut-libshut.Po \ + ./$(DEPDIR)/mge_shut-mge-hid.Po \ + ./$(DEPDIR)/mge_shut-usbhid-ups.Po ./$(DEPDIR)/microdowell.Po \ + ./$(DEPDIR)/microsol-apc.Po ./$(DEPDIR)/microsol-common.Po \ + ./$(DEPDIR)/netxml-ups.Po ./$(DEPDIR)/nut-ipmipsu.Po \ + ./$(DEPDIR)/nut-libfreeipmi.Po ./$(DEPDIR)/nutdrv_atcl_usb.Po \ + ./$(DEPDIR)/nutdrv_qx-libusb0.Po \ + ./$(DEPDIR)/nutdrv_qx-libusb1.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_ablerex.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_hunnox.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_masterguard.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Po \ + ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Po \ + ./$(DEPDIR)/nutdrv_qx-usb-common.Po \ + ./$(DEPDIR)/nutdrv_siemens_sitop.Po ./$(DEPDIR)/oneac.Po \ + ./$(DEPDIR)/openups-hid.Po ./$(DEPDIR)/optiups.Po \ + ./$(DEPDIR)/phoenixcontact_modbus.Po ./$(DEPDIR)/pijuice.Po \ + ./$(DEPDIR)/powercom-hid.Po ./$(DEPDIR)/powercom.Po \ + ./$(DEPDIR)/powerman-pdu.Po ./$(DEPDIR)/powerp-bin.Po \ + ./$(DEPDIR)/powerp-txt.Po ./$(DEPDIR)/powerpanel.Po \ + ./$(DEPDIR)/powervar-hid.Po ./$(DEPDIR)/rhino.Po \ + ./$(DEPDIR)/richcomm_usb.Po ./$(DEPDIR)/riello.Po \ + ./$(DEPDIR)/riello_ser.Po ./$(DEPDIR)/riello_usb.Po \ + ./$(DEPDIR)/safenet.Po ./$(DEPDIR)/salicru-hid.Po \ + ./$(DEPDIR)/serial.Plo ./$(DEPDIR)/skel.Po \ + ./$(DEPDIR)/snmp_ups-apc-ats-mib.Po \ + ./$(DEPDIR)/snmp_ups-apc-mib.Po \ + ./$(DEPDIR)/snmp_ups-apc-pdu-mib.Po \ + ./$(DEPDIR)/snmp_ups-baytech-mib.Po \ + ./$(DEPDIR)/snmp_ups-bestpower-mib.Po \ + ./$(DEPDIR)/snmp_ups-compaq-mib.Po \ + ./$(DEPDIR)/snmp_ups-cyberpower-mib.Po \ + ./$(DEPDIR)/snmp_ups-delta_ups-mib.Po \ + ./$(DEPDIR)/snmp_ups-eaton-ats16-nm2-mib.Po \ + ./$(DEPDIR)/snmp_ups-eaton-ats16-nmc-mib.Po \ + ./$(DEPDIR)/snmp_ups-eaton-ats30-mib.Po \ + ./$(DEPDIR)/snmp_ups-eaton-pdu-genesis2-mib.Po \ + ./$(DEPDIR)/snmp_ups-eaton-pdu-marlin-helpers.Po \ + ./$(DEPDIR)/snmp_ups-eaton-pdu-marlin-mib.Po \ + ./$(DEPDIR)/snmp_ups-eaton-pdu-pulizzi-mib.Po \ + ./$(DEPDIR)/snmp_ups-eaton-pdu-revelation-mib.Po \ + ./$(DEPDIR)/snmp_ups-emerson-avocent-pdu-mib.Po \ + ./$(DEPDIR)/snmp_ups-hpe-pdu-mib.Po \ + ./$(DEPDIR)/snmp_ups-huawei-mib.Po \ + ./$(DEPDIR)/snmp_ups-ietf-mib.Po \ + ./$(DEPDIR)/snmp_ups-mge-mib.Po \ + ./$(DEPDIR)/snmp_ups-netvision-mib.Po \ + ./$(DEPDIR)/snmp_ups-powerware-mib.Po \ + ./$(DEPDIR)/snmp_ups-raritan-pdu-mib.Po \ + ./$(DEPDIR)/snmp_ups-raritan-px2-mib.Po \ + ./$(DEPDIR)/snmp_ups-snmp-ups-helpers.Po \ + ./$(DEPDIR)/snmp_ups-snmp-ups.Po \ + ./$(DEPDIR)/snmp_ups-xppc-mib.Po ./$(DEPDIR)/socomec_jbus.Po \ + ./$(DEPDIR)/solis.Po ./$(DEPDIR)/tripplite-hid.Po \ + ./$(DEPDIR)/tripplite.Po ./$(DEPDIR)/tripplite_usb.Po \ + ./$(DEPDIR)/tripplitesu.Po ./$(DEPDIR)/upscode2.Po \ + ./$(DEPDIR)/upsdrvctl.Po ./$(DEPDIR)/usb-common.Po \ + ./$(DEPDIR)/usbhid-ups.Po ./$(DEPDIR)/victronups.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = CCLD = $(CC) -LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ - $(LDFLAGS) -o $@ -SOURCES = $(libdummy_a_SOURCES) $(libnuthalmain_a_SOURCES) \ - $(apcsmart_SOURCES) $(apcsmart_old_SOURCES) $(bcmxcp_SOURCES) \ - $(bcmxcp_usb_SOURCES) $(belkin_SOURCES) $(belkinunv_SOURCES) \ - $(bestfcom_SOURCES) bestfortress.c $(bestuferrups_SOURCES) \ +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libdummy_la_SOURCES) $(libdummy_serial_la_SOURCES) \ + $(adelsystem_cbi_SOURCES) $(al175_SOURCES) $(apcsmart_SOURCES) \ + $(apcsmart_old_SOURCES) $(apcupsd_ups_SOURCES) $(asem_SOURCES) \ + $(bcmxcp_SOURCES) $(bcmxcp_usb_SOURCES) $(belkin_SOURCES) \ + $(belkinunv_SOURCES) $(bestfcom_SOURCES) \ + $(bestfortress_SOURCES) $(bestuferrups_SOURCES) \ $(bestups_SOURCES) $(blazer_ser_SOURCES) $(blazer_usb_SOURCES) \ $(clone_SOURCES) $(clone_outlet_SOURCES) $(dummy_ups_SOURCES) \ $(etapro_SOURCES) $(everups_SOURCES) $(gamatronic_SOURCES) \ - $(genericups_SOURCES) $(hald_addon_bcmxcp_usb_SOURCES) \ - $(hald_addon_blazer_usb_SOURCES) \ - $(hald_addon_tripplite_usb_SOURCES) \ - $(hald_addon_usbhid_ups_SOURCES) $(isbmex_SOURCES) \ - $(ivtscd_SOURCES) $(liebert_SOURCES) $(liebert_esp2_SOURCES) \ - $(masterguard_SOURCES) $(metasys_SOURCES) $(mge_shut_SOURCES) \ - $(mge_utalk_SOURCES) $(microdowell_SOURCES) \ - $(netxml_ups_SOURCES) $(newmge_shut_SOURCES) \ - $(nut_ipmipsu_SOURCES) $(oneac_SOURCES) $(optiups_SOURCES) \ - $(powercom_SOURCES) $(powerman_pdu_SOURCES) \ + $(generic_modbus_SOURCES) $(genericups_SOURCES) \ + $(huawei_ups2000_SOURCES) $(isbmex_SOURCES) $(ivtscd_SOURCES) \ + $(liebert_SOURCES) $(liebert_esp2_SOURCES) \ + $(macosx_ups_SOURCES) $(masterguard_SOURCES) \ + $(metasys_SOURCES) $(mge_shut_SOURCES) $(mge_utalk_SOURCES) \ + $(microdowell_SOURCES) $(microsol_apc_SOURCES) \ + $(netxml_ups_SOURCES) $(nut_ipmipsu_SOURCES) \ + $(nutdrv_atcl_usb_SOURCES) $(nutdrv_qx_SOURCES) \ + $(nutdrv_siemens_sitop_SOURCES) $(oneac_SOURCES) \ + $(optiups_SOURCES) $(phoenixcontact_modbus_SOURCES) \ + $(pijuice_SOURCES) $(powercom_SOURCES) $(powerman_pdu_SOURCES) \ $(powerpanel_SOURCES) $(rhino_SOURCES) $(richcomm_usb_SOURCES) \ - $(safenet_SOURCES) $(skel_SOURCES) $(snmp_ups_SOURCES) \ + $(riello_ser_SOURCES) $(riello_usb_SOURCES) $(safenet_SOURCES) \ + $(skel_SOURCES) $(snmp_ups_SOURCES) $(socomec_jbus_SOURCES) \ $(solis_SOURCES) $(tripplite_SOURCES) $(tripplite_usb_SOURCES) \ $(tripplitesu_SOURCES) $(upscode2_SOURCES) \ $(upsdrvctl_SOURCES) $(usbhid_ups_SOURCES) \ $(victronups_SOURCES) -DIST_SOURCES = $(libdummy_a_SOURCES) $(libnuthalmain_a_SOURCES) \ - $(apcsmart_SOURCES) $(apcsmart_old_SOURCES) $(bcmxcp_SOURCES) \ - $(bcmxcp_usb_SOURCES) $(belkin_SOURCES) $(belkinunv_SOURCES) \ - $(bestfcom_SOURCES) bestfortress.c $(bestuferrups_SOURCES) \ - $(bestups_SOURCES) $(blazer_ser_SOURCES) $(blazer_usb_SOURCES) \ - $(clone_SOURCES) $(clone_outlet_SOURCES) $(dummy_ups_SOURCES) \ - $(etapro_SOURCES) $(everups_SOURCES) $(gamatronic_SOURCES) \ - $(genericups_SOURCES) $(hald_addon_bcmxcp_usb_SOURCES) \ - $(hald_addon_blazer_usb_SOURCES) \ - $(hald_addon_tripplite_usb_SOURCES) \ - $(hald_addon_usbhid_ups_SOURCES) $(isbmex_SOURCES) \ - $(ivtscd_SOURCES) $(liebert_SOURCES) $(liebert_esp2_SOURCES) \ - $(masterguard_SOURCES) $(metasys_SOURCES) $(mge_shut_SOURCES) \ - $(mge_utalk_SOURCES) $(microdowell_SOURCES) \ - $(netxml_ups_SOURCES) $(newmge_shut_SOURCES) \ - $(am__nut_ipmipsu_SOURCES_DIST) $(oneac_SOURCES) \ - $(optiups_SOURCES) $(powercom_SOURCES) $(powerman_pdu_SOURCES) \ +DIST_SOURCES = $(libdummy_la_SOURCES) $(libdummy_serial_la_SOURCES) \ + $(adelsystem_cbi_SOURCES) $(al175_SOURCES) $(apcsmart_SOURCES) \ + $(apcsmart_old_SOURCES) $(apcupsd_ups_SOURCES) $(asem_SOURCES) \ + $(bcmxcp_SOURCES) $(bcmxcp_usb_SOURCES) $(belkin_SOURCES) \ + $(belkinunv_SOURCES) $(bestfcom_SOURCES) \ + $(bestfortress_SOURCES) $(bestuferrups_SOURCES) \ + $(bestups_SOURCES) $(blazer_ser_SOURCES) \ + $(am__blazer_usb_SOURCES_DIST) $(clone_SOURCES) \ + $(clone_outlet_SOURCES) $(dummy_ups_SOURCES) $(etapro_SOURCES) \ + $(everups_SOURCES) $(gamatronic_SOURCES) \ + $(generic_modbus_SOURCES) $(genericups_SOURCES) \ + $(huawei_ups2000_SOURCES) $(isbmex_SOURCES) $(ivtscd_SOURCES) \ + $(liebert_SOURCES) $(liebert_esp2_SOURCES) \ + $(macosx_ups_SOURCES) $(masterguard_SOURCES) \ + $(metasys_SOURCES) $(mge_shut_SOURCES) $(mge_utalk_SOURCES) \ + $(microdowell_SOURCES) $(microsol_apc_SOURCES) \ + $(netxml_ups_SOURCES) $(am__nut_ipmipsu_SOURCES_DIST) \ + $(nutdrv_atcl_usb_SOURCES) $(am__nutdrv_qx_SOURCES_DIST) \ + $(nutdrv_siemens_sitop_SOURCES) $(oneac_SOURCES) \ + $(optiups_SOURCES) $(phoenixcontact_modbus_SOURCES) \ + $(pijuice_SOURCES) $(powercom_SOURCES) $(powerman_pdu_SOURCES) \ $(powerpanel_SOURCES) $(rhino_SOURCES) $(richcomm_usb_SOURCES) \ + $(riello_ser_SOURCES) $(am__riello_usb_SOURCES_DIST) \ $(safenet_SOURCES) $(skel_SOURCES) $(snmp_ups_SOURCES) \ - $(solis_SOURCES) $(tripplite_SOURCES) $(tripplite_usb_SOURCES) \ - $(tripplitesu_SOURCES) $(upscode2_SOURCES) \ - $(upsdrvctl_SOURCES) $(usbhid_ups_SOURCES) \ - $(victronups_SOURCES) + $(socomec_jbus_SOURCES) $(solis_SOURCES) $(tripplite_SOURCES) \ + $(am__tripplite_usb_SOURCES_DIST) $(tripplitesu_SOURCES) \ + $(upscode2_SOURCES) $(upsdrvctl_SOURCES) \ + $(am__usbhid_ups_SOURCES_DIST) $(victronups_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac HEADERS = $(dist_noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -447,16 +801,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -465,11 +828,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -479,14 +839,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -497,21 +858,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -525,35 +895,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -564,8 +945,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -584,18 +969,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -609,10 +997,9 @@ udevdir = @udevdir@ # by default, link programs in this directory with libcommon.la # (libtool version of the static lib, in order to access LTLIBOBJS) #FIXME: SERLIBS is only useful for LDADD_DRIVERS_SERIAL not for LDADD_COMMON -LDADD_COMMON = ../common/libcommon.la ../common/libparseconf.la -LDADD_DRIVERS = $(LDADD_COMMON) main.o dstate.o -LDADD_HAL_DRIVERS = $(LDADD_COMMON) libnuthalmain.a -LDADD_DRIVERS_SERIAL = $(LDADD_DRIVERS) $(SERLIBS) serial.o +LDADD_COMMON = $(top_builddir)/common/libcommon.la $(top_builddir)/common/libparseconf.la +LDADD_DRIVERS = libdummy.la $(LDADD_COMMON) +LDADD_DRIVERS_SERIAL = libdummy_serial.la $(LDADD_DRIVERS) $(SERLIBS) # most targets are drivers, so make this the default LDADD = $(LDADD_DRIVERS_SERIAL) @@ -620,25 +1007,29 @@ LDADD = $(LDADD_DRIVERS_SERIAL) # files. In any case, CFLAGS are only -I options, so there is no harm, # but only add them if we really use the target. AM_CFLAGS = -I$(top_srcdir)/include $(am__append_1) $(am__append_2) \ - $(am__append_3) $(am__append_4) $(am__append_5) \ - $(am__append_6) -SERIAL_DRIVERLIST = bcmxcp belkin belkinunv bestfcom \ + $(am__append_3) $(am__append_4) $(am__append_5) +SERIAL_DRIVERLIST = al175 bcmxcp belkin belkinunv bestfcom \ bestfortress bestuferrups bestups dummy-ups etapro everups \ gamatronic genericups isbmex liebert liebert-esp2 masterguard metasys \ - mge-shut mge-utalk microdowell newmge-shut oneac optiups powercom rhino \ - safenet skel solis tripplite tripplitesu upscode2 victronups powerpanel \ - blazer_ser clone clone-outlet ivtscd apcsmart apcsmart-old + mge-utalk microdowell microsol-apc mge-shut oneac optiups powercom rhino \ + safenet nutdrv_siemens-sitop skel solis tripplite tripplitesu upscode2 victronups powerpanel \ + blazer_ser clone clone-outlet ivtscd apcsmart apcsmart-old apcupsd-ups riello_ser SNMP_DRIVERLIST = snmp-ups USB_LIBUSB_DRIVERLIST = usbhid-ups bcmxcp_usb tripplite_usb \ - blazer_usb richcomm_usb + blazer_usb richcomm_usb riello_usb \ + nutdrv_atcl_usb USB_DRIVERLIST = $(USB_LIBUSB_DRIVERLIST) -HAL_DRIVERLIST = hald-addon-usbhid-ups hald-addon-bcmxcp_usb \ - hald-addon-tripplite_usb hald-addon-blazer_usb +SERIAL_USB_DRIVERLIST = \ + nutdrv_qx NEONXML_DRIVERLIST = netxml-ups -@SOME_DRIVERS_FALSE@@WITH_HAL_TRUE@halexecdir = $(HAL_CALLOUTS_PATH) +MACOSX_DRIVERLIST = macosx-ups +MODBUS_DRIVERLIST = phoenixcontact_modbus generic_modbus huawei-ups2000 socomec_jbus adelsystem_cbi +LINUX_I2C_DRIVERLIST = asem pijuice +POWERMAN_DRIVERLIST = powerman-pdu +IPMI_DRIVERLIST = nut-ipmipsu # ========================================================================== # Driver build details @@ -648,6 +1039,7 @@ upsdrvctl_SOURCES = upsdrvctl.c upsdrvctl_LDADD = $(LDADD_COMMON) # serial drivers: all of them use standard LDADD and CFLAGS +al175_SOURCES = al175.c apcsmart_SOURCES = apcsmart.c apcsmart_tabs.c apcsmart_old_SOURCES = apcsmart-old.c bcmxcp_SOURCES = bcmxcp.c bcmxcp_ser.c @@ -655,6 +1047,7 @@ bcmxcp_LDADD = $(LDADD) -lm belkin_SOURCES = belkin.c belkinunv_SOURCES = belkinunv.c bestfcom_SOURCES = bestfcom.c +bestfortress_SOURCES = bestfortress.c bestuferrups_SOURCES = bestuferrups.c bestups_SOURCES = bestups.c blazer_ser_SOURCES = blazer.c blazer_ser.c @@ -670,9 +1063,11 @@ liebert_SOURCES = liebert.c liebert_esp2_SOURCES = liebert-esp2.c masterguard_SOURCES = masterguard.c metasys_SOURCES = metasys.c -mge_shut_SOURCES = mge-shut.c hidparser.c +metasys_LDADD = $(LDADD) -lm mge_utalk_SOURCES = mge-utalk.c microdowell_SOURCES = microdowell.c +microsol_apc_SOURCES = microsol-apc.c microsol-common.c +microsol_apc_LDADD = $(LDADD) -lm oneac_SOURCES = oneac.c optiups_SOURCES = optiups.c powercom_SOURCES = powercom.c @@ -682,72 +1077,92 @@ powerpanel_LDADD = $(LDADD) -lm rhino_SOURCES = rhino.c rhino_LDADD = $(LDADD) -lm safenet_SOURCES = safenet.c +nutdrv_siemens_sitop_SOURCES = nutdrv_siemens_sitop.c solis_SOURCES = solis.c +solis_LDADD = $(LDADD) -lm tripplite_SOURCES = tripplite.c tripplite_LDADD = $(LDADD) -lm tripplitesu_SOURCES = tripplitesu.c upscode2_SOURCES = upscode2.c upscode2_LDADD = $(LDADD) -lm victronups_SOURCES = victronups.c +riello_ser_SOURCES = riello.c riello_ser.c +riello_ser_LDADD = $(LDADD) -lm # non-serial drivers: these use custom LDADD and/or CFLAGS # dummy dummy_ups_SOURCES = dummy-ups.c dummy_ups_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/clients \ - $(am__append_13) -dummy_ups_LDADD = $(LDADD_DRIVERS) ../clients/libupsclient.la \ - $(am__append_14) + $(am__append_16) +dummy_ups_LDADD = $(LDADD_DRIVERS) \ + $(top_builddir)/clients/libupsclient.la $(am__append_17) # Clone drivers clone_SOURCES = clone.c clone_outlet_SOURCES = clone-outlet.c +# apcupsd client driver +apcupsd_ups_SOURCES = apcupsd-ups.c +apcupsd_ups_CFLAGS = $(AM_CFLAGS) +apcupsd_ups_LDADD = $(LDADD_DRIVERS) + # sample skeleton driver skel_SOURCES = skel.c skel_LDADD = $(LDADD_DRIVERS) # USB -USBHID_UPS_SUBDRIVERS = apc-hid.c belkin-hid.c cps-hid.c explore-hid.c \ - liebert-hid.c mge-hid.c powercom-hid.c tripplite-hid.c idowell-hid.c +@WITH_LIBUSB_0_1_TRUE@LIBUSB_IMPL = libusb0.c +@WITH_LIBUSB_1_0_TRUE@LIBUSB_IMPL = libusb1.c +USBHID_UPS_SUBDRIVERS = apc-hid.c arduino-hid.c belkin-hid.c cps-hid.c explore-hid.c \ + liebert-hid.c mge-hid.c powercom-hid.c tripplite-hid.c idowell-hid.c \ + openups-hid.c powervar-hid.c delta_ups-hid.c ever-hid.c legrand-hid.c salicru-hid.c -usbhid_ups_SOURCES = usbhid-ups.c libhid.c libusb.c hidparser.c \ +usbhid_ups_SOURCES = usbhid-ups.c libhid.c $(LIBUSB_IMPL) hidparser.c \ usb-common.c $(USBHID_UPS_SUBDRIVERS) -usbhid_ups_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -tripplite_usb_SOURCES = tripplite_usb.c libusb.c usb-common.c +usbhid_ups_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm +tripplite_usb_SOURCES = tripplite_usb.c $(LIBUSB_IMPL) usb-common.c tripplite_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm bcmxcp_usb_SOURCES = bcmxcp_usb.c bcmxcp.c usb-common.c -bcmxcp_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -blazer_usb_SOURCES = blazer.c blazer_usb.c libusb.c usb-common.c +bcmxcp_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm +blazer_usb_SOURCES = blazer.c blazer_usb.c $(LIBUSB_IMPL) usb-common.c blazer_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm +nutdrv_atcl_usb_SOURCES = nutdrv_atcl_usb.c usb-common.c +nutdrv_atcl_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) richcomm_usb_SOURCES = richcomm_usb.c usb-common.c richcomm_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) +riello_usb_SOURCES = riello.c riello_usb.c $(LIBUSB_IMPL) usb-common.c +riello_usb_LDADD = $(LDADD_DRIVERS) $(LIBUSB_LIBS) -lm # HID-over-serial -newmge_shut_SOURCES = usbhid-ups.c libshut.c libhid.c hidparser.c mge-hid.c +mge_shut_SOURCES = usbhid-ups.c libshut.c libhid.c hidparser.c mge-hid.c # per-target CFLAGS are necessary here -newmge_shut_CFLAGS = $(AM_CFLAGS) -DSHUT_MODE -newmge_shut_LDADD = $(LDADD) +mge_shut_CFLAGS = $(AM_CFLAGS) -DSHUT_MODE=1 +mge_shut_LDADD = $(LDADD) -lm # SNMP -snmp_ups_SOURCES = snmp-ups.c apc-mib.c baytech-mib.c compaq-mib.c eaton-mib.c \ - ietf-mib.c mge-mib.c netvision-mib.c powerware-mib.c raritan-pdu-mib.c \ - bestpower-mib.c cyberpower-mib.c +# Please keep the MIB table below sorted roughly alphabetically (incidentally +# by vendor too) to ease maintenance and codebase fork resynchronisations +snmp_ups_SOURCES = snmp-ups.c snmp-ups-helpers.c \ + apc-mib.c apc-pdu-mib.c \ + baytech-mib.c bestpower-mib.c \ + compaq-mib.c cyberpower-mib.c \ + delta_ups-mib.c \ + eaton-pdu-genesis2-mib.c eaton-pdu-marlin-mib.c eaton-pdu-marlin-helpers.c \ + eaton-pdu-pulizzi-mib.c eaton-pdu-revelation-mib.c \ + eaton-ats16-nmc-mib.c eaton-ats16-nm2-mib.c apc-ats-mib.c eaton-ats30-mib.c \ + emerson-avocent-pdu-mib.c \ + hpe-pdu-mib.c huawei-mib.c \ + ietf-mib.c \ + mge-mib.c \ + netvision-mib.c \ + powerware-mib.c \ + raritan-pdu-mib.c raritan-px2-mib.c \ + xppc-mib.c -snmp_ups_LDADD = $(LDADD_DRIVERS) $(LIBNETSNMP_LIBS) - -# HAL -hald_addon_usbhid_ups_SOURCES = usbhid-ups.c libhid.c libusb.c hidparser.c \ - $(USBHID_UPS_SUBDRIVERS) - -hald_addon_usbhid_ups_LDADD = $(LDADD_HAL_DRIVERS) $(LIBUSB_LIBS) $(LIBHAL_LIBS) -hald_addon_tripplite_usb_SOURCES = tripplite_usb.c libusb.c -hald_addon_tripplite_usb_LDADD = $(LDADD_HAL_DRIVERS) $(LIBUSB_LIBS) $(LIBHAL_LIBS) -lm -hald_addon_bcmxcp_usb_SOURCES = bcmxcp_usb.c bcmxcp.c -hald_addon_bcmxcp_usb_LDADD = $(LDADD_HAL_DRIVERS) $(LIBUSB_LIBS) $(LIBHAL_LIBS) -hald_addon_blazer_usb_SOURCES = blazer.c blazer_usb.c libusb.c -hald_addon_blazer_usb_LDADD = $(LDADD_HAL_DRIVERS) $(LIBUSB_LIBS) $(LIBHAL_LIBS) -lm +snmp_ups_CFLAGS = $(AM_CFLAGS) $(LIBNETSNMP_CFLAGS) +snmp_ups_LDADD = $(LDADD_DRIVERS) $(LIBNETSNMP_LIBS) -lm # NEON XML/HTTP netxml_ups_SOURCES = netxml-ups.c mge-xml.c @@ -758,37 +1173,93 @@ powerman_pdu_SOURCES = powerman-pdu.c powerman_pdu_LDADD = $(LDADD) $(LIBPOWERMAN_LIBS) # IPMI PSU -nut_ipmipsu_SOURCES = nut-ipmipsu.c $(am__append_15) +nut_ipmipsu_SOURCES = nut-ipmipsu.c $(am__append_18) +# FIXME: Hacky hot-fix for build agents of varying OS generations: +# Different versions of IPMI libs requested 'unsigned int *' or 'int *' args: +#nut_ipmipsu_CFLAGS = $(AM_CFLAGS) -Wno-pointer-sign nut_ipmipsu_LDADD = $(LDADD) $(LIBIPMI_LIBS) +# Mac OS X metadriver +macosx_ups_LDADD = $(LDADD_DRIVERS) +macosx_ups_LDFLAGS = $(LDFLAGS) -framework IOKit -framework CoreFoundation +macosx_ups_SOURCES = macosx-ups.c + +# Modbus drivers +phoenixcontact_modbus_SOURCES = phoenixcontact_modbus.c +phoenixcontact_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) +generic_modbus_SOURCES = generic_modbus.c +generic_modbus_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) +adelsystem_cbi_SOURCES = adelsystem_cbi.c +adelsystem_cbi_LDADD = $(LDADD_DRIVERS) $(LIBMODBUS_LIBS) + +# Huawei UPS2000 driver +# (this is both a Modbus and a serial driver) +huawei_ups2000_SOURCES = huawei-ups2000.c +huawei_ups2000_LDADD = $(LDADD_DRIVERS_SERIAL) $(LIBMODBUS_LIBS) + +# Socomec JBUS driver +# (this is a Modbus driver) +socomec_jbus_SOURCES = socomec_jbus.c +socomec_jbus_LDADD = $(LDADD_DRIVERS_SERIAL) $(LIBMODBUS_LIBS) + +# Linux I2C drivers +asem_LDADD = $(LDADD_DRIVERS) +asem_SOURCES = asem.c +pijuice_LDADD = $(LDADD_DRIVERS) +pijuice_SOURCES = pijuice.c + +# nutdrv_qx USB/Serial +nutdrv_qx_SOURCES = nutdrv_qx.c $(am__append_22) \ + $(NUTDRV_QX_SUBDRIVERS) +nutdrv_qx_LDADD = $(LDADD_DRIVERS) -lm $(am__append_20) \ + $(am__append_23) +nutdrv_qx_CFLAGS = $(AM_CFLAGS) $(am__append_19) $(am__append_21) +NUTDRV_QX_SUBDRIVERS = nutdrv_qx_bestups.c nutdrv_qx_blazer-common.c \ + nutdrv_qx_masterguard.c \ + nutdrv_qx_mecer.c nutdrv_qx_megatec.c nutdrv_qx_megatec-old.c \ + nutdrv_qx_mustek.c nutdrv_qx_q1.c nutdrv_qx_voltronic.c \ + nutdrv_qx_voltronic-qs.c nutdrv_qx_voltronic-qs-hex.c nutdrv_qx_zinto.c \ + nutdrv_qx_hunnox.c nutdrv_qx_ablerex.c + + # ---------------------------------------------------------------------- # List of header files. The purpose of this list is not dependency # tracking (which is automatic), but to ensure these files are # distributed by "make dist". -dist_noinst_HEADERS = apc-mib.h apc-hid.h baytech-mib.h bcmxcp.h \ - bcmxcp_io.h belkin.h belkin-hid.h bestpower-mib.h blazer.h cps-hid.h dstate.h \ - dstate-hal.h dummy-ups.h eaton-mib.h explore-hid.h gamatronic.h genericups.h \ - hidparser.h hidtypes.h ietf-mib.h libhid.h libshut.h libusb.h liebert-hid.h \ - main.h main-hal.h mge-hid.h mge-mib.h mge-shut.h mge-utalk.h \ - mge-xml.h microdowell.h netvision-mib.h netxml-ups.h nut-ipmi.h oneac.h \ +dist_noinst_HEADERS = apc-mib.h apc-iem-mib.h apc-hid.h arduino-hid.h baytech-mib.h bcmxcp.h bcmxcp_ser.h \ + bcmxcp_io.h belkin.h belkin-hid.h bestpower-mib.h blazer.h cps-hid.h dstate.h \ + dummy-ups.h explore-hid.h gamatronic.h genericups.h \ + hidparser.h hidtypes.h ietf-mib.h libhid.h libshut.h nut_libusb.h liebert-hid.h \ + main.h mge-hid.h mge-mib.h mge-utalk.h \ + mge-xml.h microdowell.h microsol-apc.h microsol-common.h netvision-mib.h netxml-ups.h nut-ipmi.h oneac.h \ powercom.h powerpanel.h powerp-bin.h powerp-txt.h powerware-mib.h raritan-pdu-mib.h \ safenet.h serial.h snmp-ups.h solis.h tripplite.h tripplite-hid.h \ upshandler.h usb-common.h usbhid-ups.h powercom-hid.h compaq-mib.h idowell-hid.h \ - apcsmart.h apcsmart_tabs.h apcsmart-old.h cyberpower-mib.h + apcsmart.h apcsmart_tabs.h apcsmart-old.h apcupsd-ups.h cyberpower-mib.h riello.h openups-hid.h \ + delta_ups-mib.h nutdrv_qx.h nutdrv_qx_bestups.h nutdrv_qx_blazer-common.h \ + nutdrv_qx_masterguard.h \ + nutdrv_qx_mecer.h nutdrv_qx_ablerex.h \ + nutdrv_qx_megatec.h nutdrv_qx_megatec-old.h nutdrv_qx_mustek.h nutdrv_qx_q1.h nutdrv_qx_hunnox.h \ + nutdrv_qx_voltronic.h nutdrv_qx_voltronic-qs.h nutdrv_qx_voltronic-qs-hex.h nutdrv_qx_zinto.h \ + xppc-mib.h huawei-mib.h eaton-ats16-nmc-mib.h eaton-ats16-nm2-mib.h apc-ats-mib.h raritan-px2-mib.h eaton-ats30-mib.h \ + apc-pdu-mib.h ever-hid.h eaton-pdu-genesis2-mib.h eaton-pdu-marlin-mib.h eaton-pdu-marlin-helpers.h \ + eaton-pdu-pulizzi-mib.h eaton-pdu-revelation-mib.h emerson-avocent-pdu-mib.h legrand-hid.h \ + hpe-pdu-mib.h powervar-hid.h delta_ups-hid.h generic_modbus.h salicru-hid.h adelsystem_cbi.h # Define a dummy library so that Automake builds rules for the # corresponding object files. This library is not actually built, - -# the nuthalmain library combines the code for main-hal.c and -# dstate-hal.c. It is necessary for Automake-technical reasons, +# as a final product. It was necessary for Automake-technical reasons, # because per-object CFLAGS can only be specified for libraries, not # for object files. This library is used during the build process, # and is not meant to be installed. -EXTRA_LIBRARIES = libdummy.a libnuthalmain.a -libdummy_a_SOURCES = main.c dstate.c serial.c -libnuthalmain_a_SOURCES = main-hal.c dstate-hal.c usb-common.c -MOSTLYCLEANFILES = libnuthalmain.a +EXTRA_LTLIBRARIES = libdummy.la libdummy_serial.la +libdummy_la_SOURCES = main.c dstate.c +libdummy_la_LDFLAGS = -no-undefined -static +libdummy_serial_la_SOURCES = serial.c +libdummy_serial_la_LDFLAGS = -no-undefined -static +CLEANFILES = $(EXTRA_LTLIBRARIES) $(EXTRA_PROGRAMS) +MAINTAINERCLEANFILES = Makefile.in .dirstamp all: all-am .SUFFIXES: @@ -805,14 +1276,13 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu drivers/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu drivers/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -823,24 +1293,21 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): -libdummy.a: $(libdummy_a_OBJECTS) $(libdummy_a_DEPENDENCIES) - -rm -f libdummy.a - $(libdummy_a_AR) libdummy.a $(libdummy_a_OBJECTS) $(libdummy_a_LIBADD) - $(RANLIB) libdummy.a -libnuthalmain.a: $(libnuthalmain_a_OBJECTS) $(libnuthalmain_a_DEPENDENCIES) - -rm -f libnuthalmain.a - $(libnuthalmain_a_AR) libnuthalmain.a $(libnuthalmain_a_OBJECTS) $(libnuthalmain_a_LIBADD) - $(RANLIB) libnuthalmain.a install-driverexecPROGRAMS: $(driverexec_PROGRAMS) @$(NORMAL_INSTALL) - test -z "$(driverexecdir)" || $(MKDIR_P) "$(DESTDIR)$(driverexecdir)" @list='$(driverexec_PROGRAMS)'; test -n "$(driverexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(driverexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(driverexecdir)" || exit 1; \ + fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ - while read p p1; do if test -f $$p || test -f $$p1; \ - then echo "$$p"; echo "$$p"; else :; fi; \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ - sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ @@ -861,7 +1328,8 @@ uninstall-driverexecPROGRAMS: @list='$(driverexec_PROGRAMS)'; test -n "$(driverexecdir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ - -e 's/$$/$(EXEEXT)/' `; \ + -e 's/$$/$(EXEEXT)/' \ + `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(driverexecdir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(driverexecdir)" && rm -f $$files @@ -874,16 +1342,21 @@ clean-driverexecPROGRAMS: list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list -install-halexecPROGRAMS: $(halexec_PROGRAMS) +install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) - test -z "$(halexecdir)" || $(MKDIR_P) "$(DESTDIR)$(halexecdir)" - @list='$(halexec_PROGRAMS)'; test -n "$(halexecdir)" || list=; \ + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ - while read p p1; do if test -f $$p || test -f $$p1; \ - then echo "$$p"; echo "$$p"; else :; fi; \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ - sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ @@ -894,188 +1367,292 @@ install-halexecPROGRAMS: $(halexec_PROGRAMS) while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ - echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(halexecdir)$$dir'"; \ - $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(halexecdir)$$dir" || exit $$?; \ + echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ } \ ; done -uninstall-halexecPROGRAMS: +uninstall-sbinPROGRAMS: @$(NORMAL_UNINSTALL) - @list='$(halexec_PROGRAMS)'; test -n "$(halexecdir)" || list=; \ + @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ - -e 's/$$/$(EXEEXT)/' `; \ + -e 's/$$/$(EXEEXT)/' \ + `; \ test -n "$$list" || exit 0; \ - echo " ( cd '$(DESTDIR)$(halexecdir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(halexecdir)" && rm -f $$files + echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(sbindir)" && rm -f $$files -clean-halexecPROGRAMS: - @list='$(halexec_PROGRAMS)'; test -n "$$list" || exit 0; \ +clean-sbinPROGRAMS: + @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \ echo " rm -f" $$list; \ rm -f $$list || exit $$?; \ test -n "$(EXEEXT)" || exit 0; \ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list -apcsmart$(EXEEXT): $(apcsmart_OBJECTS) $(apcsmart_DEPENDENCIES) + +libdummy.la: $(libdummy_la_OBJECTS) $(libdummy_la_DEPENDENCIES) $(EXTRA_libdummy_la_DEPENDENCIES) + $(AM_V_CCLD)$(libdummy_la_LINK) $(libdummy_la_OBJECTS) $(libdummy_la_LIBADD) $(LIBS) + +libdummy_serial.la: $(libdummy_serial_la_OBJECTS) $(libdummy_serial_la_DEPENDENCIES) $(EXTRA_libdummy_serial_la_DEPENDENCIES) + $(AM_V_CCLD)$(libdummy_serial_la_LINK) $(libdummy_serial_la_OBJECTS) $(libdummy_serial_la_LIBADD) $(LIBS) + +adelsystem_cbi$(EXEEXT): $(adelsystem_cbi_OBJECTS) $(adelsystem_cbi_DEPENDENCIES) $(EXTRA_adelsystem_cbi_DEPENDENCIES) + @rm -f adelsystem_cbi$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(adelsystem_cbi_OBJECTS) $(adelsystem_cbi_LDADD) $(LIBS) + +al175$(EXEEXT): $(al175_OBJECTS) $(al175_DEPENDENCIES) $(EXTRA_al175_DEPENDENCIES) + @rm -f al175$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(al175_OBJECTS) $(al175_LDADD) $(LIBS) + +apcsmart$(EXEEXT): $(apcsmart_OBJECTS) $(apcsmart_DEPENDENCIES) $(EXTRA_apcsmart_DEPENDENCIES) @rm -f apcsmart$(EXEEXT) - $(LINK) $(apcsmart_OBJECTS) $(apcsmart_LDADD) $(LIBS) -apcsmart-old$(EXEEXT): $(apcsmart_old_OBJECTS) $(apcsmart_old_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(apcsmart_OBJECTS) $(apcsmart_LDADD) $(LIBS) + +apcsmart-old$(EXEEXT): $(apcsmart_old_OBJECTS) $(apcsmart_old_DEPENDENCIES) $(EXTRA_apcsmart_old_DEPENDENCIES) @rm -f apcsmart-old$(EXEEXT) - $(LINK) $(apcsmart_old_OBJECTS) $(apcsmart_old_LDADD) $(LIBS) -bcmxcp$(EXEEXT): $(bcmxcp_OBJECTS) $(bcmxcp_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(apcsmart_old_OBJECTS) $(apcsmart_old_LDADD) $(LIBS) + +apcupsd-ups$(EXEEXT): $(apcupsd_ups_OBJECTS) $(apcupsd_ups_DEPENDENCIES) $(EXTRA_apcupsd_ups_DEPENDENCIES) + @rm -f apcupsd-ups$(EXEEXT) + $(AM_V_CCLD)$(apcupsd_ups_LINK) $(apcupsd_ups_OBJECTS) $(apcupsd_ups_LDADD) $(LIBS) + +asem$(EXEEXT): $(asem_OBJECTS) $(asem_DEPENDENCIES) $(EXTRA_asem_DEPENDENCIES) + @rm -f asem$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(asem_OBJECTS) $(asem_LDADD) $(LIBS) + +bcmxcp$(EXEEXT): $(bcmxcp_OBJECTS) $(bcmxcp_DEPENDENCIES) $(EXTRA_bcmxcp_DEPENDENCIES) @rm -f bcmxcp$(EXEEXT) - $(LINK) $(bcmxcp_OBJECTS) $(bcmxcp_LDADD) $(LIBS) -bcmxcp_usb$(EXEEXT): $(bcmxcp_usb_OBJECTS) $(bcmxcp_usb_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(bcmxcp_OBJECTS) $(bcmxcp_LDADD) $(LIBS) + +bcmxcp_usb$(EXEEXT): $(bcmxcp_usb_OBJECTS) $(bcmxcp_usb_DEPENDENCIES) $(EXTRA_bcmxcp_usb_DEPENDENCIES) @rm -f bcmxcp_usb$(EXEEXT) - $(LINK) $(bcmxcp_usb_OBJECTS) $(bcmxcp_usb_LDADD) $(LIBS) -belkin$(EXEEXT): $(belkin_OBJECTS) $(belkin_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(bcmxcp_usb_OBJECTS) $(bcmxcp_usb_LDADD) $(LIBS) + +belkin$(EXEEXT): $(belkin_OBJECTS) $(belkin_DEPENDENCIES) $(EXTRA_belkin_DEPENDENCIES) @rm -f belkin$(EXEEXT) - $(LINK) $(belkin_OBJECTS) $(belkin_LDADD) $(LIBS) -belkinunv$(EXEEXT): $(belkinunv_OBJECTS) $(belkinunv_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(belkin_OBJECTS) $(belkin_LDADD) $(LIBS) + +belkinunv$(EXEEXT): $(belkinunv_OBJECTS) $(belkinunv_DEPENDENCIES) $(EXTRA_belkinunv_DEPENDENCIES) @rm -f belkinunv$(EXEEXT) - $(LINK) $(belkinunv_OBJECTS) $(belkinunv_LDADD) $(LIBS) -bestfcom$(EXEEXT): $(bestfcom_OBJECTS) $(bestfcom_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(belkinunv_OBJECTS) $(belkinunv_LDADD) $(LIBS) + +bestfcom$(EXEEXT): $(bestfcom_OBJECTS) $(bestfcom_DEPENDENCIES) $(EXTRA_bestfcom_DEPENDENCIES) @rm -f bestfcom$(EXEEXT) - $(LINK) $(bestfcom_OBJECTS) $(bestfcom_LDADD) $(LIBS) -bestfortress$(EXEEXT): $(bestfortress_OBJECTS) $(bestfortress_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(bestfcom_OBJECTS) $(bestfcom_LDADD) $(LIBS) + +bestfortress$(EXEEXT): $(bestfortress_OBJECTS) $(bestfortress_DEPENDENCIES) $(EXTRA_bestfortress_DEPENDENCIES) @rm -f bestfortress$(EXEEXT) - $(LINK) $(bestfortress_OBJECTS) $(bestfortress_LDADD) $(LIBS) -bestuferrups$(EXEEXT): $(bestuferrups_OBJECTS) $(bestuferrups_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(bestfortress_OBJECTS) $(bestfortress_LDADD) $(LIBS) + +bestuferrups$(EXEEXT): $(bestuferrups_OBJECTS) $(bestuferrups_DEPENDENCIES) $(EXTRA_bestuferrups_DEPENDENCIES) @rm -f bestuferrups$(EXEEXT) - $(LINK) $(bestuferrups_OBJECTS) $(bestuferrups_LDADD) $(LIBS) -bestups$(EXEEXT): $(bestups_OBJECTS) $(bestups_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(bestuferrups_OBJECTS) $(bestuferrups_LDADD) $(LIBS) + +bestups$(EXEEXT): $(bestups_OBJECTS) $(bestups_DEPENDENCIES) $(EXTRA_bestups_DEPENDENCIES) @rm -f bestups$(EXEEXT) - $(LINK) $(bestups_OBJECTS) $(bestups_LDADD) $(LIBS) -blazer_ser$(EXEEXT): $(blazer_ser_OBJECTS) $(blazer_ser_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(bestups_OBJECTS) $(bestups_LDADD) $(LIBS) + +blazer_ser$(EXEEXT): $(blazer_ser_OBJECTS) $(blazer_ser_DEPENDENCIES) $(EXTRA_blazer_ser_DEPENDENCIES) @rm -f blazer_ser$(EXEEXT) - $(LINK) $(blazer_ser_OBJECTS) $(blazer_ser_LDADD) $(LIBS) -blazer_usb$(EXEEXT): $(blazer_usb_OBJECTS) $(blazer_usb_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(blazer_ser_OBJECTS) $(blazer_ser_LDADD) $(LIBS) + +blazer_usb$(EXEEXT): $(blazer_usb_OBJECTS) $(blazer_usb_DEPENDENCIES) $(EXTRA_blazer_usb_DEPENDENCIES) @rm -f blazer_usb$(EXEEXT) - $(LINK) $(blazer_usb_OBJECTS) $(blazer_usb_LDADD) $(LIBS) -clone$(EXEEXT): $(clone_OBJECTS) $(clone_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(blazer_usb_OBJECTS) $(blazer_usb_LDADD) $(LIBS) + +clone$(EXEEXT): $(clone_OBJECTS) $(clone_DEPENDENCIES) $(EXTRA_clone_DEPENDENCIES) @rm -f clone$(EXEEXT) - $(LINK) $(clone_OBJECTS) $(clone_LDADD) $(LIBS) -clone-outlet$(EXEEXT): $(clone_outlet_OBJECTS) $(clone_outlet_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(clone_OBJECTS) $(clone_LDADD) $(LIBS) + +clone-outlet$(EXEEXT): $(clone_outlet_OBJECTS) $(clone_outlet_DEPENDENCIES) $(EXTRA_clone_outlet_DEPENDENCIES) @rm -f clone-outlet$(EXEEXT) - $(LINK) $(clone_outlet_OBJECTS) $(clone_outlet_LDADD) $(LIBS) -dummy-ups$(EXEEXT): $(dummy_ups_OBJECTS) $(dummy_ups_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(clone_outlet_OBJECTS) $(clone_outlet_LDADD) $(LIBS) + +dummy-ups$(EXEEXT): $(dummy_ups_OBJECTS) $(dummy_ups_DEPENDENCIES) $(EXTRA_dummy_ups_DEPENDENCIES) @rm -f dummy-ups$(EXEEXT) - $(dummy_ups_LINK) $(dummy_ups_OBJECTS) $(dummy_ups_LDADD) $(LIBS) -etapro$(EXEEXT): $(etapro_OBJECTS) $(etapro_DEPENDENCIES) + $(AM_V_CCLD)$(dummy_ups_LINK) $(dummy_ups_OBJECTS) $(dummy_ups_LDADD) $(LIBS) + +etapro$(EXEEXT): $(etapro_OBJECTS) $(etapro_DEPENDENCIES) $(EXTRA_etapro_DEPENDENCIES) @rm -f etapro$(EXEEXT) - $(LINK) $(etapro_OBJECTS) $(etapro_LDADD) $(LIBS) -everups$(EXEEXT): $(everups_OBJECTS) $(everups_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(etapro_OBJECTS) $(etapro_LDADD) $(LIBS) + +everups$(EXEEXT): $(everups_OBJECTS) $(everups_DEPENDENCIES) $(EXTRA_everups_DEPENDENCIES) @rm -f everups$(EXEEXT) - $(LINK) $(everups_OBJECTS) $(everups_LDADD) $(LIBS) -gamatronic$(EXEEXT): $(gamatronic_OBJECTS) $(gamatronic_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(everups_OBJECTS) $(everups_LDADD) $(LIBS) + +gamatronic$(EXEEXT): $(gamatronic_OBJECTS) $(gamatronic_DEPENDENCIES) $(EXTRA_gamatronic_DEPENDENCIES) @rm -f gamatronic$(EXEEXT) - $(LINK) $(gamatronic_OBJECTS) $(gamatronic_LDADD) $(LIBS) -genericups$(EXEEXT): $(genericups_OBJECTS) $(genericups_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(gamatronic_OBJECTS) $(gamatronic_LDADD) $(LIBS) + +generic_modbus$(EXEEXT): $(generic_modbus_OBJECTS) $(generic_modbus_DEPENDENCIES) $(EXTRA_generic_modbus_DEPENDENCIES) + @rm -f generic_modbus$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(generic_modbus_OBJECTS) $(generic_modbus_LDADD) $(LIBS) + +genericups$(EXEEXT): $(genericups_OBJECTS) $(genericups_DEPENDENCIES) $(EXTRA_genericups_DEPENDENCIES) @rm -f genericups$(EXEEXT) - $(LINK) $(genericups_OBJECTS) $(genericups_LDADD) $(LIBS) -hald-addon-bcmxcp_usb$(EXEEXT): $(hald_addon_bcmxcp_usb_OBJECTS) $(hald_addon_bcmxcp_usb_DEPENDENCIES) - @rm -f hald-addon-bcmxcp_usb$(EXEEXT) - $(LINK) $(hald_addon_bcmxcp_usb_OBJECTS) $(hald_addon_bcmxcp_usb_LDADD) $(LIBS) -hald-addon-blazer_usb$(EXEEXT): $(hald_addon_blazer_usb_OBJECTS) $(hald_addon_blazer_usb_DEPENDENCIES) - @rm -f hald-addon-blazer_usb$(EXEEXT) - $(LINK) $(hald_addon_blazer_usb_OBJECTS) $(hald_addon_blazer_usb_LDADD) $(LIBS) -hald-addon-tripplite_usb$(EXEEXT): $(hald_addon_tripplite_usb_OBJECTS) $(hald_addon_tripplite_usb_DEPENDENCIES) - @rm -f hald-addon-tripplite_usb$(EXEEXT) - $(LINK) $(hald_addon_tripplite_usb_OBJECTS) $(hald_addon_tripplite_usb_LDADD) $(LIBS) -hald-addon-usbhid-ups$(EXEEXT): $(hald_addon_usbhid_ups_OBJECTS) $(hald_addon_usbhid_ups_DEPENDENCIES) - @rm -f hald-addon-usbhid-ups$(EXEEXT) - $(LINK) $(hald_addon_usbhid_ups_OBJECTS) $(hald_addon_usbhid_ups_LDADD) $(LIBS) -isbmex$(EXEEXT): $(isbmex_OBJECTS) $(isbmex_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(genericups_OBJECTS) $(genericups_LDADD) $(LIBS) + +huawei-ups2000$(EXEEXT): $(huawei_ups2000_OBJECTS) $(huawei_ups2000_DEPENDENCIES) $(EXTRA_huawei_ups2000_DEPENDENCIES) + @rm -f huawei-ups2000$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(huawei_ups2000_OBJECTS) $(huawei_ups2000_LDADD) $(LIBS) + +isbmex$(EXEEXT): $(isbmex_OBJECTS) $(isbmex_DEPENDENCIES) $(EXTRA_isbmex_DEPENDENCIES) @rm -f isbmex$(EXEEXT) - $(LINK) $(isbmex_OBJECTS) $(isbmex_LDADD) $(LIBS) -ivtscd$(EXEEXT): $(ivtscd_OBJECTS) $(ivtscd_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(isbmex_OBJECTS) $(isbmex_LDADD) $(LIBS) + +ivtscd$(EXEEXT): $(ivtscd_OBJECTS) $(ivtscd_DEPENDENCIES) $(EXTRA_ivtscd_DEPENDENCIES) @rm -f ivtscd$(EXEEXT) - $(LINK) $(ivtscd_OBJECTS) $(ivtscd_LDADD) $(LIBS) -liebert$(EXEEXT): $(liebert_OBJECTS) $(liebert_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(ivtscd_OBJECTS) $(ivtscd_LDADD) $(LIBS) + +liebert$(EXEEXT): $(liebert_OBJECTS) $(liebert_DEPENDENCIES) $(EXTRA_liebert_DEPENDENCIES) @rm -f liebert$(EXEEXT) - $(LINK) $(liebert_OBJECTS) $(liebert_LDADD) $(LIBS) -liebert-esp2$(EXEEXT): $(liebert_esp2_OBJECTS) $(liebert_esp2_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(liebert_OBJECTS) $(liebert_LDADD) $(LIBS) + +liebert-esp2$(EXEEXT): $(liebert_esp2_OBJECTS) $(liebert_esp2_DEPENDENCIES) $(EXTRA_liebert_esp2_DEPENDENCIES) @rm -f liebert-esp2$(EXEEXT) - $(LINK) $(liebert_esp2_OBJECTS) $(liebert_esp2_LDADD) $(LIBS) -masterguard$(EXEEXT): $(masterguard_OBJECTS) $(masterguard_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(liebert_esp2_OBJECTS) $(liebert_esp2_LDADD) $(LIBS) + +macosx-ups$(EXEEXT): $(macosx_ups_OBJECTS) $(macosx_ups_DEPENDENCIES) $(EXTRA_macosx_ups_DEPENDENCIES) + @rm -f macosx-ups$(EXEEXT) + $(AM_V_CCLD)$(macosx_ups_LINK) $(macosx_ups_OBJECTS) $(macosx_ups_LDADD) $(LIBS) + +masterguard$(EXEEXT): $(masterguard_OBJECTS) $(masterguard_DEPENDENCIES) $(EXTRA_masterguard_DEPENDENCIES) @rm -f masterguard$(EXEEXT) - $(LINK) $(masterguard_OBJECTS) $(masterguard_LDADD) $(LIBS) -metasys$(EXEEXT): $(metasys_OBJECTS) $(metasys_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(masterguard_OBJECTS) $(masterguard_LDADD) $(LIBS) + +metasys$(EXEEXT): $(metasys_OBJECTS) $(metasys_DEPENDENCIES) $(EXTRA_metasys_DEPENDENCIES) @rm -f metasys$(EXEEXT) - $(LINK) $(metasys_OBJECTS) $(metasys_LDADD) $(LIBS) -mge-shut$(EXEEXT): $(mge_shut_OBJECTS) $(mge_shut_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(metasys_OBJECTS) $(metasys_LDADD) $(LIBS) + +mge-shut$(EXEEXT): $(mge_shut_OBJECTS) $(mge_shut_DEPENDENCIES) $(EXTRA_mge_shut_DEPENDENCIES) @rm -f mge-shut$(EXEEXT) - $(LINK) $(mge_shut_OBJECTS) $(mge_shut_LDADD) $(LIBS) -mge-utalk$(EXEEXT): $(mge_utalk_OBJECTS) $(mge_utalk_DEPENDENCIES) + $(AM_V_CCLD)$(mge_shut_LINK) $(mge_shut_OBJECTS) $(mge_shut_LDADD) $(LIBS) + +mge-utalk$(EXEEXT): $(mge_utalk_OBJECTS) $(mge_utalk_DEPENDENCIES) $(EXTRA_mge_utalk_DEPENDENCIES) @rm -f mge-utalk$(EXEEXT) - $(LINK) $(mge_utalk_OBJECTS) $(mge_utalk_LDADD) $(LIBS) -microdowell$(EXEEXT): $(microdowell_OBJECTS) $(microdowell_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(mge_utalk_OBJECTS) $(mge_utalk_LDADD) $(LIBS) + +microdowell$(EXEEXT): $(microdowell_OBJECTS) $(microdowell_DEPENDENCIES) $(EXTRA_microdowell_DEPENDENCIES) @rm -f microdowell$(EXEEXT) - $(LINK) $(microdowell_OBJECTS) $(microdowell_LDADD) $(LIBS) -netxml-ups$(EXEEXT): $(netxml_ups_OBJECTS) $(netxml_ups_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(microdowell_OBJECTS) $(microdowell_LDADD) $(LIBS) + +microsol-apc$(EXEEXT): $(microsol_apc_OBJECTS) $(microsol_apc_DEPENDENCIES) $(EXTRA_microsol_apc_DEPENDENCIES) + @rm -f microsol-apc$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(microsol_apc_OBJECTS) $(microsol_apc_LDADD) $(LIBS) + +netxml-ups$(EXEEXT): $(netxml_ups_OBJECTS) $(netxml_ups_DEPENDENCIES) $(EXTRA_netxml_ups_DEPENDENCIES) @rm -f netxml-ups$(EXEEXT) - $(LINK) $(netxml_ups_OBJECTS) $(netxml_ups_LDADD) $(LIBS) -newmge-shut$(EXEEXT): $(newmge_shut_OBJECTS) $(newmge_shut_DEPENDENCIES) - @rm -f newmge-shut$(EXEEXT) - $(newmge_shut_LINK) $(newmge_shut_OBJECTS) $(newmge_shut_LDADD) $(LIBS) -nut-ipmipsu$(EXEEXT): $(nut_ipmipsu_OBJECTS) $(nut_ipmipsu_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(netxml_ups_OBJECTS) $(netxml_ups_LDADD) $(LIBS) + +nut-ipmipsu$(EXEEXT): $(nut_ipmipsu_OBJECTS) $(nut_ipmipsu_DEPENDENCIES) $(EXTRA_nut_ipmipsu_DEPENDENCIES) @rm -f nut-ipmipsu$(EXEEXT) - $(LINK) $(nut_ipmipsu_OBJECTS) $(nut_ipmipsu_LDADD) $(LIBS) -oneac$(EXEEXT): $(oneac_OBJECTS) $(oneac_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(nut_ipmipsu_OBJECTS) $(nut_ipmipsu_LDADD) $(LIBS) + +nutdrv_atcl_usb$(EXEEXT): $(nutdrv_atcl_usb_OBJECTS) $(nutdrv_atcl_usb_DEPENDENCIES) $(EXTRA_nutdrv_atcl_usb_DEPENDENCIES) + @rm -f nutdrv_atcl_usb$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(nutdrv_atcl_usb_OBJECTS) $(nutdrv_atcl_usb_LDADD) $(LIBS) + +nutdrv_qx$(EXEEXT): $(nutdrv_qx_OBJECTS) $(nutdrv_qx_DEPENDENCIES) $(EXTRA_nutdrv_qx_DEPENDENCIES) + @rm -f nutdrv_qx$(EXEEXT) + $(AM_V_CCLD)$(nutdrv_qx_LINK) $(nutdrv_qx_OBJECTS) $(nutdrv_qx_LDADD) $(LIBS) + +nutdrv_siemens-sitop$(EXEEXT): $(nutdrv_siemens_sitop_OBJECTS) $(nutdrv_siemens_sitop_DEPENDENCIES) $(EXTRA_nutdrv_siemens_sitop_DEPENDENCIES) + @rm -f nutdrv_siemens-sitop$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(nutdrv_siemens_sitop_OBJECTS) $(nutdrv_siemens_sitop_LDADD) $(LIBS) + +oneac$(EXEEXT): $(oneac_OBJECTS) $(oneac_DEPENDENCIES) $(EXTRA_oneac_DEPENDENCIES) @rm -f oneac$(EXEEXT) - $(LINK) $(oneac_OBJECTS) $(oneac_LDADD) $(LIBS) -optiups$(EXEEXT): $(optiups_OBJECTS) $(optiups_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(oneac_OBJECTS) $(oneac_LDADD) $(LIBS) + +optiups$(EXEEXT): $(optiups_OBJECTS) $(optiups_DEPENDENCIES) $(EXTRA_optiups_DEPENDENCIES) @rm -f optiups$(EXEEXT) - $(LINK) $(optiups_OBJECTS) $(optiups_LDADD) $(LIBS) -powercom$(EXEEXT): $(powercom_OBJECTS) $(powercom_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(optiups_OBJECTS) $(optiups_LDADD) $(LIBS) + +phoenixcontact_modbus$(EXEEXT): $(phoenixcontact_modbus_OBJECTS) $(phoenixcontact_modbus_DEPENDENCIES) $(EXTRA_phoenixcontact_modbus_DEPENDENCIES) + @rm -f phoenixcontact_modbus$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(phoenixcontact_modbus_OBJECTS) $(phoenixcontact_modbus_LDADD) $(LIBS) + +pijuice$(EXEEXT): $(pijuice_OBJECTS) $(pijuice_DEPENDENCIES) $(EXTRA_pijuice_DEPENDENCIES) + @rm -f pijuice$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(pijuice_OBJECTS) $(pijuice_LDADD) $(LIBS) + +powercom$(EXEEXT): $(powercom_OBJECTS) $(powercom_DEPENDENCIES) $(EXTRA_powercom_DEPENDENCIES) @rm -f powercom$(EXEEXT) - $(LINK) $(powercom_OBJECTS) $(powercom_LDADD) $(LIBS) -powerman-pdu$(EXEEXT): $(powerman_pdu_OBJECTS) $(powerman_pdu_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(powercom_OBJECTS) $(powercom_LDADD) $(LIBS) + +powerman-pdu$(EXEEXT): $(powerman_pdu_OBJECTS) $(powerman_pdu_DEPENDENCIES) $(EXTRA_powerman_pdu_DEPENDENCIES) @rm -f powerman-pdu$(EXEEXT) - $(LINK) $(powerman_pdu_OBJECTS) $(powerman_pdu_LDADD) $(LIBS) -powerpanel$(EXEEXT): $(powerpanel_OBJECTS) $(powerpanel_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(powerman_pdu_OBJECTS) $(powerman_pdu_LDADD) $(LIBS) + +powerpanel$(EXEEXT): $(powerpanel_OBJECTS) $(powerpanel_DEPENDENCIES) $(EXTRA_powerpanel_DEPENDENCIES) @rm -f powerpanel$(EXEEXT) - $(LINK) $(powerpanel_OBJECTS) $(powerpanel_LDADD) $(LIBS) -rhino$(EXEEXT): $(rhino_OBJECTS) $(rhino_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(powerpanel_OBJECTS) $(powerpanel_LDADD) $(LIBS) + +rhino$(EXEEXT): $(rhino_OBJECTS) $(rhino_DEPENDENCIES) $(EXTRA_rhino_DEPENDENCIES) @rm -f rhino$(EXEEXT) - $(LINK) $(rhino_OBJECTS) $(rhino_LDADD) $(LIBS) -richcomm_usb$(EXEEXT): $(richcomm_usb_OBJECTS) $(richcomm_usb_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(rhino_OBJECTS) $(rhino_LDADD) $(LIBS) + +richcomm_usb$(EXEEXT): $(richcomm_usb_OBJECTS) $(richcomm_usb_DEPENDENCIES) $(EXTRA_richcomm_usb_DEPENDENCIES) @rm -f richcomm_usb$(EXEEXT) - $(LINK) $(richcomm_usb_OBJECTS) $(richcomm_usb_LDADD) $(LIBS) -safenet$(EXEEXT): $(safenet_OBJECTS) $(safenet_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(richcomm_usb_OBJECTS) $(richcomm_usb_LDADD) $(LIBS) + +riello_ser$(EXEEXT): $(riello_ser_OBJECTS) $(riello_ser_DEPENDENCIES) $(EXTRA_riello_ser_DEPENDENCIES) + @rm -f riello_ser$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(riello_ser_OBJECTS) $(riello_ser_LDADD) $(LIBS) + +riello_usb$(EXEEXT): $(riello_usb_OBJECTS) $(riello_usb_DEPENDENCIES) $(EXTRA_riello_usb_DEPENDENCIES) + @rm -f riello_usb$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(riello_usb_OBJECTS) $(riello_usb_LDADD) $(LIBS) + +safenet$(EXEEXT): $(safenet_OBJECTS) $(safenet_DEPENDENCIES) $(EXTRA_safenet_DEPENDENCIES) @rm -f safenet$(EXEEXT) - $(LINK) $(safenet_OBJECTS) $(safenet_LDADD) $(LIBS) -skel$(EXEEXT): $(skel_OBJECTS) $(skel_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(safenet_OBJECTS) $(safenet_LDADD) $(LIBS) + +skel$(EXEEXT): $(skel_OBJECTS) $(skel_DEPENDENCIES) $(EXTRA_skel_DEPENDENCIES) @rm -f skel$(EXEEXT) - $(LINK) $(skel_OBJECTS) $(skel_LDADD) $(LIBS) -snmp-ups$(EXEEXT): $(snmp_ups_OBJECTS) $(snmp_ups_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(skel_OBJECTS) $(skel_LDADD) $(LIBS) + +snmp-ups$(EXEEXT): $(snmp_ups_OBJECTS) $(snmp_ups_DEPENDENCIES) $(EXTRA_snmp_ups_DEPENDENCIES) @rm -f snmp-ups$(EXEEXT) - $(LINK) $(snmp_ups_OBJECTS) $(snmp_ups_LDADD) $(LIBS) -solis$(EXEEXT): $(solis_OBJECTS) $(solis_DEPENDENCIES) + $(AM_V_CCLD)$(snmp_ups_LINK) $(snmp_ups_OBJECTS) $(snmp_ups_LDADD) $(LIBS) + +socomec_jbus$(EXEEXT): $(socomec_jbus_OBJECTS) $(socomec_jbus_DEPENDENCIES) $(EXTRA_socomec_jbus_DEPENDENCIES) + @rm -f socomec_jbus$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(socomec_jbus_OBJECTS) $(socomec_jbus_LDADD) $(LIBS) + +solis$(EXEEXT): $(solis_OBJECTS) $(solis_DEPENDENCIES) $(EXTRA_solis_DEPENDENCIES) @rm -f solis$(EXEEXT) - $(LINK) $(solis_OBJECTS) $(solis_LDADD) $(LIBS) -tripplite$(EXEEXT): $(tripplite_OBJECTS) $(tripplite_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(solis_OBJECTS) $(solis_LDADD) $(LIBS) + +tripplite$(EXEEXT): $(tripplite_OBJECTS) $(tripplite_DEPENDENCIES) $(EXTRA_tripplite_DEPENDENCIES) @rm -f tripplite$(EXEEXT) - $(LINK) $(tripplite_OBJECTS) $(tripplite_LDADD) $(LIBS) -tripplite_usb$(EXEEXT): $(tripplite_usb_OBJECTS) $(tripplite_usb_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(tripplite_OBJECTS) $(tripplite_LDADD) $(LIBS) + +tripplite_usb$(EXEEXT): $(tripplite_usb_OBJECTS) $(tripplite_usb_DEPENDENCIES) $(EXTRA_tripplite_usb_DEPENDENCIES) @rm -f tripplite_usb$(EXEEXT) - $(LINK) $(tripplite_usb_OBJECTS) $(tripplite_usb_LDADD) $(LIBS) -tripplitesu$(EXEEXT): $(tripplitesu_OBJECTS) $(tripplitesu_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(tripplite_usb_OBJECTS) $(tripplite_usb_LDADD) $(LIBS) + +tripplitesu$(EXEEXT): $(tripplitesu_OBJECTS) $(tripplitesu_DEPENDENCIES) $(EXTRA_tripplitesu_DEPENDENCIES) @rm -f tripplitesu$(EXEEXT) - $(LINK) $(tripplitesu_OBJECTS) $(tripplitesu_LDADD) $(LIBS) -upscode2$(EXEEXT): $(upscode2_OBJECTS) $(upscode2_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(tripplitesu_OBJECTS) $(tripplitesu_LDADD) $(LIBS) + +upscode2$(EXEEXT): $(upscode2_OBJECTS) $(upscode2_DEPENDENCIES) $(EXTRA_upscode2_DEPENDENCIES) @rm -f upscode2$(EXEEXT) - $(LINK) $(upscode2_OBJECTS) $(upscode2_LDADD) $(LIBS) -upsdrvctl$(EXEEXT): $(upsdrvctl_OBJECTS) $(upsdrvctl_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(upscode2_OBJECTS) $(upscode2_LDADD) $(LIBS) + +upsdrvctl$(EXEEXT): $(upsdrvctl_OBJECTS) $(upsdrvctl_DEPENDENCIES) $(EXTRA_upsdrvctl_DEPENDENCIES) @rm -f upsdrvctl$(EXEEXT) - $(LINK) $(upsdrvctl_OBJECTS) $(upsdrvctl_LDADD) $(LIBS) -usbhid-ups$(EXEEXT): $(usbhid_ups_OBJECTS) $(usbhid_ups_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(upsdrvctl_OBJECTS) $(upsdrvctl_LDADD) $(LIBS) + +usbhid-ups$(EXEEXT): $(usbhid_ups_OBJECTS) $(usbhid_ups_DEPENDENCIES) $(EXTRA_usbhid_ups_DEPENDENCIES) @rm -f usbhid-ups$(EXEEXT) - $(LINK) $(usbhid_ups_OBJECTS) $(usbhid_ups_LDADD) $(LIBS) -victronups$(EXEEXT): $(victronups_OBJECTS) $(victronups_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(usbhid_ups_OBJECTS) $(usbhid_ups_LDADD) $(LIBS) + +victronups$(EXEEXT): $(victronups_OBJECTS) $(victronups_DEPENDENCIES) $(EXTRA_victronups_DEPENDENCIES) @rm -f victronups$(EXEEXT) - $(LINK) $(victronups_OBJECTS) $(victronups_LDADD) $(LIBS) + $(AM_V_CCLD)$(LINK) $(victronups_OBJECTS) $(victronups_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -1083,200 +1660,923 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apc-hid.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apc-mib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apcsmart-old.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apcsmart.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apcsmart_tabs.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/baytech-mib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bcmxcp.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bcmxcp_ser.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bcmxcp_usb.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/belkin-hid.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/belkin.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/belkinunv.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestfcom.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestfortress.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestpower-mib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestuferrups.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestups.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blazer.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blazer_ser.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blazer_usb.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clone-outlet.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clone.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compaq-mib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cps-hid.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cyberpower-mib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dstate-hal.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dstate.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dummy_ups-dummy-ups.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/eaton-mib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/etapro.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/everups.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/explore-hid.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gamatronic.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/genericups.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hidparser.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idowell-hid.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ietf-mib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isbmex.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ivtscd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhid.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libusb.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liebert-esp2.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liebert-hid.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liebert.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main-hal.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/masterguard.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metasys.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-hid.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-mib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-shut.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-utalk.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-xml.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/microdowell.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netvision-mib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netxml-ups.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newmge_shut-hidparser.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newmge_shut-libhid.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newmge_shut-libshut.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newmge_shut-mge-hid.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/newmge_shut-usbhid-ups.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nut-ipmipsu.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nut-libfreeipmi.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oneac.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/optiups.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powercom-hid.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powercom.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerman-pdu.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerp-bin.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerp-txt.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerpanel.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerware-mib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/raritan-pdu-mib.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rhino.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/richcomm_usb.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safenet.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/serial.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/skel.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp-ups.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/solis.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tripplite-hid.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tripplite.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tripplite_usb.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tripplitesu.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upscode2.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsdrvctl.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/usb-common.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/usbhid-ups.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/victronups.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/adelsystem_cbi.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/al175.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apc-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apcsmart-old.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apcsmart.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apcsmart_tabs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apcupsd_ups-apcupsd-ups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/arduino-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/asem.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bcmxcp.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bcmxcp_ser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bcmxcp_usb.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/belkin-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/belkin.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/belkinunv.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestfcom.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestfortress.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestuferrups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bestups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blazer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blazer_ser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/blazer_usb.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clone-outlet.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/clone.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cps-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delta_ups-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dstate.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dummy_ups-dummy-ups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/etapro.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ever-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/everups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/explore-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gamatronic.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/generic_modbus.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/genericups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hidparser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/huawei-ups2000.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idowell-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/isbmex.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ivtscd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/legrand-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libhid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libusb0.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libusb1.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liebert-esp2.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liebert-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liebert.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/macosx-ups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/masterguard.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/metasys.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-utalk.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge-xml.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge_shut-hidparser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge_shut-libhid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge_shut-libshut.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge_shut-mge-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mge_shut-usbhid-ups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/microdowell.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/microsol-apc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/microsol-common.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netxml-ups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nut-ipmipsu.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nut-libfreeipmi.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_atcl_usb.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-libusb0.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-libusb1.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_ablerex.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_hunnox.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_masterguard.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_qx-usb-common.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutdrv_siemens_sitop.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oneac.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openups-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/optiups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/phoenixcontact_modbus.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pijuice.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powercom-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powercom.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerman-pdu.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerp-bin.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerp-txt.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powerpanel.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/powervar-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rhino.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/richcomm_usb.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/riello.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/riello_ser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/riello_usb.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/safenet.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/salicru-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/serial.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/skel.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-apc-ats-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-apc-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-apc-pdu-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-baytech-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-bestpower-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-compaq-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-cyberpower-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-delta_ups-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-eaton-ats16-nm2-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-eaton-ats16-nmc-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-eaton-ats30-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-eaton-pdu-genesis2-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-eaton-pdu-marlin-helpers.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-eaton-pdu-marlin-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-eaton-pdu-pulizzi-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-eaton-pdu-revelation-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-emerson-avocent-pdu-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-hpe-pdu-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-huawei-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-ietf-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-mge-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-netvision-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-powerware-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-raritan-pdu-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-raritan-px2-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-snmp-ups-helpers.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-snmp-ups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/snmp_ups-xppc-mib.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/socomec_jbus.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/solis.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tripplite-hid.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tripplite.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tripplite_usb.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tripplitesu.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upscode2.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsdrvctl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/usb-common.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/usbhid-ups.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/victronups.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) .c.o: -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +apcupsd_ups-apcupsd-ups.o: apcupsd-ups.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(apcupsd_ups_CFLAGS) $(CFLAGS) -MT apcupsd_ups-apcupsd-ups.o -MD -MP -MF $(DEPDIR)/apcupsd_ups-apcupsd-ups.Tpo -c -o apcupsd_ups-apcupsd-ups.o `test -f 'apcupsd-ups.c' || echo '$(srcdir)/'`apcupsd-ups.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/apcupsd_ups-apcupsd-ups.Tpo $(DEPDIR)/apcupsd_ups-apcupsd-ups.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apcupsd-ups.c' object='apcupsd_ups-apcupsd-ups.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(apcupsd_ups_CFLAGS) $(CFLAGS) -c -o apcupsd_ups-apcupsd-ups.o `test -f 'apcupsd-ups.c' || echo '$(srcdir)/'`apcupsd-ups.c + +apcupsd_ups-apcupsd-ups.obj: apcupsd-ups.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(apcupsd_ups_CFLAGS) $(CFLAGS) -MT apcupsd_ups-apcupsd-ups.obj -MD -MP -MF $(DEPDIR)/apcupsd_ups-apcupsd-ups.Tpo -c -o apcupsd_ups-apcupsd-ups.obj `if test -f 'apcupsd-ups.c'; then $(CYGPATH_W) 'apcupsd-ups.c'; else $(CYGPATH_W) '$(srcdir)/apcupsd-ups.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/apcupsd_ups-apcupsd-ups.Tpo $(DEPDIR)/apcupsd_ups-apcupsd-ups.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apcupsd-ups.c' object='apcupsd_ups-apcupsd-ups.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(apcupsd_ups_CFLAGS) $(CFLAGS) -c -o apcupsd_ups-apcupsd-ups.obj `if test -f 'apcupsd-ups.c'; then $(CYGPATH_W) 'apcupsd-ups.c'; else $(CYGPATH_W) '$(srcdir)/apcupsd-ups.c'; fi` dummy_ups-dummy-ups.o: dummy-ups.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dummy_ups_CFLAGS) $(CFLAGS) -MT dummy_ups-dummy-ups.o -MD -MP -MF $(DEPDIR)/dummy_ups-dummy-ups.Tpo -c -o dummy_ups-dummy-ups.o `test -f 'dummy-ups.c' || echo '$(srcdir)/'`dummy-ups.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dummy_ups-dummy-ups.Tpo $(DEPDIR)/dummy_ups-dummy-ups.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dummy-ups.c' object='dummy_ups-dummy-ups.o' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dummy_ups_CFLAGS) $(CFLAGS) -MT dummy_ups-dummy-ups.o -MD -MP -MF $(DEPDIR)/dummy_ups-dummy-ups.Tpo -c -o dummy_ups-dummy-ups.o `test -f 'dummy-ups.c' || echo '$(srcdir)/'`dummy-ups.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dummy_ups-dummy-ups.Tpo $(DEPDIR)/dummy_ups-dummy-ups.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dummy-ups.c' object='dummy_ups-dummy-ups.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dummy_ups_CFLAGS) $(CFLAGS) -c -o dummy_ups-dummy-ups.o `test -f 'dummy-ups.c' || echo '$(srcdir)/'`dummy-ups.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dummy_ups_CFLAGS) $(CFLAGS) -c -o dummy_ups-dummy-ups.o `test -f 'dummy-ups.c' || echo '$(srcdir)/'`dummy-ups.c dummy_ups-dummy-ups.obj: dummy-ups.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dummy_ups_CFLAGS) $(CFLAGS) -MT dummy_ups-dummy-ups.obj -MD -MP -MF $(DEPDIR)/dummy_ups-dummy-ups.Tpo -c -o dummy_ups-dummy-ups.obj `if test -f 'dummy-ups.c'; then $(CYGPATH_W) 'dummy-ups.c'; else $(CYGPATH_W) '$(srcdir)/dummy-ups.c'; fi` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/dummy_ups-dummy-ups.Tpo $(DEPDIR)/dummy_ups-dummy-ups.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='dummy-ups.c' object='dummy_ups-dummy-ups.obj' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dummy_ups_CFLAGS) $(CFLAGS) -MT dummy_ups-dummy-ups.obj -MD -MP -MF $(DEPDIR)/dummy_ups-dummy-ups.Tpo -c -o dummy_ups-dummy-ups.obj `if test -f 'dummy-ups.c'; then $(CYGPATH_W) 'dummy-ups.c'; else $(CYGPATH_W) '$(srcdir)/dummy-ups.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/dummy_ups-dummy-ups.Tpo $(DEPDIR)/dummy_ups-dummy-ups.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='dummy-ups.c' object='dummy_ups-dummy-ups.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dummy_ups_CFLAGS) $(CFLAGS) -c -o dummy_ups-dummy-ups.obj `if test -f 'dummy-ups.c'; then $(CYGPATH_W) 'dummy-ups.c'; else $(CYGPATH_W) '$(srcdir)/dummy-ups.c'; fi` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(dummy_ups_CFLAGS) $(CFLAGS) -c -o dummy_ups-dummy-ups.obj `if test -f 'dummy-ups.c'; then $(CYGPATH_W) 'dummy-ups.c'; else $(CYGPATH_W) '$(srcdir)/dummy-ups.c'; fi` -newmge_shut-usbhid-ups.o: usbhid-ups.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -MT newmge_shut-usbhid-ups.o -MD -MP -MF $(DEPDIR)/newmge_shut-usbhid-ups.Tpo -c -o newmge_shut-usbhid-ups.o `test -f 'usbhid-ups.c' || echo '$(srcdir)/'`usbhid-ups.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/newmge_shut-usbhid-ups.Tpo $(DEPDIR)/newmge_shut-usbhid-ups.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='usbhid-ups.c' object='newmge_shut-usbhid-ups.o' libtool=no @AMDEPBACKSLASH@ +mge_shut-usbhid-ups.o: usbhid-ups.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-usbhid-ups.o -MD -MP -MF $(DEPDIR)/mge_shut-usbhid-ups.Tpo -c -o mge_shut-usbhid-ups.o `test -f 'usbhid-ups.c' || echo '$(srcdir)/'`usbhid-ups.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-usbhid-ups.Tpo $(DEPDIR)/mge_shut-usbhid-ups.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='usbhid-ups.c' object='mge_shut-usbhid-ups.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -c -o newmge_shut-usbhid-ups.o `test -f 'usbhid-ups.c' || echo '$(srcdir)/'`usbhid-ups.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-usbhid-ups.o `test -f 'usbhid-ups.c' || echo '$(srcdir)/'`usbhid-ups.c -newmge_shut-usbhid-ups.obj: usbhid-ups.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -MT newmge_shut-usbhid-ups.obj -MD -MP -MF $(DEPDIR)/newmge_shut-usbhid-ups.Tpo -c -o newmge_shut-usbhid-ups.obj `if test -f 'usbhid-ups.c'; then $(CYGPATH_W) 'usbhid-ups.c'; else $(CYGPATH_W) '$(srcdir)/usbhid-ups.c'; fi` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/newmge_shut-usbhid-ups.Tpo $(DEPDIR)/newmge_shut-usbhid-ups.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='usbhid-ups.c' object='newmge_shut-usbhid-ups.obj' libtool=no @AMDEPBACKSLASH@ +mge_shut-usbhid-ups.obj: usbhid-ups.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-usbhid-ups.obj -MD -MP -MF $(DEPDIR)/mge_shut-usbhid-ups.Tpo -c -o mge_shut-usbhid-ups.obj `if test -f 'usbhid-ups.c'; then $(CYGPATH_W) 'usbhid-ups.c'; else $(CYGPATH_W) '$(srcdir)/usbhid-ups.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-usbhid-ups.Tpo $(DEPDIR)/mge_shut-usbhid-ups.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='usbhid-ups.c' object='mge_shut-usbhid-ups.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -c -o newmge_shut-usbhid-ups.obj `if test -f 'usbhid-ups.c'; then $(CYGPATH_W) 'usbhid-ups.c'; else $(CYGPATH_W) '$(srcdir)/usbhid-ups.c'; fi` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-usbhid-ups.obj `if test -f 'usbhid-ups.c'; then $(CYGPATH_W) 'usbhid-ups.c'; else $(CYGPATH_W) '$(srcdir)/usbhid-ups.c'; fi` -newmge_shut-libshut.o: libshut.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -MT newmge_shut-libshut.o -MD -MP -MF $(DEPDIR)/newmge_shut-libshut.Tpo -c -o newmge_shut-libshut.o `test -f 'libshut.c' || echo '$(srcdir)/'`libshut.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/newmge_shut-libshut.Tpo $(DEPDIR)/newmge_shut-libshut.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libshut.c' object='newmge_shut-libshut.o' libtool=no @AMDEPBACKSLASH@ +mge_shut-libshut.o: libshut.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-libshut.o -MD -MP -MF $(DEPDIR)/mge_shut-libshut.Tpo -c -o mge_shut-libshut.o `test -f 'libshut.c' || echo '$(srcdir)/'`libshut.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-libshut.Tpo $(DEPDIR)/mge_shut-libshut.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libshut.c' object='mge_shut-libshut.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -c -o newmge_shut-libshut.o `test -f 'libshut.c' || echo '$(srcdir)/'`libshut.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-libshut.o `test -f 'libshut.c' || echo '$(srcdir)/'`libshut.c -newmge_shut-libshut.obj: libshut.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -MT newmge_shut-libshut.obj -MD -MP -MF $(DEPDIR)/newmge_shut-libshut.Tpo -c -o newmge_shut-libshut.obj `if test -f 'libshut.c'; then $(CYGPATH_W) 'libshut.c'; else $(CYGPATH_W) '$(srcdir)/libshut.c'; fi` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/newmge_shut-libshut.Tpo $(DEPDIR)/newmge_shut-libshut.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libshut.c' object='newmge_shut-libshut.obj' libtool=no @AMDEPBACKSLASH@ +mge_shut-libshut.obj: libshut.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-libshut.obj -MD -MP -MF $(DEPDIR)/mge_shut-libshut.Tpo -c -o mge_shut-libshut.obj `if test -f 'libshut.c'; then $(CYGPATH_W) 'libshut.c'; else $(CYGPATH_W) '$(srcdir)/libshut.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-libshut.Tpo $(DEPDIR)/mge_shut-libshut.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libshut.c' object='mge_shut-libshut.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -c -o newmge_shut-libshut.obj `if test -f 'libshut.c'; then $(CYGPATH_W) 'libshut.c'; else $(CYGPATH_W) '$(srcdir)/libshut.c'; fi` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-libshut.obj `if test -f 'libshut.c'; then $(CYGPATH_W) 'libshut.c'; else $(CYGPATH_W) '$(srcdir)/libshut.c'; fi` -newmge_shut-libhid.o: libhid.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -MT newmge_shut-libhid.o -MD -MP -MF $(DEPDIR)/newmge_shut-libhid.Tpo -c -o newmge_shut-libhid.o `test -f 'libhid.c' || echo '$(srcdir)/'`libhid.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/newmge_shut-libhid.Tpo $(DEPDIR)/newmge_shut-libhid.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libhid.c' object='newmge_shut-libhid.o' libtool=no @AMDEPBACKSLASH@ +mge_shut-libhid.o: libhid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-libhid.o -MD -MP -MF $(DEPDIR)/mge_shut-libhid.Tpo -c -o mge_shut-libhid.o `test -f 'libhid.c' || echo '$(srcdir)/'`libhid.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-libhid.Tpo $(DEPDIR)/mge_shut-libhid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libhid.c' object='mge_shut-libhid.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -c -o newmge_shut-libhid.o `test -f 'libhid.c' || echo '$(srcdir)/'`libhid.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-libhid.o `test -f 'libhid.c' || echo '$(srcdir)/'`libhid.c -newmge_shut-libhid.obj: libhid.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -MT newmge_shut-libhid.obj -MD -MP -MF $(DEPDIR)/newmge_shut-libhid.Tpo -c -o newmge_shut-libhid.obj `if test -f 'libhid.c'; then $(CYGPATH_W) 'libhid.c'; else $(CYGPATH_W) '$(srcdir)/libhid.c'; fi` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/newmge_shut-libhid.Tpo $(DEPDIR)/newmge_shut-libhid.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='libhid.c' object='newmge_shut-libhid.obj' libtool=no @AMDEPBACKSLASH@ +mge_shut-libhid.obj: libhid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-libhid.obj -MD -MP -MF $(DEPDIR)/mge_shut-libhid.Tpo -c -o mge_shut-libhid.obj `if test -f 'libhid.c'; then $(CYGPATH_W) 'libhid.c'; else $(CYGPATH_W) '$(srcdir)/libhid.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-libhid.Tpo $(DEPDIR)/mge_shut-libhid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libhid.c' object='mge_shut-libhid.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -c -o newmge_shut-libhid.obj `if test -f 'libhid.c'; then $(CYGPATH_W) 'libhid.c'; else $(CYGPATH_W) '$(srcdir)/libhid.c'; fi` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-libhid.obj `if test -f 'libhid.c'; then $(CYGPATH_W) 'libhid.c'; else $(CYGPATH_W) '$(srcdir)/libhid.c'; fi` -newmge_shut-hidparser.o: hidparser.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -MT newmge_shut-hidparser.o -MD -MP -MF $(DEPDIR)/newmge_shut-hidparser.Tpo -c -o newmge_shut-hidparser.o `test -f 'hidparser.c' || echo '$(srcdir)/'`hidparser.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/newmge_shut-hidparser.Tpo $(DEPDIR)/newmge_shut-hidparser.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hidparser.c' object='newmge_shut-hidparser.o' libtool=no @AMDEPBACKSLASH@ +mge_shut-hidparser.o: hidparser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-hidparser.o -MD -MP -MF $(DEPDIR)/mge_shut-hidparser.Tpo -c -o mge_shut-hidparser.o `test -f 'hidparser.c' || echo '$(srcdir)/'`hidparser.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-hidparser.Tpo $(DEPDIR)/mge_shut-hidparser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hidparser.c' object='mge_shut-hidparser.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -c -o newmge_shut-hidparser.o `test -f 'hidparser.c' || echo '$(srcdir)/'`hidparser.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-hidparser.o `test -f 'hidparser.c' || echo '$(srcdir)/'`hidparser.c -newmge_shut-hidparser.obj: hidparser.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -MT newmge_shut-hidparser.obj -MD -MP -MF $(DEPDIR)/newmge_shut-hidparser.Tpo -c -o newmge_shut-hidparser.obj `if test -f 'hidparser.c'; then $(CYGPATH_W) 'hidparser.c'; else $(CYGPATH_W) '$(srcdir)/hidparser.c'; fi` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/newmge_shut-hidparser.Tpo $(DEPDIR)/newmge_shut-hidparser.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='hidparser.c' object='newmge_shut-hidparser.obj' libtool=no @AMDEPBACKSLASH@ +mge_shut-hidparser.obj: hidparser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-hidparser.obj -MD -MP -MF $(DEPDIR)/mge_shut-hidparser.Tpo -c -o mge_shut-hidparser.obj `if test -f 'hidparser.c'; then $(CYGPATH_W) 'hidparser.c'; else $(CYGPATH_W) '$(srcdir)/hidparser.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-hidparser.Tpo $(DEPDIR)/mge_shut-hidparser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hidparser.c' object='mge_shut-hidparser.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -c -o newmge_shut-hidparser.obj `if test -f 'hidparser.c'; then $(CYGPATH_W) 'hidparser.c'; else $(CYGPATH_W) '$(srcdir)/hidparser.c'; fi` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-hidparser.obj `if test -f 'hidparser.c'; then $(CYGPATH_W) 'hidparser.c'; else $(CYGPATH_W) '$(srcdir)/hidparser.c'; fi` -newmge_shut-mge-hid.o: mge-hid.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -MT newmge_shut-mge-hid.o -MD -MP -MF $(DEPDIR)/newmge_shut-mge-hid.Tpo -c -o newmge_shut-mge-hid.o `test -f 'mge-hid.c' || echo '$(srcdir)/'`mge-hid.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/newmge_shut-mge-hid.Tpo $(DEPDIR)/newmge_shut-mge-hid.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mge-hid.c' object='newmge_shut-mge-hid.o' libtool=no @AMDEPBACKSLASH@ +mge_shut-mge-hid.o: mge-hid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-mge-hid.o -MD -MP -MF $(DEPDIR)/mge_shut-mge-hid.Tpo -c -o mge_shut-mge-hid.o `test -f 'mge-hid.c' || echo '$(srcdir)/'`mge-hid.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-mge-hid.Tpo $(DEPDIR)/mge_shut-mge-hid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mge-hid.c' object='mge_shut-mge-hid.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -c -o newmge_shut-mge-hid.o `test -f 'mge-hid.c' || echo '$(srcdir)/'`mge-hid.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-mge-hid.o `test -f 'mge-hid.c' || echo '$(srcdir)/'`mge-hid.c -newmge_shut-mge-hid.obj: mge-hid.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -MT newmge_shut-mge-hid.obj -MD -MP -MF $(DEPDIR)/newmge_shut-mge-hid.Tpo -c -o newmge_shut-mge-hid.obj `if test -f 'mge-hid.c'; then $(CYGPATH_W) 'mge-hid.c'; else $(CYGPATH_W) '$(srcdir)/mge-hid.c'; fi` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/newmge_shut-mge-hid.Tpo $(DEPDIR)/newmge_shut-mge-hid.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mge-hid.c' object='newmge_shut-mge-hid.obj' libtool=no @AMDEPBACKSLASH@ +mge_shut-mge-hid.obj: mge-hid.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -MT mge_shut-mge-hid.obj -MD -MP -MF $(DEPDIR)/mge_shut-mge-hid.Tpo -c -o mge_shut-mge-hid.obj `if test -f 'mge-hid.c'; then $(CYGPATH_W) 'mge-hid.c'; else $(CYGPATH_W) '$(srcdir)/mge-hid.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/mge_shut-mge-hid.Tpo $(DEPDIR)/mge_shut-mge-hid.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mge-hid.c' object='mge_shut-mge-hid.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(newmge_shut_CFLAGS) $(CFLAGS) -c -o newmge_shut-mge-hid.obj `if test -f 'mge-hid.c'; then $(CYGPATH_W) 'mge-hid.c'; else $(CYGPATH_W) '$(srcdir)/mge-hid.c'; fi` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(mge_shut_CFLAGS) $(CFLAGS) -c -o mge_shut-mge-hid.obj `if test -f 'mge-hid.c'; then $(CYGPATH_W) 'mge-hid.c'; else $(CYGPATH_W) '$(srcdir)/mge-hid.c'; fi` + +nutdrv_qx-nutdrv_qx.o: nutdrv_qx.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx.Tpo -c -o nutdrv_qx-nutdrv_qx.o `test -f 'nutdrv_qx.c' || echo '$(srcdir)/'`nutdrv_qx.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx.c' object='nutdrv_qx-nutdrv_qx.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx.o `test -f 'nutdrv_qx.c' || echo '$(srcdir)/'`nutdrv_qx.c + +nutdrv_qx-nutdrv_qx.obj: nutdrv_qx.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx.Tpo -c -o nutdrv_qx-nutdrv_qx.obj `if test -f 'nutdrv_qx.c'; then $(CYGPATH_W) 'nutdrv_qx.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx.c' object='nutdrv_qx-nutdrv_qx.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx.obj `if test -f 'nutdrv_qx.c'; then $(CYGPATH_W) 'nutdrv_qx.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx.c'; fi` + +nutdrv_qx-libusb0.o: libusb0.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-libusb0.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-libusb0.Tpo -c -o nutdrv_qx-libusb0.o `test -f 'libusb0.c' || echo '$(srcdir)/'`libusb0.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-libusb0.Tpo $(DEPDIR)/nutdrv_qx-libusb0.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libusb0.c' object='nutdrv_qx-libusb0.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-libusb0.o `test -f 'libusb0.c' || echo '$(srcdir)/'`libusb0.c + +nutdrv_qx-libusb0.obj: libusb0.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-libusb0.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-libusb0.Tpo -c -o nutdrv_qx-libusb0.obj `if test -f 'libusb0.c'; then $(CYGPATH_W) 'libusb0.c'; else $(CYGPATH_W) '$(srcdir)/libusb0.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-libusb0.Tpo $(DEPDIR)/nutdrv_qx-libusb0.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libusb0.c' object='nutdrv_qx-libusb0.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-libusb0.obj `if test -f 'libusb0.c'; then $(CYGPATH_W) 'libusb0.c'; else $(CYGPATH_W) '$(srcdir)/libusb0.c'; fi` + +nutdrv_qx-libusb1.o: libusb1.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-libusb1.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-libusb1.Tpo -c -o nutdrv_qx-libusb1.o `test -f 'libusb1.c' || echo '$(srcdir)/'`libusb1.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-libusb1.Tpo $(DEPDIR)/nutdrv_qx-libusb1.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libusb1.c' object='nutdrv_qx-libusb1.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-libusb1.o `test -f 'libusb1.c' || echo '$(srcdir)/'`libusb1.c + +nutdrv_qx-libusb1.obj: libusb1.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-libusb1.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-libusb1.Tpo -c -o nutdrv_qx-libusb1.obj `if test -f 'libusb1.c'; then $(CYGPATH_W) 'libusb1.c'; else $(CYGPATH_W) '$(srcdir)/libusb1.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-libusb1.Tpo $(DEPDIR)/nutdrv_qx-libusb1.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='libusb1.c' object='nutdrv_qx-libusb1.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-libusb1.obj `if test -f 'libusb1.c'; then $(CYGPATH_W) 'libusb1.c'; else $(CYGPATH_W) '$(srcdir)/libusb1.c'; fi` + +nutdrv_qx-usb-common.o: usb-common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-usb-common.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-usb-common.Tpo -c -o nutdrv_qx-usb-common.o `test -f 'usb-common.c' || echo '$(srcdir)/'`usb-common.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-usb-common.Tpo $(DEPDIR)/nutdrv_qx-usb-common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='usb-common.c' object='nutdrv_qx-usb-common.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-usb-common.o `test -f 'usb-common.c' || echo '$(srcdir)/'`usb-common.c + +nutdrv_qx-usb-common.obj: usb-common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-usb-common.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-usb-common.Tpo -c -o nutdrv_qx-usb-common.obj `if test -f 'usb-common.c'; then $(CYGPATH_W) 'usb-common.c'; else $(CYGPATH_W) '$(srcdir)/usb-common.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-usb-common.Tpo $(DEPDIR)/nutdrv_qx-usb-common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='usb-common.c' object='nutdrv_qx-usb-common.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-usb-common.obj `if test -f 'usb-common.c'; then $(CYGPATH_W) 'usb-common.c'; else $(CYGPATH_W) '$(srcdir)/usb-common.c'; fi` + +nutdrv_qx-nutdrv_qx_bestups.o: nutdrv_qx_bestups.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_bestups.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Tpo -c -o nutdrv_qx-nutdrv_qx_bestups.o `test -f 'nutdrv_qx_bestups.c' || echo '$(srcdir)/'`nutdrv_qx_bestups.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_bestups.c' object='nutdrv_qx-nutdrv_qx_bestups.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_bestups.o `test -f 'nutdrv_qx_bestups.c' || echo '$(srcdir)/'`nutdrv_qx_bestups.c + +nutdrv_qx-nutdrv_qx_bestups.obj: nutdrv_qx_bestups.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_bestups.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Tpo -c -o nutdrv_qx-nutdrv_qx_bestups.obj `if test -f 'nutdrv_qx_bestups.c'; then $(CYGPATH_W) 'nutdrv_qx_bestups.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_bestups.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_bestups.c' object='nutdrv_qx-nutdrv_qx_bestups.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_bestups.obj `if test -f 'nutdrv_qx_bestups.c'; then $(CYGPATH_W) 'nutdrv_qx_bestups.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_bestups.c'; fi` + +nutdrv_qx-nutdrv_qx_blazer-common.o: nutdrv_qx_blazer-common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_blazer-common.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Tpo -c -o nutdrv_qx-nutdrv_qx_blazer-common.o `test -f 'nutdrv_qx_blazer-common.c' || echo '$(srcdir)/'`nutdrv_qx_blazer-common.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_blazer-common.c' object='nutdrv_qx-nutdrv_qx_blazer-common.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_blazer-common.o `test -f 'nutdrv_qx_blazer-common.c' || echo '$(srcdir)/'`nutdrv_qx_blazer-common.c + +nutdrv_qx-nutdrv_qx_blazer-common.obj: nutdrv_qx_blazer-common.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_blazer-common.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Tpo -c -o nutdrv_qx-nutdrv_qx_blazer-common.obj `if test -f 'nutdrv_qx_blazer-common.c'; then $(CYGPATH_W) 'nutdrv_qx_blazer-common.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_blazer-common.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_blazer-common.c' object='nutdrv_qx-nutdrv_qx_blazer-common.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_blazer-common.obj `if test -f 'nutdrv_qx_blazer-common.c'; then $(CYGPATH_W) 'nutdrv_qx_blazer-common.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_blazer-common.c'; fi` + +nutdrv_qx-nutdrv_qx_masterguard.o: nutdrv_qx_masterguard.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_masterguard.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_masterguard.Tpo -c -o nutdrv_qx-nutdrv_qx_masterguard.o `test -f 'nutdrv_qx_masterguard.c' || echo '$(srcdir)/'`nutdrv_qx_masterguard.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_masterguard.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_masterguard.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_masterguard.c' object='nutdrv_qx-nutdrv_qx_masterguard.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_masterguard.o `test -f 'nutdrv_qx_masterguard.c' || echo '$(srcdir)/'`nutdrv_qx_masterguard.c + +nutdrv_qx-nutdrv_qx_masterguard.obj: nutdrv_qx_masterguard.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_masterguard.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_masterguard.Tpo -c -o nutdrv_qx-nutdrv_qx_masterguard.obj `if test -f 'nutdrv_qx_masterguard.c'; then $(CYGPATH_W) 'nutdrv_qx_masterguard.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_masterguard.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_masterguard.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_masterguard.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_masterguard.c' object='nutdrv_qx-nutdrv_qx_masterguard.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_masterguard.obj `if test -f 'nutdrv_qx_masterguard.c'; then $(CYGPATH_W) 'nutdrv_qx_masterguard.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_masterguard.c'; fi` + +nutdrv_qx-nutdrv_qx_mecer.o: nutdrv_qx_mecer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_mecer.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Tpo -c -o nutdrv_qx-nutdrv_qx_mecer.o `test -f 'nutdrv_qx_mecer.c' || echo '$(srcdir)/'`nutdrv_qx_mecer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_mecer.c' object='nutdrv_qx-nutdrv_qx_mecer.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_mecer.o `test -f 'nutdrv_qx_mecer.c' || echo '$(srcdir)/'`nutdrv_qx_mecer.c + +nutdrv_qx-nutdrv_qx_mecer.obj: nutdrv_qx_mecer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_mecer.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Tpo -c -o nutdrv_qx-nutdrv_qx_mecer.obj `if test -f 'nutdrv_qx_mecer.c'; then $(CYGPATH_W) 'nutdrv_qx_mecer.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_mecer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_mecer.c' object='nutdrv_qx-nutdrv_qx_mecer.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_mecer.obj `if test -f 'nutdrv_qx_mecer.c'; then $(CYGPATH_W) 'nutdrv_qx_mecer.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_mecer.c'; fi` + +nutdrv_qx-nutdrv_qx_megatec.o: nutdrv_qx_megatec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_megatec.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Tpo -c -o nutdrv_qx-nutdrv_qx_megatec.o `test -f 'nutdrv_qx_megatec.c' || echo '$(srcdir)/'`nutdrv_qx_megatec.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_megatec.c' object='nutdrv_qx-nutdrv_qx_megatec.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_megatec.o `test -f 'nutdrv_qx_megatec.c' || echo '$(srcdir)/'`nutdrv_qx_megatec.c + +nutdrv_qx-nutdrv_qx_megatec.obj: nutdrv_qx_megatec.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_megatec.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Tpo -c -o nutdrv_qx-nutdrv_qx_megatec.obj `if test -f 'nutdrv_qx_megatec.c'; then $(CYGPATH_W) 'nutdrv_qx_megatec.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_megatec.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_megatec.c' object='nutdrv_qx-nutdrv_qx_megatec.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_megatec.obj `if test -f 'nutdrv_qx_megatec.c'; then $(CYGPATH_W) 'nutdrv_qx_megatec.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_megatec.c'; fi` + +nutdrv_qx-nutdrv_qx_megatec-old.o: nutdrv_qx_megatec-old.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_megatec-old.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Tpo -c -o nutdrv_qx-nutdrv_qx_megatec-old.o `test -f 'nutdrv_qx_megatec-old.c' || echo '$(srcdir)/'`nutdrv_qx_megatec-old.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_megatec-old.c' object='nutdrv_qx-nutdrv_qx_megatec-old.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_megatec-old.o `test -f 'nutdrv_qx_megatec-old.c' || echo '$(srcdir)/'`nutdrv_qx_megatec-old.c + +nutdrv_qx-nutdrv_qx_megatec-old.obj: nutdrv_qx_megatec-old.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_megatec-old.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Tpo -c -o nutdrv_qx-nutdrv_qx_megatec-old.obj `if test -f 'nutdrv_qx_megatec-old.c'; then $(CYGPATH_W) 'nutdrv_qx_megatec-old.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_megatec-old.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_megatec-old.c' object='nutdrv_qx-nutdrv_qx_megatec-old.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_megatec-old.obj `if test -f 'nutdrv_qx_megatec-old.c'; then $(CYGPATH_W) 'nutdrv_qx_megatec-old.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_megatec-old.c'; fi` + +nutdrv_qx-nutdrv_qx_mustek.o: nutdrv_qx_mustek.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_mustek.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Tpo -c -o nutdrv_qx-nutdrv_qx_mustek.o `test -f 'nutdrv_qx_mustek.c' || echo '$(srcdir)/'`nutdrv_qx_mustek.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_mustek.c' object='nutdrv_qx-nutdrv_qx_mustek.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_mustek.o `test -f 'nutdrv_qx_mustek.c' || echo '$(srcdir)/'`nutdrv_qx_mustek.c + +nutdrv_qx-nutdrv_qx_mustek.obj: nutdrv_qx_mustek.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_mustek.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Tpo -c -o nutdrv_qx-nutdrv_qx_mustek.obj `if test -f 'nutdrv_qx_mustek.c'; then $(CYGPATH_W) 'nutdrv_qx_mustek.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_mustek.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_mustek.c' object='nutdrv_qx-nutdrv_qx_mustek.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_mustek.obj `if test -f 'nutdrv_qx_mustek.c'; then $(CYGPATH_W) 'nutdrv_qx_mustek.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_mustek.c'; fi` + +nutdrv_qx-nutdrv_qx_q1.o: nutdrv_qx_q1.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_q1.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Tpo -c -o nutdrv_qx-nutdrv_qx_q1.o `test -f 'nutdrv_qx_q1.c' || echo '$(srcdir)/'`nutdrv_qx_q1.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_q1.c' object='nutdrv_qx-nutdrv_qx_q1.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_q1.o `test -f 'nutdrv_qx_q1.c' || echo '$(srcdir)/'`nutdrv_qx_q1.c + +nutdrv_qx-nutdrv_qx_q1.obj: nutdrv_qx_q1.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_q1.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Tpo -c -o nutdrv_qx-nutdrv_qx_q1.obj `if test -f 'nutdrv_qx_q1.c'; then $(CYGPATH_W) 'nutdrv_qx_q1.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_q1.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_q1.c' object='nutdrv_qx-nutdrv_qx_q1.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_q1.obj `if test -f 'nutdrv_qx_q1.c'; then $(CYGPATH_W) 'nutdrv_qx_q1.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_q1.c'; fi` + +nutdrv_qx-nutdrv_qx_voltronic.o: nutdrv_qx_voltronic.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_voltronic.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Tpo -c -o nutdrv_qx-nutdrv_qx_voltronic.o `test -f 'nutdrv_qx_voltronic.c' || echo '$(srcdir)/'`nutdrv_qx_voltronic.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_voltronic.c' object='nutdrv_qx-nutdrv_qx_voltronic.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_voltronic.o `test -f 'nutdrv_qx_voltronic.c' || echo '$(srcdir)/'`nutdrv_qx_voltronic.c + +nutdrv_qx-nutdrv_qx_voltronic.obj: nutdrv_qx_voltronic.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_voltronic.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Tpo -c -o nutdrv_qx-nutdrv_qx_voltronic.obj `if test -f 'nutdrv_qx_voltronic.c'; then $(CYGPATH_W) 'nutdrv_qx_voltronic.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_voltronic.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_voltronic.c' object='nutdrv_qx-nutdrv_qx_voltronic.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_voltronic.obj `if test -f 'nutdrv_qx_voltronic.c'; then $(CYGPATH_W) 'nutdrv_qx_voltronic.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_voltronic.c'; fi` + +nutdrv_qx-nutdrv_qx_voltronic-qs.o: nutdrv_qx_voltronic-qs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_voltronic-qs.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Tpo -c -o nutdrv_qx-nutdrv_qx_voltronic-qs.o `test -f 'nutdrv_qx_voltronic-qs.c' || echo '$(srcdir)/'`nutdrv_qx_voltronic-qs.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_voltronic-qs.c' object='nutdrv_qx-nutdrv_qx_voltronic-qs.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_voltronic-qs.o `test -f 'nutdrv_qx_voltronic-qs.c' || echo '$(srcdir)/'`nutdrv_qx_voltronic-qs.c + +nutdrv_qx-nutdrv_qx_voltronic-qs.obj: nutdrv_qx_voltronic-qs.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_voltronic-qs.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Tpo -c -o nutdrv_qx-nutdrv_qx_voltronic-qs.obj `if test -f 'nutdrv_qx_voltronic-qs.c'; then $(CYGPATH_W) 'nutdrv_qx_voltronic-qs.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_voltronic-qs.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_voltronic-qs.c' object='nutdrv_qx-nutdrv_qx_voltronic-qs.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_voltronic-qs.obj `if test -f 'nutdrv_qx_voltronic-qs.c'; then $(CYGPATH_W) 'nutdrv_qx_voltronic-qs.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_voltronic-qs.c'; fi` + +nutdrv_qx-nutdrv_qx_voltronic-qs-hex.o: nutdrv_qx_voltronic-qs-hex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_voltronic-qs-hex.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Tpo -c -o nutdrv_qx-nutdrv_qx_voltronic-qs-hex.o `test -f 'nutdrv_qx_voltronic-qs-hex.c' || echo '$(srcdir)/'`nutdrv_qx_voltronic-qs-hex.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_voltronic-qs-hex.c' object='nutdrv_qx-nutdrv_qx_voltronic-qs-hex.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_voltronic-qs-hex.o `test -f 'nutdrv_qx_voltronic-qs-hex.c' || echo '$(srcdir)/'`nutdrv_qx_voltronic-qs-hex.c + +nutdrv_qx-nutdrv_qx_voltronic-qs-hex.obj: nutdrv_qx_voltronic-qs-hex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_voltronic-qs-hex.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Tpo -c -o nutdrv_qx-nutdrv_qx_voltronic-qs-hex.obj `if test -f 'nutdrv_qx_voltronic-qs-hex.c'; then $(CYGPATH_W) 'nutdrv_qx_voltronic-qs-hex.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_voltronic-qs-hex.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_voltronic-qs-hex.c' object='nutdrv_qx-nutdrv_qx_voltronic-qs-hex.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_voltronic-qs-hex.obj `if test -f 'nutdrv_qx_voltronic-qs-hex.c'; then $(CYGPATH_W) 'nutdrv_qx_voltronic-qs-hex.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_voltronic-qs-hex.c'; fi` + +nutdrv_qx-nutdrv_qx_zinto.o: nutdrv_qx_zinto.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_zinto.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Tpo -c -o nutdrv_qx-nutdrv_qx_zinto.o `test -f 'nutdrv_qx_zinto.c' || echo '$(srcdir)/'`nutdrv_qx_zinto.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_zinto.c' object='nutdrv_qx-nutdrv_qx_zinto.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_zinto.o `test -f 'nutdrv_qx_zinto.c' || echo '$(srcdir)/'`nutdrv_qx_zinto.c + +nutdrv_qx-nutdrv_qx_zinto.obj: nutdrv_qx_zinto.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_zinto.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Tpo -c -o nutdrv_qx-nutdrv_qx_zinto.obj `if test -f 'nutdrv_qx_zinto.c'; then $(CYGPATH_W) 'nutdrv_qx_zinto.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_zinto.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_zinto.c' object='nutdrv_qx-nutdrv_qx_zinto.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_zinto.obj `if test -f 'nutdrv_qx_zinto.c'; then $(CYGPATH_W) 'nutdrv_qx_zinto.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_zinto.c'; fi` + +nutdrv_qx-nutdrv_qx_hunnox.o: nutdrv_qx_hunnox.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_hunnox.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_hunnox.Tpo -c -o nutdrv_qx-nutdrv_qx_hunnox.o `test -f 'nutdrv_qx_hunnox.c' || echo '$(srcdir)/'`nutdrv_qx_hunnox.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_hunnox.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_hunnox.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_hunnox.c' object='nutdrv_qx-nutdrv_qx_hunnox.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_hunnox.o `test -f 'nutdrv_qx_hunnox.c' || echo '$(srcdir)/'`nutdrv_qx_hunnox.c + +nutdrv_qx-nutdrv_qx_hunnox.obj: nutdrv_qx_hunnox.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_hunnox.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_hunnox.Tpo -c -o nutdrv_qx-nutdrv_qx_hunnox.obj `if test -f 'nutdrv_qx_hunnox.c'; then $(CYGPATH_W) 'nutdrv_qx_hunnox.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_hunnox.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_hunnox.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_hunnox.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_hunnox.c' object='nutdrv_qx-nutdrv_qx_hunnox.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_hunnox.obj `if test -f 'nutdrv_qx_hunnox.c'; then $(CYGPATH_W) 'nutdrv_qx_hunnox.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_hunnox.c'; fi` + +nutdrv_qx-nutdrv_qx_ablerex.o: nutdrv_qx_ablerex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_ablerex.o -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_ablerex.Tpo -c -o nutdrv_qx-nutdrv_qx_ablerex.o `test -f 'nutdrv_qx_ablerex.c' || echo '$(srcdir)/'`nutdrv_qx_ablerex.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_ablerex.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_ablerex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_ablerex.c' object='nutdrv_qx-nutdrv_qx_ablerex.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_ablerex.o `test -f 'nutdrv_qx_ablerex.c' || echo '$(srcdir)/'`nutdrv_qx_ablerex.c + +nutdrv_qx-nutdrv_qx_ablerex.obj: nutdrv_qx_ablerex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -MT nutdrv_qx-nutdrv_qx_ablerex.obj -MD -MP -MF $(DEPDIR)/nutdrv_qx-nutdrv_qx_ablerex.Tpo -c -o nutdrv_qx-nutdrv_qx_ablerex.obj `if test -f 'nutdrv_qx_ablerex.c'; then $(CYGPATH_W) 'nutdrv_qx_ablerex.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_ablerex.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nutdrv_qx-nutdrv_qx_ablerex.Tpo $(DEPDIR)/nutdrv_qx-nutdrv_qx_ablerex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutdrv_qx_ablerex.c' object='nutdrv_qx-nutdrv_qx_ablerex.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nutdrv_qx_CFLAGS) $(CFLAGS) -c -o nutdrv_qx-nutdrv_qx_ablerex.obj `if test -f 'nutdrv_qx_ablerex.c'; then $(CYGPATH_W) 'nutdrv_qx_ablerex.c'; else $(CYGPATH_W) '$(srcdir)/nutdrv_qx_ablerex.c'; fi` + +snmp_ups-snmp-ups.o: snmp-ups.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-snmp-ups.o -MD -MP -MF $(DEPDIR)/snmp_ups-snmp-ups.Tpo -c -o snmp_ups-snmp-ups.o `test -f 'snmp-ups.c' || echo '$(srcdir)/'`snmp-ups.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-snmp-ups.Tpo $(DEPDIR)/snmp_ups-snmp-ups.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='snmp-ups.c' object='snmp_ups-snmp-ups.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-snmp-ups.o `test -f 'snmp-ups.c' || echo '$(srcdir)/'`snmp-ups.c + +snmp_ups-snmp-ups.obj: snmp-ups.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-snmp-ups.obj -MD -MP -MF $(DEPDIR)/snmp_ups-snmp-ups.Tpo -c -o snmp_ups-snmp-ups.obj `if test -f 'snmp-ups.c'; then $(CYGPATH_W) 'snmp-ups.c'; else $(CYGPATH_W) '$(srcdir)/snmp-ups.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-snmp-ups.Tpo $(DEPDIR)/snmp_ups-snmp-ups.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='snmp-ups.c' object='snmp_ups-snmp-ups.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-snmp-ups.obj `if test -f 'snmp-ups.c'; then $(CYGPATH_W) 'snmp-ups.c'; else $(CYGPATH_W) '$(srcdir)/snmp-ups.c'; fi` + +snmp_ups-snmp-ups-helpers.o: snmp-ups-helpers.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-snmp-ups-helpers.o -MD -MP -MF $(DEPDIR)/snmp_ups-snmp-ups-helpers.Tpo -c -o snmp_ups-snmp-ups-helpers.o `test -f 'snmp-ups-helpers.c' || echo '$(srcdir)/'`snmp-ups-helpers.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-snmp-ups-helpers.Tpo $(DEPDIR)/snmp_ups-snmp-ups-helpers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='snmp-ups-helpers.c' object='snmp_ups-snmp-ups-helpers.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-snmp-ups-helpers.o `test -f 'snmp-ups-helpers.c' || echo '$(srcdir)/'`snmp-ups-helpers.c + +snmp_ups-snmp-ups-helpers.obj: snmp-ups-helpers.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-snmp-ups-helpers.obj -MD -MP -MF $(DEPDIR)/snmp_ups-snmp-ups-helpers.Tpo -c -o snmp_ups-snmp-ups-helpers.obj `if test -f 'snmp-ups-helpers.c'; then $(CYGPATH_W) 'snmp-ups-helpers.c'; else $(CYGPATH_W) '$(srcdir)/snmp-ups-helpers.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-snmp-ups-helpers.Tpo $(DEPDIR)/snmp_ups-snmp-ups-helpers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='snmp-ups-helpers.c' object='snmp_ups-snmp-ups-helpers.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-snmp-ups-helpers.obj `if test -f 'snmp-ups-helpers.c'; then $(CYGPATH_W) 'snmp-ups-helpers.c'; else $(CYGPATH_W) '$(srcdir)/snmp-ups-helpers.c'; fi` + +snmp_ups-apc-mib.o: apc-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-apc-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-apc-mib.Tpo -c -o snmp_ups-apc-mib.o `test -f 'apc-mib.c' || echo '$(srcdir)/'`apc-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-apc-mib.Tpo $(DEPDIR)/snmp_ups-apc-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apc-mib.c' object='snmp_ups-apc-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-apc-mib.o `test -f 'apc-mib.c' || echo '$(srcdir)/'`apc-mib.c + +snmp_ups-apc-mib.obj: apc-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-apc-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-apc-mib.Tpo -c -o snmp_ups-apc-mib.obj `if test -f 'apc-mib.c'; then $(CYGPATH_W) 'apc-mib.c'; else $(CYGPATH_W) '$(srcdir)/apc-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-apc-mib.Tpo $(DEPDIR)/snmp_ups-apc-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apc-mib.c' object='snmp_ups-apc-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-apc-mib.obj `if test -f 'apc-mib.c'; then $(CYGPATH_W) 'apc-mib.c'; else $(CYGPATH_W) '$(srcdir)/apc-mib.c'; fi` + +snmp_ups-apc-pdu-mib.o: apc-pdu-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-apc-pdu-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-apc-pdu-mib.Tpo -c -o snmp_ups-apc-pdu-mib.o `test -f 'apc-pdu-mib.c' || echo '$(srcdir)/'`apc-pdu-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-apc-pdu-mib.Tpo $(DEPDIR)/snmp_ups-apc-pdu-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apc-pdu-mib.c' object='snmp_ups-apc-pdu-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-apc-pdu-mib.o `test -f 'apc-pdu-mib.c' || echo '$(srcdir)/'`apc-pdu-mib.c + +snmp_ups-apc-pdu-mib.obj: apc-pdu-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-apc-pdu-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-apc-pdu-mib.Tpo -c -o snmp_ups-apc-pdu-mib.obj `if test -f 'apc-pdu-mib.c'; then $(CYGPATH_W) 'apc-pdu-mib.c'; else $(CYGPATH_W) '$(srcdir)/apc-pdu-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-apc-pdu-mib.Tpo $(DEPDIR)/snmp_ups-apc-pdu-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apc-pdu-mib.c' object='snmp_ups-apc-pdu-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-apc-pdu-mib.obj `if test -f 'apc-pdu-mib.c'; then $(CYGPATH_W) 'apc-pdu-mib.c'; else $(CYGPATH_W) '$(srcdir)/apc-pdu-mib.c'; fi` + +snmp_ups-baytech-mib.o: baytech-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-baytech-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-baytech-mib.Tpo -c -o snmp_ups-baytech-mib.o `test -f 'baytech-mib.c' || echo '$(srcdir)/'`baytech-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-baytech-mib.Tpo $(DEPDIR)/snmp_ups-baytech-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='baytech-mib.c' object='snmp_ups-baytech-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-baytech-mib.o `test -f 'baytech-mib.c' || echo '$(srcdir)/'`baytech-mib.c + +snmp_ups-baytech-mib.obj: baytech-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-baytech-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-baytech-mib.Tpo -c -o snmp_ups-baytech-mib.obj `if test -f 'baytech-mib.c'; then $(CYGPATH_W) 'baytech-mib.c'; else $(CYGPATH_W) '$(srcdir)/baytech-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-baytech-mib.Tpo $(DEPDIR)/snmp_ups-baytech-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='baytech-mib.c' object='snmp_ups-baytech-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-baytech-mib.obj `if test -f 'baytech-mib.c'; then $(CYGPATH_W) 'baytech-mib.c'; else $(CYGPATH_W) '$(srcdir)/baytech-mib.c'; fi` + +snmp_ups-bestpower-mib.o: bestpower-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-bestpower-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-bestpower-mib.Tpo -c -o snmp_ups-bestpower-mib.o `test -f 'bestpower-mib.c' || echo '$(srcdir)/'`bestpower-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-bestpower-mib.Tpo $(DEPDIR)/snmp_ups-bestpower-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bestpower-mib.c' object='snmp_ups-bestpower-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-bestpower-mib.o `test -f 'bestpower-mib.c' || echo '$(srcdir)/'`bestpower-mib.c + +snmp_ups-bestpower-mib.obj: bestpower-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-bestpower-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-bestpower-mib.Tpo -c -o snmp_ups-bestpower-mib.obj `if test -f 'bestpower-mib.c'; then $(CYGPATH_W) 'bestpower-mib.c'; else $(CYGPATH_W) '$(srcdir)/bestpower-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-bestpower-mib.Tpo $(DEPDIR)/snmp_ups-bestpower-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bestpower-mib.c' object='snmp_ups-bestpower-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-bestpower-mib.obj `if test -f 'bestpower-mib.c'; then $(CYGPATH_W) 'bestpower-mib.c'; else $(CYGPATH_W) '$(srcdir)/bestpower-mib.c'; fi` + +snmp_ups-compaq-mib.o: compaq-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-compaq-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-compaq-mib.Tpo -c -o snmp_ups-compaq-mib.o `test -f 'compaq-mib.c' || echo '$(srcdir)/'`compaq-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-compaq-mib.Tpo $(DEPDIR)/snmp_ups-compaq-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compaq-mib.c' object='snmp_ups-compaq-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-compaq-mib.o `test -f 'compaq-mib.c' || echo '$(srcdir)/'`compaq-mib.c + +snmp_ups-compaq-mib.obj: compaq-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-compaq-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-compaq-mib.Tpo -c -o snmp_ups-compaq-mib.obj `if test -f 'compaq-mib.c'; then $(CYGPATH_W) 'compaq-mib.c'; else $(CYGPATH_W) '$(srcdir)/compaq-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-compaq-mib.Tpo $(DEPDIR)/snmp_ups-compaq-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='compaq-mib.c' object='snmp_ups-compaq-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-compaq-mib.obj `if test -f 'compaq-mib.c'; then $(CYGPATH_W) 'compaq-mib.c'; else $(CYGPATH_W) '$(srcdir)/compaq-mib.c'; fi` + +snmp_ups-cyberpower-mib.o: cyberpower-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-cyberpower-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-cyberpower-mib.Tpo -c -o snmp_ups-cyberpower-mib.o `test -f 'cyberpower-mib.c' || echo '$(srcdir)/'`cyberpower-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-cyberpower-mib.Tpo $(DEPDIR)/snmp_ups-cyberpower-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cyberpower-mib.c' object='snmp_ups-cyberpower-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-cyberpower-mib.o `test -f 'cyberpower-mib.c' || echo '$(srcdir)/'`cyberpower-mib.c + +snmp_ups-cyberpower-mib.obj: cyberpower-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-cyberpower-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-cyberpower-mib.Tpo -c -o snmp_ups-cyberpower-mib.obj `if test -f 'cyberpower-mib.c'; then $(CYGPATH_W) 'cyberpower-mib.c'; else $(CYGPATH_W) '$(srcdir)/cyberpower-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-cyberpower-mib.Tpo $(DEPDIR)/snmp_ups-cyberpower-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='cyberpower-mib.c' object='snmp_ups-cyberpower-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-cyberpower-mib.obj `if test -f 'cyberpower-mib.c'; then $(CYGPATH_W) 'cyberpower-mib.c'; else $(CYGPATH_W) '$(srcdir)/cyberpower-mib.c'; fi` + +snmp_ups-delta_ups-mib.o: delta_ups-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-delta_ups-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-delta_ups-mib.Tpo -c -o snmp_ups-delta_ups-mib.o `test -f 'delta_ups-mib.c' || echo '$(srcdir)/'`delta_ups-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-delta_ups-mib.Tpo $(DEPDIR)/snmp_ups-delta_ups-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='delta_ups-mib.c' object='snmp_ups-delta_ups-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-delta_ups-mib.o `test -f 'delta_ups-mib.c' || echo '$(srcdir)/'`delta_ups-mib.c + +snmp_ups-delta_ups-mib.obj: delta_ups-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-delta_ups-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-delta_ups-mib.Tpo -c -o snmp_ups-delta_ups-mib.obj `if test -f 'delta_ups-mib.c'; then $(CYGPATH_W) 'delta_ups-mib.c'; else $(CYGPATH_W) '$(srcdir)/delta_ups-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-delta_ups-mib.Tpo $(DEPDIR)/snmp_ups-delta_ups-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='delta_ups-mib.c' object='snmp_ups-delta_ups-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-delta_ups-mib.obj `if test -f 'delta_ups-mib.c'; then $(CYGPATH_W) 'delta_ups-mib.c'; else $(CYGPATH_W) '$(srcdir)/delta_ups-mib.c'; fi` + +snmp_ups-eaton-pdu-genesis2-mib.o: eaton-pdu-genesis2-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-pdu-genesis2-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-pdu-genesis2-mib.Tpo -c -o snmp_ups-eaton-pdu-genesis2-mib.o `test -f 'eaton-pdu-genesis2-mib.c' || echo '$(srcdir)/'`eaton-pdu-genesis2-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-pdu-genesis2-mib.Tpo $(DEPDIR)/snmp_ups-eaton-pdu-genesis2-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-pdu-genesis2-mib.c' object='snmp_ups-eaton-pdu-genesis2-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-pdu-genesis2-mib.o `test -f 'eaton-pdu-genesis2-mib.c' || echo '$(srcdir)/'`eaton-pdu-genesis2-mib.c + +snmp_ups-eaton-pdu-genesis2-mib.obj: eaton-pdu-genesis2-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-pdu-genesis2-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-pdu-genesis2-mib.Tpo -c -o snmp_ups-eaton-pdu-genesis2-mib.obj `if test -f 'eaton-pdu-genesis2-mib.c'; then $(CYGPATH_W) 'eaton-pdu-genesis2-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-pdu-genesis2-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-pdu-genesis2-mib.Tpo $(DEPDIR)/snmp_ups-eaton-pdu-genesis2-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-pdu-genesis2-mib.c' object='snmp_ups-eaton-pdu-genesis2-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-pdu-genesis2-mib.obj `if test -f 'eaton-pdu-genesis2-mib.c'; then $(CYGPATH_W) 'eaton-pdu-genesis2-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-pdu-genesis2-mib.c'; fi` + +snmp_ups-eaton-pdu-marlin-mib.o: eaton-pdu-marlin-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-pdu-marlin-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-pdu-marlin-mib.Tpo -c -o snmp_ups-eaton-pdu-marlin-mib.o `test -f 'eaton-pdu-marlin-mib.c' || echo '$(srcdir)/'`eaton-pdu-marlin-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-pdu-marlin-mib.Tpo $(DEPDIR)/snmp_ups-eaton-pdu-marlin-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-pdu-marlin-mib.c' object='snmp_ups-eaton-pdu-marlin-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-pdu-marlin-mib.o `test -f 'eaton-pdu-marlin-mib.c' || echo '$(srcdir)/'`eaton-pdu-marlin-mib.c + +snmp_ups-eaton-pdu-marlin-mib.obj: eaton-pdu-marlin-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-pdu-marlin-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-pdu-marlin-mib.Tpo -c -o snmp_ups-eaton-pdu-marlin-mib.obj `if test -f 'eaton-pdu-marlin-mib.c'; then $(CYGPATH_W) 'eaton-pdu-marlin-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-pdu-marlin-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-pdu-marlin-mib.Tpo $(DEPDIR)/snmp_ups-eaton-pdu-marlin-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-pdu-marlin-mib.c' object='snmp_ups-eaton-pdu-marlin-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-pdu-marlin-mib.obj `if test -f 'eaton-pdu-marlin-mib.c'; then $(CYGPATH_W) 'eaton-pdu-marlin-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-pdu-marlin-mib.c'; fi` + +snmp_ups-eaton-pdu-marlin-helpers.o: eaton-pdu-marlin-helpers.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-pdu-marlin-helpers.o -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-pdu-marlin-helpers.Tpo -c -o snmp_ups-eaton-pdu-marlin-helpers.o `test -f 'eaton-pdu-marlin-helpers.c' || echo '$(srcdir)/'`eaton-pdu-marlin-helpers.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-pdu-marlin-helpers.Tpo $(DEPDIR)/snmp_ups-eaton-pdu-marlin-helpers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-pdu-marlin-helpers.c' object='snmp_ups-eaton-pdu-marlin-helpers.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-pdu-marlin-helpers.o `test -f 'eaton-pdu-marlin-helpers.c' || echo '$(srcdir)/'`eaton-pdu-marlin-helpers.c + +snmp_ups-eaton-pdu-marlin-helpers.obj: eaton-pdu-marlin-helpers.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-pdu-marlin-helpers.obj -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-pdu-marlin-helpers.Tpo -c -o snmp_ups-eaton-pdu-marlin-helpers.obj `if test -f 'eaton-pdu-marlin-helpers.c'; then $(CYGPATH_W) 'eaton-pdu-marlin-helpers.c'; else $(CYGPATH_W) '$(srcdir)/eaton-pdu-marlin-helpers.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-pdu-marlin-helpers.Tpo $(DEPDIR)/snmp_ups-eaton-pdu-marlin-helpers.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-pdu-marlin-helpers.c' object='snmp_ups-eaton-pdu-marlin-helpers.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-pdu-marlin-helpers.obj `if test -f 'eaton-pdu-marlin-helpers.c'; then $(CYGPATH_W) 'eaton-pdu-marlin-helpers.c'; else $(CYGPATH_W) '$(srcdir)/eaton-pdu-marlin-helpers.c'; fi` + +snmp_ups-eaton-pdu-pulizzi-mib.o: eaton-pdu-pulizzi-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-pdu-pulizzi-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-pdu-pulizzi-mib.Tpo -c -o snmp_ups-eaton-pdu-pulizzi-mib.o `test -f 'eaton-pdu-pulizzi-mib.c' || echo '$(srcdir)/'`eaton-pdu-pulizzi-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-pdu-pulizzi-mib.Tpo $(DEPDIR)/snmp_ups-eaton-pdu-pulizzi-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-pdu-pulizzi-mib.c' object='snmp_ups-eaton-pdu-pulizzi-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-pdu-pulizzi-mib.o `test -f 'eaton-pdu-pulizzi-mib.c' || echo '$(srcdir)/'`eaton-pdu-pulizzi-mib.c + +snmp_ups-eaton-pdu-pulizzi-mib.obj: eaton-pdu-pulizzi-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-pdu-pulizzi-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-pdu-pulizzi-mib.Tpo -c -o snmp_ups-eaton-pdu-pulizzi-mib.obj `if test -f 'eaton-pdu-pulizzi-mib.c'; then $(CYGPATH_W) 'eaton-pdu-pulizzi-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-pdu-pulizzi-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-pdu-pulizzi-mib.Tpo $(DEPDIR)/snmp_ups-eaton-pdu-pulizzi-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-pdu-pulizzi-mib.c' object='snmp_ups-eaton-pdu-pulizzi-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-pdu-pulizzi-mib.obj `if test -f 'eaton-pdu-pulizzi-mib.c'; then $(CYGPATH_W) 'eaton-pdu-pulizzi-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-pdu-pulizzi-mib.c'; fi` + +snmp_ups-eaton-pdu-revelation-mib.o: eaton-pdu-revelation-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-pdu-revelation-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-pdu-revelation-mib.Tpo -c -o snmp_ups-eaton-pdu-revelation-mib.o `test -f 'eaton-pdu-revelation-mib.c' || echo '$(srcdir)/'`eaton-pdu-revelation-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-pdu-revelation-mib.Tpo $(DEPDIR)/snmp_ups-eaton-pdu-revelation-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-pdu-revelation-mib.c' object='snmp_ups-eaton-pdu-revelation-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-pdu-revelation-mib.o `test -f 'eaton-pdu-revelation-mib.c' || echo '$(srcdir)/'`eaton-pdu-revelation-mib.c + +snmp_ups-eaton-pdu-revelation-mib.obj: eaton-pdu-revelation-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-pdu-revelation-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-pdu-revelation-mib.Tpo -c -o snmp_ups-eaton-pdu-revelation-mib.obj `if test -f 'eaton-pdu-revelation-mib.c'; then $(CYGPATH_W) 'eaton-pdu-revelation-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-pdu-revelation-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-pdu-revelation-mib.Tpo $(DEPDIR)/snmp_ups-eaton-pdu-revelation-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-pdu-revelation-mib.c' object='snmp_ups-eaton-pdu-revelation-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-pdu-revelation-mib.obj `if test -f 'eaton-pdu-revelation-mib.c'; then $(CYGPATH_W) 'eaton-pdu-revelation-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-pdu-revelation-mib.c'; fi` + +snmp_ups-eaton-ats16-nmc-mib.o: eaton-ats16-nmc-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-ats16-nmc-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-ats16-nmc-mib.Tpo -c -o snmp_ups-eaton-ats16-nmc-mib.o `test -f 'eaton-ats16-nmc-mib.c' || echo '$(srcdir)/'`eaton-ats16-nmc-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-ats16-nmc-mib.Tpo $(DEPDIR)/snmp_ups-eaton-ats16-nmc-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-ats16-nmc-mib.c' object='snmp_ups-eaton-ats16-nmc-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-ats16-nmc-mib.o `test -f 'eaton-ats16-nmc-mib.c' || echo '$(srcdir)/'`eaton-ats16-nmc-mib.c + +snmp_ups-eaton-ats16-nmc-mib.obj: eaton-ats16-nmc-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-ats16-nmc-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-ats16-nmc-mib.Tpo -c -o snmp_ups-eaton-ats16-nmc-mib.obj `if test -f 'eaton-ats16-nmc-mib.c'; then $(CYGPATH_W) 'eaton-ats16-nmc-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-ats16-nmc-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-ats16-nmc-mib.Tpo $(DEPDIR)/snmp_ups-eaton-ats16-nmc-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-ats16-nmc-mib.c' object='snmp_ups-eaton-ats16-nmc-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-ats16-nmc-mib.obj `if test -f 'eaton-ats16-nmc-mib.c'; then $(CYGPATH_W) 'eaton-ats16-nmc-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-ats16-nmc-mib.c'; fi` + +snmp_ups-eaton-ats16-nm2-mib.o: eaton-ats16-nm2-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-ats16-nm2-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-ats16-nm2-mib.Tpo -c -o snmp_ups-eaton-ats16-nm2-mib.o `test -f 'eaton-ats16-nm2-mib.c' || echo '$(srcdir)/'`eaton-ats16-nm2-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-ats16-nm2-mib.Tpo $(DEPDIR)/snmp_ups-eaton-ats16-nm2-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-ats16-nm2-mib.c' object='snmp_ups-eaton-ats16-nm2-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-ats16-nm2-mib.o `test -f 'eaton-ats16-nm2-mib.c' || echo '$(srcdir)/'`eaton-ats16-nm2-mib.c + +snmp_ups-eaton-ats16-nm2-mib.obj: eaton-ats16-nm2-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-ats16-nm2-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-ats16-nm2-mib.Tpo -c -o snmp_ups-eaton-ats16-nm2-mib.obj `if test -f 'eaton-ats16-nm2-mib.c'; then $(CYGPATH_W) 'eaton-ats16-nm2-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-ats16-nm2-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-ats16-nm2-mib.Tpo $(DEPDIR)/snmp_ups-eaton-ats16-nm2-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-ats16-nm2-mib.c' object='snmp_ups-eaton-ats16-nm2-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-ats16-nm2-mib.obj `if test -f 'eaton-ats16-nm2-mib.c'; then $(CYGPATH_W) 'eaton-ats16-nm2-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-ats16-nm2-mib.c'; fi` + +snmp_ups-apc-ats-mib.o: apc-ats-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-apc-ats-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-apc-ats-mib.Tpo -c -o snmp_ups-apc-ats-mib.o `test -f 'apc-ats-mib.c' || echo '$(srcdir)/'`apc-ats-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-apc-ats-mib.Tpo $(DEPDIR)/snmp_ups-apc-ats-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apc-ats-mib.c' object='snmp_ups-apc-ats-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-apc-ats-mib.o `test -f 'apc-ats-mib.c' || echo '$(srcdir)/'`apc-ats-mib.c + +snmp_ups-apc-ats-mib.obj: apc-ats-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-apc-ats-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-apc-ats-mib.Tpo -c -o snmp_ups-apc-ats-mib.obj `if test -f 'apc-ats-mib.c'; then $(CYGPATH_W) 'apc-ats-mib.c'; else $(CYGPATH_W) '$(srcdir)/apc-ats-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-apc-ats-mib.Tpo $(DEPDIR)/snmp_ups-apc-ats-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='apc-ats-mib.c' object='snmp_ups-apc-ats-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-apc-ats-mib.obj `if test -f 'apc-ats-mib.c'; then $(CYGPATH_W) 'apc-ats-mib.c'; else $(CYGPATH_W) '$(srcdir)/apc-ats-mib.c'; fi` + +snmp_ups-eaton-ats30-mib.o: eaton-ats30-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-ats30-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-ats30-mib.Tpo -c -o snmp_ups-eaton-ats30-mib.o `test -f 'eaton-ats30-mib.c' || echo '$(srcdir)/'`eaton-ats30-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-ats30-mib.Tpo $(DEPDIR)/snmp_ups-eaton-ats30-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-ats30-mib.c' object='snmp_ups-eaton-ats30-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-ats30-mib.o `test -f 'eaton-ats30-mib.c' || echo '$(srcdir)/'`eaton-ats30-mib.c + +snmp_ups-eaton-ats30-mib.obj: eaton-ats30-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-eaton-ats30-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-eaton-ats30-mib.Tpo -c -o snmp_ups-eaton-ats30-mib.obj `if test -f 'eaton-ats30-mib.c'; then $(CYGPATH_W) 'eaton-ats30-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-ats30-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-eaton-ats30-mib.Tpo $(DEPDIR)/snmp_ups-eaton-ats30-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='eaton-ats30-mib.c' object='snmp_ups-eaton-ats30-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-eaton-ats30-mib.obj `if test -f 'eaton-ats30-mib.c'; then $(CYGPATH_W) 'eaton-ats30-mib.c'; else $(CYGPATH_W) '$(srcdir)/eaton-ats30-mib.c'; fi` + +snmp_ups-emerson-avocent-pdu-mib.o: emerson-avocent-pdu-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-emerson-avocent-pdu-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-emerson-avocent-pdu-mib.Tpo -c -o snmp_ups-emerson-avocent-pdu-mib.o `test -f 'emerson-avocent-pdu-mib.c' || echo '$(srcdir)/'`emerson-avocent-pdu-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-emerson-avocent-pdu-mib.Tpo $(DEPDIR)/snmp_ups-emerson-avocent-pdu-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emerson-avocent-pdu-mib.c' object='snmp_ups-emerson-avocent-pdu-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-emerson-avocent-pdu-mib.o `test -f 'emerson-avocent-pdu-mib.c' || echo '$(srcdir)/'`emerson-avocent-pdu-mib.c + +snmp_ups-emerson-avocent-pdu-mib.obj: emerson-avocent-pdu-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-emerson-avocent-pdu-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-emerson-avocent-pdu-mib.Tpo -c -o snmp_ups-emerson-avocent-pdu-mib.obj `if test -f 'emerson-avocent-pdu-mib.c'; then $(CYGPATH_W) 'emerson-avocent-pdu-mib.c'; else $(CYGPATH_W) '$(srcdir)/emerson-avocent-pdu-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-emerson-avocent-pdu-mib.Tpo $(DEPDIR)/snmp_ups-emerson-avocent-pdu-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='emerson-avocent-pdu-mib.c' object='snmp_ups-emerson-avocent-pdu-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-emerson-avocent-pdu-mib.obj `if test -f 'emerson-avocent-pdu-mib.c'; then $(CYGPATH_W) 'emerson-avocent-pdu-mib.c'; else $(CYGPATH_W) '$(srcdir)/emerson-avocent-pdu-mib.c'; fi` + +snmp_ups-hpe-pdu-mib.o: hpe-pdu-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-hpe-pdu-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-hpe-pdu-mib.Tpo -c -o snmp_ups-hpe-pdu-mib.o `test -f 'hpe-pdu-mib.c' || echo '$(srcdir)/'`hpe-pdu-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-hpe-pdu-mib.Tpo $(DEPDIR)/snmp_ups-hpe-pdu-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hpe-pdu-mib.c' object='snmp_ups-hpe-pdu-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-hpe-pdu-mib.o `test -f 'hpe-pdu-mib.c' || echo '$(srcdir)/'`hpe-pdu-mib.c + +snmp_ups-hpe-pdu-mib.obj: hpe-pdu-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-hpe-pdu-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-hpe-pdu-mib.Tpo -c -o snmp_ups-hpe-pdu-mib.obj `if test -f 'hpe-pdu-mib.c'; then $(CYGPATH_W) 'hpe-pdu-mib.c'; else $(CYGPATH_W) '$(srcdir)/hpe-pdu-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-hpe-pdu-mib.Tpo $(DEPDIR)/snmp_ups-hpe-pdu-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hpe-pdu-mib.c' object='snmp_ups-hpe-pdu-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-hpe-pdu-mib.obj `if test -f 'hpe-pdu-mib.c'; then $(CYGPATH_W) 'hpe-pdu-mib.c'; else $(CYGPATH_W) '$(srcdir)/hpe-pdu-mib.c'; fi` + +snmp_ups-huawei-mib.o: huawei-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-huawei-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-huawei-mib.Tpo -c -o snmp_ups-huawei-mib.o `test -f 'huawei-mib.c' || echo '$(srcdir)/'`huawei-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-huawei-mib.Tpo $(DEPDIR)/snmp_ups-huawei-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='huawei-mib.c' object='snmp_ups-huawei-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-huawei-mib.o `test -f 'huawei-mib.c' || echo '$(srcdir)/'`huawei-mib.c + +snmp_ups-huawei-mib.obj: huawei-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-huawei-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-huawei-mib.Tpo -c -o snmp_ups-huawei-mib.obj `if test -f 'huawei-mib.c'; then $(CYGPATH_W) 'huawei-mib.c'; else $(CYGPATH_W) '$(srcdir)/huawei-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-huawei-mib.Tpo $(DEPDIR)/snmp_ups-huawei-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='huawei-mib.c' object='snmp_ups-huawei-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-huawei-mib.obj `if test -f 'huawei-mib.c'; then $(CYGPATH_W) 'huawei-mib.c'; else $(CYGPATH_W) '$(srcdir)/huawei-mib.c'; fi` + +snmp_ups-ietf-mib.o: ietf-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-ietf-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-ietf-mib.Tpo -c -o snmp_ups-ietf-mib.o `test -f 'ietf-mib.c' || echo '$(srcdir)/'`ietf-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-ietf-mib.Tpo $(DEPDIR)/snmp_ups-ietf-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ietf-mib.c' object='snmp_ups-ietf-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-ietf-mib.o `test -f 'ietf-mib.c' || echo '$(srcdir)/'`ietf-mib.c + +snmp_ups-ietf-mib.obj: ietf-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-ietf-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-ietf-mib.Tpo -c -o snmp_ups-ietf-mib.obj `if test -f 'ietf-mib.c'; then $(CYGPATH_W) 'ietf-mib.c'; else $(CYGPATH_W) '$(srcdir)/ietf-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-ietf-mib.Tpo $(DEPDIR)/snmp_ups-ietf-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='ietf-mib.c' object='snmp_ups-ietf-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-ietf-mib.obj `if test -f 'ietf-mib.c'; then $(CYGPATH_W) 'ietf-mib.c'; else $(CYGPATH_W) '$(srcdir)/ietf-mib.c'; fi` + +snmp_ups-mge-mib.o: mge-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-mge-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-mge-mib.Tpo -c -o snmp_ups-mge-mib.o `test -f 'mge-mib.c' || echo '$(srcdir)/'`mge-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-mge-mib.Tpo $(DEPDIR)/snmp_ups-mge-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mge-mib.c' object='snmp_ups-mge-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-mge-mib.o `test -f 'mge-mib.c' || echo '$(srcdir)/'`mge-mib.c + +snmp_ups-mge-mib.obj: mge-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-mge-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-mge-mib.Tpo -c -o snmp_ups-mge-mib.obj `if test -f 'mge-mib.c'; then $(CYGPATH_W) 'mge-mib.c'; else $(CYGPATH_W) '$(srcdir)/mge-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-mge-mib.Tpo $(DEPDIR)/snmp_ups-mge-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='mge-mib.c' object='snmp_ups-mge-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-mge-mib.obj `if test -f 'mge-mib.c'; then $(CYGPATH_W) 'mge-mib.c'; else $(CYGPATH_W) '$(srcdir)/mge-mib.c'; fi` + +snmp_ups-netvision-mib.o: netvision-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-netvision-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-netvision-mib.Tpo -c -o snmp_ups-netvision-mib.o `test -f 'netvision-mib.c' || echo '$(srcdir)/'`netvision-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-netvision-mib.Tpo $(DEPDIR)/snmp_ups-netvision-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netvision-mib.c' object='snmp_ups-netvision-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-netvision-mib.o `test -f 'netvision-mib.c' || echo '$(srcdir)/'`netvision-mib.c + +snmp_ups-netvision-mib.obj: netvision-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-netvision-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-netvision-mib.Tpo -c -o snmp_ups-netvision-mib.obj `if test -f 'netvision-mib.c'; then $(CYGPATH_W) 'netvision-mib.c'; else $(CYGPATH_W) '$(srcdir)/netvision-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-netvision-mib.Tpo $(DEPDIR)/snmp_ups-netvision-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='netvision-mib.c' object='snmp_ups-netvision-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-netvision-mib.obj `if test -f 'netvision-mib.c'; then $(CYGPATH_W) 'netvision-mib.c'; else $(CYGPATH_W) '$(srcdir)/netvision-mib.c'; fi` + +snmp_ups-powerware-mib.o: powerware-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-powerware-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-powerware-mib.Tpo -c -o snmp_ups-powerware-mib.o `test -f 'powerware-mib.c' || echo '$(srcdir)/'`powerware-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-powerware-mib.Tpo $(DEPDIR)/snmp_ups-powerware-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='powerware-mib.c' object='snmp_ups-powerware-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-powerware-mib.o `test -f 'powerware-mib.c' || echo '$(srcdir)/'`powerware-mib.c + +snmp_ups-powerware-mib.obj: powerware-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-powerware-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-powerware-mib.Tpo -c -o snmp_ups-powerware-mib.obj `if test -f 'powerware-mib.c'; then $(CYGPATH_W) 'powerware-mib.c'; else $(CYGPATH_W) '$(srcdir)/powerware-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-powerware-mib.Tpo $(DEPDIR)/snmp_ups-powerware-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='powerware-mib.c' object='snmp_ups-powerware-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-powerware-mib.obj `if test -f 'powerware-mib.c'; then $(CYGPATH_W) 'powerware-mib.c'; else $(CYGPATH_W) '$(srcdir)/powerware-mib.c'; fi` + +snmp_ups-raritan-pdu-mib.o: raritan-pdu-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-raritan-pdu-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-raritan-pdu-mib.Tpo -c -o snmp_ups-raritan-pdu-mib.o `test -f 'raritan-pdu-mib.c' || echo '$(srcdir)/'`raritan-pdu-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-raritan-pdu-mib.Tpo $(DEPDIR)/snmp_ups-raritan-pdu-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='raritan-pdu-mib.c' object='snmp_ups-raritan-pdu-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-raritan-pdu-mib.o `test -f 'raritan-pdu-mib.c' || echo '$(srcdir)/'`raritan-pdu-mib.c + +snmp_ups-raritan-pdu-mib.obj: raritan-pdu-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-raritan-pdu-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-raritan-pdu-mib.Tpo -c -o snmp_ups-raritan-pdu-mib.obj `if test -f 'raritan-pdu-mib.c'; then $(CYGPATH_W) 'raritan-pdu-mib.c'; else $(CYGPATH_W) '$(srcdir)/raritan-pdu-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-raritan-pdu-mib.Tpo $(DEPDIR)/snmp_ups-raritan-pdu-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='raritan-pdu-mib.c' object='snmp_ups-raritan-pdu-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-raritan-pdu-mib.obj `if test -f 'raritan-pdu-mib.c'; then $(CYGPATH_W) 'raritan-pdu-mib.c'; else $(CYGPATH_W) '$(srcdir)/raritan-pdu-mib.c'; fi` + +snmp_ups-raritan-px2-mib.o: raritan-px2-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-raritan-px2-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-raritan-px2-mib.Tpo -c -o snmp_ups-raritan-px2-mib.o `test -f 'raritan-px2-mib.c' || echo '$(srcdir)/'`raritan-px2-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-raritan-px2-mib.Tpo $(DEPDIR)/snmp_ups-raritan-px2-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='raritan-px2-mib.c' object='snmp_ups-raritan-px2-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-raritan-px2-mib.o `test -f 'raritan-px2-mib.c' || echo '$(srcdir)/'`raritan-px2-mib.c + +snmp_ups-raritan-px2-mib.obj: raritan-px2-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-raritan-px2-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-raritan-px2-mib.Tpo -c -o snmp_ups-raritan-px2-mib.obj `if test -f 'raritan-px2-mib.c'; then $(CYGPATH_W) 'raritan-px2-mib.c'; else $(CYGPATH_W) '$(srcdir)/raritan-px2-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-raritan-px2-mib.Tpo $(DEPDIR)/snmp_ups-raritan-px2-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='raritan-px2-mib.c' object='snmp_ups-raritan-px2-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-raritan-px2-mib.obj `if test -f 'raritan-px2-mib.c'; then $(CYGPATH_W) 'raritan-px2-mib.c'; else $(CYGPATH_W) '$(srcdir)/raritan-px2-mib.c'; fi` + +snmp_ups-xppc-mib.o: xppc-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-xppc-mib.o -MD -MP -MF $(DEPDIR)/snmp_ups-xppc-mib.Tpo -c -o snmp_ups-xppc-mib.o `test -f 'xppc-mib.c' || echo '$(srcdir)/'`xppc-mib.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-xppc-mib.Tpo $(DEPDIR)/snmp_ups-xppc-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xppc-mib.c' object='snmp_ups-xppc-mib.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-xppc-mib.o `test -f 'xppc-mib.c' || echo '$(srcdir)/'`xppc-mib.c + +snmp_ups-xppc-mib.obj: xppc-mib.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -MT snmp_ups-xppc-mib.obj -MD -MP -MF $(DEPDIR)/snmp_ups-xppc-mib.Tpo -c -o snmp_ups-xppc-mib.obj `if test -f 'xppc-mib.c'; then $(CYGPATH_W) 'xppc-mib.c'; else $(CYGPATH_W) '$(srcdir)/xppc-mib.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/snmp_ups-xppc-mib.Tpo $(DEPDIR)/snmp_ups-xppc-mib.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='xppc-mib.c' object='snmp_ups-xppc-mib.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(snmp_ups_CFLAGS) $(CFLAGS) -c -o snmp_ups-xppc-mib.obj `if test -f 'xppc-mib.c'; then $(CYGPATH_W) 'xppc-mib.c'; else $(CYGPATH_W) '$(srcdir)/xppc-mib.c'; fi` mostlyclean-libtool: -rm -f *.lo @@ -1284,26 +2584,15 @@ mostlyclean-libtool: clean-libtool: -rm -rf .libs _libs -ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ - mkid -fID $$unique -tags: TAGS +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags -TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ + $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ @@ -1315,15 +2604,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $$unique; \ fi; \ fi -ctags: CTAGS -CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique @@ -1332,11 +2617,29 @@ GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -1370,7 +2673,7 @@ check-am: all-am check: check-am all-am: Makefile $(PROGRAMS) $(HEADERS) installdirs: - for dir in "$(DESTDIR)$(driverexecdir)" "$(DESTDIR)$(halexecdir)"; do \ + for dir in "$(DESTDIR)$(driverexecdir)" "$(DESTDIR)$(sbindir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am @@ -1383,14 +2686,19 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: - -test -z "$(MOSTLYCLEANFILES)" || rm -f $(MOSTLYCLEANFILES) clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) @@ -1399,13 +2707,158 @@ distclean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am -clean-am: clean-driverexecPROGRAMS clean-generic clean-halexecPROGRAMS \ - clean-libtool mostlyclean-am +clean-am: clean-driverexecPROGRAMS clean-generic clean-libtool \ + clean-sbinPROGRAMS mostlyclean-am distclean: distclean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/adelsystem_cbi.Po + -rm -f ./$(DEPDIR)/al175.Po + -rm -f ./$(DEPDIR)/apc-hid.Po + -rm -f ./$(DEPDIR)/apcsmart-old.Po + -rm -f ./$(DEPDIR)/apcsmart.Po + -rm -f ./$(DEPDIR)/apcsmart_tabs.Po + -rm -f ./$(DEPDIR)/apcupsd_ups-apcupsd-ups.Po + -rm -f ./$(DEPDIR)/arduino-hid.Po + -rm -f ./$(DEPDIR)/asem.Po + -rm -f ./$(DEPDIR)/bcmxcp.Po + -rm -f ./$(DEPDIR)/bcmxcp_ser.Po + -rm -f ./$(DEPDIR)/bcmxcp_usb.Po + -rm -f ./$(DEPDIR)/belkin-hid.Po + -rm -f ./$(DEPDIR)/belkin.Po + -rm -f ./$(DEPDIR)/belkinunv.Po + -rm -f ./$(DEPDIR)/bestfcom.Po + -rm -f ./$(DEPDIR)/bestfortress.Po + -rm -f ./$(DEPDIR)/bestuferrups.Po + -rm -f ./$(DEPDIR)/bestups.Po + -rm -f ./$(DEPDIR)/blazer.Po + -rm -f ./$(DEPDIR)/blazer_ser.Po + -rm -f ./$(DEPDIR)/blazer_usb.Po + -rm -f ./$(DEPDIR)/clone-outlet.Po + -rm -f ./$(DEPDIR)/clone.Po + -rm -f ./$(DEPDIR)/cps-hid.Po + -rm -f ./$(DEPDIR)/delta_ups-hid.Po + -rm -f ./$(DEPDIR)/dstate.Plo + -rm -f ./$(DEPDIR)/dummy_ups-dummy-ups.Po + -rm -f ./$(DEPDIR)/etapro.Po + -rm -f ./$(DEPDIR)/ever-hid.Po + -rm -f ./$(DEPDIR)/everups.Po + -rm -f ./$(DEPDIR)/explore-hid.Po + -rm -f ./$(DEPDIR)/gamatronic.Po + -rm -f ./$(DEPDIR)/generic_modbus.Po + -rm -f ./$(DEPDIR)/genericups.Po + -rm -f ./$(DEPDIR)/hidparser.Po + -rm -f ./$(DEPDIR)/huawei-ups2000.Po + -rm -f ./$(DEPDIR)/idowell-hid.Po + -rm -f ./$(DEPDIR)/isbmex.Po + -rm -f ./$(DEPDIR)/ivtscd.Po + -rm -f ./$(DEPDIR)/legrand-hid.Po + -rm -f ./$(DEPDIR)/libhid.Po + -rm -f ./$(DEPDIR)/libusb0.Po + -rm -f ./$(DEPDIR)/libusb1.Po + -rm -f ./$(DEPDIR)/liebert-esp2.Po + -rm -f ./$(DEPDIR)/liebert-hid.Po + -rm -f ./$(DEPDIR)/liebert.Po + -rm -f ./$(DEPDIR)/macosx-ups.Po + -rm -f ./$(DEPDIR)/main.Plo + -rm -f ./$(DEPDIR)/masterguard.Po + -rm -f ./$(DEPDIR)/metasys.Po + -rm -f ./$(DEPDIR)/mge-hid.Po + -rm -f ./$(DEPDIR)/mge-utalk.Po + -rm -f ./$(DEPDIR)/mge-xml.Po + -rm -f ./$(DEPDIR)/mge_shut-hidparser.Po + -rm -f ./$(DEPDIR)/mge_shut-libhid.Po + -rm -f ./$(DEPDIR)/mge_shut-libshut.Po + -rm -f ./$(DEPDIR)/mge_shut-mge-hid.Po + -rm -f ./$(DEPDIR)/mge_shut-usbhid-ups.Po + -rm -f ./$(DEPDIR)/microdowell.Po + -rm -f ./$(DEPDIR)/microsol-apc.Po + -rm -f ./$(DEPDIR)/microsol-common.Po + -rm -f ./$(DEPDIR)/netxml-ups.Po + -rm -f ./$(DEPDIR)/nut-ipmipsu.Po + -rm -f ./$(DEPDIR)/nut-libfreeipmi.Po + -rm -f ./$(DEPDIR)/nutdrv_atcl_usb.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-libusb0.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-libusb1.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_ablerex.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_hunnox.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_masterguard.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-usb-common.Po + -rm -f ./$(DEPDIR)/nutdrv_siemens_sitop.Po + -rm -f ./$(DEPDIR)/oneac.Po + -rm -f ./$(DEPDIR)/openups-hid.Po + -rm -f ./$(DEPDIR)/optiups.Po + -rm -f ./$(DEPDIR)/phoenixcontact_modbus.Po + -rm -f ./$(DEPDIR)/pijuice.Po + -rm -f ./$(DEPDIR)/powercom-hid.Po + -rm -f ./$(DEPDIR)/powercom.Po + -rm -f ./$(DEPDIR)/powerman-pdu.Po + -rm -f ./$(DEPDIR)/powerp-bin.Po + -rm -f ./$(DEPDIR)/powerp-txt.Po + -rm -f ./$(DEPDIR)/powerpanel.Po + -rm -f ./$(DEPDIR)/powervar-hid.Po + -rm -f ./$(DEPDIR)/rhino.Po + -rm -f ./$(DEPDIR)/richcomm_usb.Po + -rm -f ./$(DEPDIR)/riello.Po + -rm -f ./$(DEPDIR)/riello_ser.Po + -rm -f ./$(DEPDIR)/riello_usb.Po + -rm -f ./$(DEPDIR)/safenet.Po + -rm -f ./$(DEPDIR)/salicru-hid.Po + -rm -f ./$(DEPDIR)/serial.Plo + -rm -f ./$(DEPDIR)/skel.Po + -rm -f ./$(DEPDIR)/snmp_ups-apc-ats-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-apc-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-apc-pdu-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-baytech-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-bestpower-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-compaq-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-cyberpower-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-delta_ups-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-ats16-nm2-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-ats16-nmc-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-ats30-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-pdu-genesis2-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-pdu-marlin-helpers.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-pdu-marlin-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-pdu-pulizzi-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-pdu-revelation-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-emerson-avocent-pdu-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-hpe-pdu-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-huawei-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-ietf-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-mge-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-netvision-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-powerware-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-raritan-pdu-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-raritan-px2-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-snmp-ups-helpers.Po + -rm -f ./$(DEPDIR)/snmp_ups-snmp-ups.Po + -rm -f ./$(DEPDIR)/snmp_ups-xppc-mib.Po + -rm -f ./$(DEPDIR)/socomec_jbus.Po + -rm -f ./$(DEPDIR)/solis.Po + -rm -f ./$(DEPDIR)/tripplite-hid.Po + -rm -f ./$(DEPDIR)/tripplite.Po + -rm -f ./$(DEPDIR)/tripplite_usb.Po + -rm -f ./$(DEPDIR)/tripplitesu.Po + -rm -f ./$(DEPDIR)/upscode2.Po + -rm -f ./$(DEPDIR)/upsdrvctl.Po + -rm -f ./$(DEPDIR)/usb-common.Po + -rm -f ./$(DEPDIR)/usbhid-ups.Po + -rm -f ./$(DEPDIR)/victronups.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -1428,7 +2881,7 @@ install-dvi: install-dvi-am install-dvi-am: -install-exec-am: install-driverexecPROGRAMS install-halexecPROGRAMS +install-exec-am: install-driverexecPROGRAMS install-sbinPROGRAMS install-html: install-html-am @@ -1451,7 +2904,151 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/adelsystem_cbi.Po + -rm -f ./$(DEPDIR)/al175.Po + -rm -f ./$(DEPDIR)/apc-hid.Po + -rm -f ./$(DEPDIR)/apcsmart-old.Po + -rm -f ./$(DEPDIR)/apcsmart.Po + -rm -f ./$(DEPDIR)/apcsmart_tabs.Po + -rm -f ./$(DEPDIR)/apcupsd_ups-apcupsd-ups.Po + -rm -f ./$(DEPDIR)/arduino-hid.Po + -rm -f ./$(DEPDIR)/asem.Po + -rm -f ./$(DEPDIR)/bcmxcp.Po + -rm -f ./$(DEPDIR)/bcmxcp_ser.Po + -rm -f ./$(DEPDIR)/bcmxcp_usb.Po + -rm -f ./$(DEPDIR)/belkin-hid.Po + -rm -f ./$(DEPDIR)/belkin.Po + -rm -f ./$(DEPDIR)/belkinunv.Po + -rm -f ./$(DEPDIR)/bestfcom.Po + -rm -f ./$(DEPDIR)/bestfortress.Po + -rm -f ./$(DEPDIR)/bestuferrups.Po + -rm -f ./$(DEPDIR)/bestups.Po + -rm -f ./$(DEPDIR)/blazer.Po + -rm -f ./$(DEPDIR)/blazer_ser.Po + -rm -f ./$(DEPDIR)/blazer_usb.Po + -rm -f ./$(DEPDIR)/clone-outlet.Po + -rm -f ./$(DEPDIR)/clone.Po + -rm -f ./$(DEPDIR)/cps-hid.Po + -rm -f ./$(DEPDIR)/delta_ups-hid.Po + -rm -f ./$(DEPDIR)/dstate.Plo + -rm -f ./$(DEPDIR)/dummy_ups-dummy-ups.Po + -rm -f ./$(DEPDIR)/etapro.Po + -rm -f ./$(DEPDIR)/ever-hid.Po + -rm -f ./$(DEPDIR)/everups.Po + -rm -f ./$(DEPDIR)/explore-hid.Po + -rm -f ./$(DEPDIR)/gamatronic.Po + -rm -f ./$(DEPDIR)/generic_modbus.Po + -rm -f ./$(DEPDIR)/genericups.Po + -rm -f ./$(DEPDIR)/hidparser.Po + -rm -f ./$(DEPDIR)/huawei-ups2000.Po + -rm -f ./$(DEPDIR)/idowell-hid.Po + -rm -f ./$(DEPDIR)/isbmex.Po + -rm -f ./$(DEPDIR)/ivtscd.Po + -rm -f ./$(DEPDIR)/legrand-hid.Po + -rm -f ./$(DEPDIR)/libhid.Po + -rm -f ./$(DEPDIR)/libusb0.Po + -rm -f ./$(DEPDIR)/libusb1.Po + -rm -f ./$(DEPDIR)/liebert-esp2.Po + -rm -f ./$(DEPDIR)/liebert-hid.Po + -rm -f ./$(DEPDIR)/liebert.Po + -rm -f ./$(DEPDIR)/macosx-ups.Po + -rm -f ./$(DEPDIR)/main.Plo + -rm -f ./$(DEPDIR)/masterguard.Po + -rm -f ./$(DEPDIR)/metasys.Po + -rm -f ./$(DEPDIR)/mge-hid.Po + -rm -f ./$(DEPDIR)/mge-utalk.Po + -rm -f ./$(DEPDIR)/mge-xml.Po + -rm -f ./$(DEPDIR)/mge_shut-hidparser.Po + -rm -f ./$(DEPDIR)/mge_shut-libhid.Po + -rm -f ./$(DEPDIR)/mge_shut-libshut.Po + -rm -f ./$(DEPDIR)/mge_shut-mge-hid.Po + -rm -f ./$(DEPDIR)/mge_shut-usbhid-ups.Po + -rm -f ./$(DEPDIR)/microdowell.Po + -rm -f ./$(DEPDIR)/microsol-apc.Po + -rm -f ./$(DEPDIR)/microsol-common.Po + -rm -f ./$(DEPDIR)/netxml-ups.Po + -rm -f ./$(DEPDIR)/nut-ipmipsu.Po + -rm -f ./$(DEPDIR)/nut-libfreeipmi.Po + -rm -f ./$(DEPDIR)/nutdrv_atcl_usb.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-libusb0.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-libusb1.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_ablerex.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_bestups.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_blazer-common.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_hunnox.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_masterguard.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_mecer.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec-old.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_megatec.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_mustek.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_q1.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs-hex.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic-qs.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_voltronic.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-nutdrv_qx_zinto.Po + -rm -f ./$(DEPDIR)/nutdrv_qx-usb-common.Po + -rm -f ./$(DEPDIR)/nutdrv_siemens_sitop.Po + -rm -f ./$(DEPDIR)/oneac.Po + -rm -f ./$(DEPDIR)/openups-hid.Po + -rm -f ./$(DEPDIR)/optiups.Po + -rm -f ./$(DEPDIR)/phoenixcontact_modbus.Po + -rm -f ./$(DEPDIR)/pijuice.Po + -rm -f ./$(DEPDIR)/powercom-hid.Po + -rm -f ./$(DEPDIR)/powercom.Po + -rm -f ./$(DEPDIR)/powerman-pdu.Po + -rm -f ./$(DEPDIR)/powerp-bin.Po + -rm -f ./$(DEPDIR)/powerp-txt.Po + -rm -f ./$(DEPDIR)/powerpanel.Po + -rm -f ./$(DEPDIR)/powervar-hid.Po + -rm -f ./$(DEPDIR)/rhino.Po + -rm -f ./$(DEPDIR)/richcomm_usb.Po + -rm -f ./$(DEPDIR)/riello.Po + -rm -f ./$(DEPDIR)/riello_ser.Po + -rm -f ./$(DEPDIR)/riello_usb.Po + -rm -f ./$(DEPDIR)/safenet.Po + -rm -f ./$(DEPDIR)/salicru-hid.Po + -rm -f ./$(DEPDIR)/serial.Plo + -rm -f ./$(DEPDIR)/skel.Po + -rm -f ./$(DEPDIR)/snmp_ups-apc-ats-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-apc-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-apc-pdu-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-baytech-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-bestpower-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-compaq-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-cyberpower-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-delta_ups-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-ats16-nm2-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-ats16-nmc-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-ats30-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-pdu-genesis2-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-pdu-marlin-helpers.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-pdu-marlin-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-pdu-pulizzi-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-eaton-pdu-revelation-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-emerson-avocent-pdu-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-hpe-pdu-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-huawei-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-ietf-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-mge-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-netvision-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-powerware-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-raritan-pdu-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-raritan-px2-mib.Po + -rm -f ./$(DEPDIR)/snmp_ups-snmp-ups-helpers.Po + -rm -f ./$(DEPDIR)/snmp_ups-snmp-ups.Po + -rm -f ./$(DEPDIR)/snmp_ups-xppc-mib.Po + -rm -f ./$(DEPDIR)/socomec_jbus.Po + -rm -f ./$(DEPDIR)/solis.Po + -rm -f ./$(DEPDIR)/tripplite-hid.Po + -rm -f ./$(DEPDIR)/tripplite.Po + -rm -f ./$(DEPDIR)/tripplite_usb.Po + -rm -f ./$(DEPDIR)/tripplitesu.Po + -rm -f ./$(DEPDIR)/upscode2.Po + -rm -f ./$(DEPDIR)/upsdrvctl.Po + -rm -f ./$(DEPDIR)/usb-common.Po + -rm -f ./$(DEPDIR)/usbhid-ups.Po + -rm -f ./$(DEPDIR)/victronups.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -1468,26 +3065,41 @@ ps: ps-am ps-am: -uninstall-am: uninstall-driverexecPROGRAMS uninstall-halexecPROGRAMS +uninstall-am: uninstall-driverexecPROGRAMS uninstall-sbinPROGRAMS .MAKE: install-am install-strip -.PHONY: CTAGS GTAGS all all-am check check-am clean \ - clean-driverexecPROGRAMS clean-generic clean-halexecPROGRAMS \ - clean-libtool ctags distclean distclean-compile \ - distclean-generic distclean-libtool distclean-tags distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-driverexecPROGRAMS \ - install-dvi install-dvi-am install-exec install-exec-am \ - install-halexecPROGRAMS install-html install-html-am \ +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-driverexecPROGRAMS clean-generic clean-libtool \ + clean-sbinPROGRAMS cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-driverexecPROGRAMS install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ install-info install-info-am install-man install-pdf \ - install-pdf-am install-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-compile \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags uninstall uninstall-am uninstall-driverexecPROGRAMS \ - uninstall-halexecPROGRAMS + install-pdf-am install-ps install-ps-am install-sbinPROGRAMS \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-compile mostlyclean-generic mostlyclean-libtool \ + pdf pdf-am ps ps-am tags tags-am uninstall uninstall-am \ + uninstall-driverexecPROGRAMS uninstall-sbinPROGRAMS +.PRECIOUS: Makefile + + +# Make sure out-of-dir dependencies exist (especially when dev-building parts): +$(top_builddir)/common/libcommon.la \ +$(top_builddir)/common/libparseconf.la \ +$(top_builddir)/clients/libupsclient.la: dummy + @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +dummy: + +# NOTE: Do not clean ".deps" in SUBDIRS of the main project, +# the root Makefile.am takes care of that! +#clean-local: +# rm -rf $(builddir)/.deps # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/drivers/adelsystem_cbi.c b/drivers/adelsystem_cbi.c new file mode 100644 index 0000000..cda2696 --- /dev/null +++ b/drivers/adelsystem_cbi.c @@ -0,0 +1,1344 @@ +/* adelsystem_cbi.c - driver for ADELSYSTEM CB/CBI DC-UPS + * + * Copyright (C) + * 2022 Dimitris Economou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * code indentation with tabstop=4 + */ + +#include "main.h" +#include "adelsystem_cbi.h" +#include +#include + +#define DRIVER_NAME "NUT ADELSYSTEM DC-UPS CB/CBI driver" +#define DRIVER_VERSION "0.01" + +/* variables */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static devstate_t *dstate = NULL; /* device state context */ +static int errcnt = 0; /* modbus access error counter */ +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static char *device_type = DEVICE_TYPE; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int dev_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ + + +/* initialize alarm structs */ +void alrminit(void); + +/* initialize register start address and hex address from register number */ +void reginit(void); + +/* read registers' memory region */ +int read_all_regs(modbus_t *mb, uint16_t *data); + +/* get config vars set by -x or defined in ups.conf driver section */ +void get_config_vars(void); + +/* get device state */ +int get_dev_state(devreg_t regindx, devstate_t **dvstat); + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port); + +/* reconnect upon communication error */ +void modbus_reconnect(void); + +/* modbus register read function */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data); + +/* modbus register write function */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg); + +/* count the time elapsed since start */ +long time_elapsed(struct timeval *start); + + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} +}; + +/* + * driver functions + */ + +/* read configuration variables from ups.conf and connect to ups device */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + dstate = (devstate_t *)xmalloc(sizeof(devstate_t)); + alrminit(); + reginit(); + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, dev_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", dev_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { + /* Older libmodbus API (with timeval), and we have + * checked at configure time that we can put uint32_t + * into its fields. They are probably "long" on many + * systems as respectively time_t and suseconds_t - + * but that is not guaranteed; for more details see + * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_time.h.html + */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_resp_to_s; + to.tv_usec = mod_resp_to_us; + /* void */ modbus_set_response_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#else +# error "Can not use libmodbus API for timeouts" +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ + + /* set modbus byte time out */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_byte_to_s; + to.tv_usec = mod_byte_to_us; + /* void */ modbus_set_byte_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ + +} + +/* initialize ups driver information */ +void upsdrv_initinfo(void) +{ + devstate_t *ds = dstate; /* device state context */ + upsdebugx(2, "upsdrv_initinfo"); + + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); + dstate_setinfo("device.type", "%s", device_type); + + /* read ups model */ + get_dev_state(PRDN, &ds); + dstate_setinfo("ups.model", "%s", ds->product.name); + upslogx(LOG_INFO, "ups.model = %s", ds->product.name); + + /* register instant commands */ + dstate_addcmd("load.off"); + + /* set callback for instant commands */ + upsh.instcmd = upscmd; +} + + +/* update UPS signal state */ +void upsdrv_updateinfo(void) +{ + int rval; /* return value */ + int i; /* local index */ + devstate_t *ds = dstate; /* device state */ + + upsdebugx(2, "upsdrv_updateinfo"); + + errcnt = 0; /* initialize error counter to zero */ + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ +#if READALL_REGS == 1 + rval = read_all_regs(mbctx, regs_data); + if (rval == -1) { + errcnt++; + } else { +#endif + /* + * update UPS status regarding MAINS and SHUTDOWN request + * - OL: On line (mains is present) + * - OB: On battery (mains is not present) + */ + rval = get_dev_state(MAIN, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->alrm->alrm[MAINS_AVAIL_I].actv) { + status_set("OB"); + alarm_set(mains->alrm[MAINS_AVAIL_I].descr); + upslogx(LOG_INFO, "ups.status = OB"); + } else { + status_set("OL"); + upslogx(LOG_INFO, "ups.status = OL"); + } + if (ds->alrm->alrm[SHUTD_REQST_I].actv) { + status_set("FSD"); + alarm_set(mains->alrm[SHUTD_REQST_I].descr); + upslogx(LOG_INFO, "ups.status = FSD"); + } + } + + /* + * update UPS status regarding battery voltage + */ + rval = get_dev_state(BVAL, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->alrm->alrm[BVAL_LOALRM_I].actv) { + status_set("LB"); + alarm_set(bval->alrm[BVAL_LOALRM_I].descr); + upslogx(LOG_INFO, "ups.status = LB"); + } + if (ds->alrm->alrm[BVAL_HIALRM_I].actv) { + status_set("HB"); + alarm_set(bval->alrm[BVAL_HIALRM_I].descr); + upslogx(LOG_INFO, "ups.status = HB"); + } + if (ds->alrm->alrm[BVAL_BSTSFL_I].actv) { + alarm_set(bval->alrm[BVAL_BSTSFL_I].descr); + upslogx(LOG_INFO, "battery start with battery flat"); + } + } + + /* get "battery.voltage" */ + rval = get_dev_state(BATV, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.voltage = %s", ds->reg.strval); + } + /* + * update UPS status regarding battery charger status + */ + + /* get "battery.charger.status" */ + rval = get_dev_state(CHRG, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->charge.state == CHRG_BULK || ds->charge.state == CHRG_ABSR) { + status_set("CHRG"); + upslogx(LOG_INFO, "ups.status = CHRG"); + } + dstate_setinfo("battery.charger.status", "%s", ds->charge.info); + upslogx(LOG_DEBUG, "battery.charger.status = %s", ds->charge.info); + } + rval = get_dev_state(PMNG, &ds); + if (rval == -1) { + errcnt++; + } else { + if (ds->power.state == PMNG_BCKUP) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + upslogx(LOG_INFO, "ups.status = DISCHRG"); + } + if (ds->power.state == PMNG_BOOST) { + status_set("BOOST"); + upslogx(LOG_INFO, "ups.status = BOOST"); + } + } + + /* + * update UPS battery state of charge + */ + rval = get_dev_state(BSOC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.charge", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.charge = %s", ds->reg.strval); + } + + /* + * update UPS AC input state + */ + rval = get_dev_state(VACA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(VAC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("input.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "input.voltage = %s", ds->reg.strval); + } + + /* + * update UPS onboard temperature state + */ + rval = get_dev_state(OBTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(OTMP, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("ups.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "ups.temperature = %s", ds->reg.strval); + } + /* + * update UPS battery temperature state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + rval = get_dev_state(BTMP, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.temperature", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.temperature = %s", ds->reg.strval); + } + rval = get_dev_state(TBUF, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("battery.runtime", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "battery.runtime = %s", ds->reg.strval); + } + + /* + * update UPS device failure state + */ + rval = get_dev_state(DEVF, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS SoH and SoC states + */ + rval = get_dev_state(SCSH, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS battery state + */ + rval = get_dev_state(BSTA, &ds); + if (rval == -1) { + errcnt++; + } else { + for (i = 0; i < ds->alrm->alrm_c; i++) { + if (ds->alrm->alrm[i].actv) { + alarm_set(ds->alrm->alrm[i].descr); + upsdebugx(3, "%s alarm is active", ds->alrm->alrm[i].descr); + } + } + } + + /* + * update UPS load status + */ + rval = get_dev_state(LVDC, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("output.voltage", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.voltage = %s", ds->reg.strval); + } + rval = get_dev_state(LCUR, &ds); + if (rval == -1) { + errcnt++; + } else { + dstate_setinfo("output.current", "%s", ds->reg.strval); + upslogx(LOG_DEBUG, "output.current = %s", ds->reg.strval); + } +#if READALL_REGS == 1 + } +#endif + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2, "Communication errors: %d", errcnt); + dstate_datastale(); + } +} + +/* shutdown UPS */ +void upsdrv_shutdown(void) +{ + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2, "ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); +} + +/* print driver usage info */ +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "dev_slave_id", "device modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); +} + +/* close modbus connection and free modbus context allocated memory */ +void upsdrv_cleanup(void) +{ + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } + if (dstate != NULL) { + free(dstate); + } +} + +/* + * driver support functions + */ + +/* initialize alarm structs */ +void alrminit(void) +{ + mains = alloc_alrm_ar(mains_c, sizeof(mains_ar)); + alrm_ar_init(mains, mains_ar, mains_c); + vaca = alloc_alrm_ar(vaca_c, sizeof(vaca_ar)); + alrm_ar_init(vaca, vaca_ar, vaca_c); + devf = alloc_alrm_ar(devf_c, sizeof(devf_ar)); + alrm_ar_init(devf, devf_ar, devf_c); + btsf = alloc_alrm_ar(btsf_c, sizeof(btsf_ar)); + alrm_ar_init(btsf, btsf_ar, btsf_c); + bval = alloc_alrm_ar(bval_c, sizeof(bval_ar)); + alrm_ar_init(bval, bval_ar, bval_c); + shsc = alloc_alrm_ar(shsc_c, sizeof(shsc_ar)); + alrm_ar_init(shsc, shsc_ar, shsc_c); + bsta = alloc_alrm_ar(bsta_c, sizeof(bsta_ar)); + alrm_ar_init(bsta, bsta_ar, bsta_c); + obta = alloc_alrm_ar(obta_c, sizeof(obta_ar)); + alrm_ar_init(obta, obta_ar, obta_c); +} + +/* initialize register start address and hex address from register number */ +void reginit(void) +{ + int i; /* local index */ + + for (i = 0; i < MODBUS_NUMOF_REGS; i++) { + int rnum = regs[i].num; + switch (regs[i].type) { + case COIL: + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x0 + regs[i].num - 1; + break; + case INPUT_B: + rnum -= 10000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x10000 + rnum - 1; + break; + case INPUT_R: + rnum -= 30000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x30000 + rnum - 1; + break; + case HOLDING: + rnum -= 40000; + regs[i].saddr = rnum - 1; + regs[i].xaddr = 0x40000 + rnum - 1; + break; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: + upslogx(LOG_ERR, + "Invalid register type %d for register %d", + regs[i].type, + regs[i].num + ); + upsdebugx(3, + "Invalid register type %d for register %d", + regs[i].type, + regs[i].num + ); +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif + } + upsdebugx(3, + "reginit: num:%d, type: %d saddr: %d, xaddr: 0x%x", + regs[i].num, + regs[i].type, + regs[i].saddr, + regs[i].xaddr + ); + } +} + +/* read registers' memory region */ +int read_all_regs(modbus_t *mb, uint16_t *data) +{ + int rval; + + /* read all HOLDING registers */ + rval = modbus_read_registers(mb, regs[H_REG_STARTIDX].xaddr, MAX_H_REGS, data); + if (rval == -1) { + upslogx(LOG_ERR, + "ERROR:(%s) modbus_read: addr:0x%x, length:%8d, path:%s\n", + modbus_strerror(errno), + regs[H_REG_STARTIDX].xaddr, + MAX_H_REGS, + device_path + ); + + /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ + if (errno == EPIPE || errno == EMBBADDATA || errno == EMBBADCRC) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + + /* no COIL, INPUT_B or INPUT_R register regions to read */ + + return rval; +} + +/* Read a modbus register */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: + upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + break; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif + } + if (rval == -1) { + upslogx(LOG_ERR, + "ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE, INVALID CRC and INVALID DATA error try to reconnect */ + if (errno == EPIPE || errno == EMBBADDATA || errno == EMBBADCRC) { + upsdebugx(1, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* write a modbus register */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x00FF; + uint mask16 = 0xFFFF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR, + "ERROR:(%s) modbus_write: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(1, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* returns the time elapsed since start in milliseconds */ +long time_elapsed(struct timeval *start) +{ + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; +} + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg) +{ + int rval; + int data; + + if (!strcasecmp(cmd, "load.off")) { + data = 1; + rval = register_write(mbctx, regs[FSD].xaddr, regs[FSD].type, &data); + if (rval == -1) { + upslogx(2, + "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + regs[FSD].xaddr, + regs[FSD].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", regs[FSD].xaddr, data); + rval = STAT_INSTCMD_HANDLED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; +} + +/* read device state, returns 0 on success or -1 on communication error + it formats state depending on register semantics */ +int get_dev_state(devreg_t regindx, devstate_t **dvstat) +{ + int i; /* local index */ + int n; + int rval; /* return value */ + static char *ptr = NULL; /* temporary pointer */ + uint reg_val; /* register value */ +#if READALL_REGS == 0 + uint num; /* register number */ + regtype_t rtype; /* register type */ + int addr; /* register address */ +#endif + devstate_t *state; /* device state */ + + state = *dvstat; +#if READALL_REGS == 1 + reg_val = regs_data[regindx]; + rval = 0; +#elif READALL_REGS == 0 + num = regs[regindx].num; + addr = regs[regindx].xaddr; + rtype = regs[regindx].type; + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval == -1) { + return rval; + } + upsdebugx(3, + "get_dev_state: num: %d, addr: 0x%x, regtype: %d, data: %d", + num, + addr, + rtype, + reg_val + ); +#endif + /* process register data */ + switch (regindx) { + case CHRG: /* "ups.charge" */ + if (reg_val == CHRG_NONE) { + state->charge.state = CHRG_NONE; + state->charge.info = chrgs_i[CHRG_NONE]; + } else if (reg_val == CHRG_RECV) { + state->charge.state = CHRG_RECV; + state->charge.info = chrgs_i[CHRG_RECV]; + } else if (reg_val == CHRG_BULK) { + state->charge.state = CHRG_BULK; + state->charge.info = chrgs_i[CHRG_BULK]; + } else if (reg_val == CHRG_ABSR) { + state->charge.state = CHRG_ABSR; + state->charge.info = chrgs_i[CHRG_ABSR]; + } else if (reg_val == CHRG_FLOAT) { + state->charge.state = CHRG_FLOAT; + state->charge.info = chrgs_i[CHRG_FLOAT]; + } + upsdebugx(3, "get_dev_state: charge.state: %s", state->charge.info); + break; + case BATV: /* "battery.voltage" */ + case LVDC: /* "output.voltage" */ + case LCUR: /* "output.current" */ + if (reg_val != 0) { + state->reg.val.ui16 = reg_val; + double fval = reg_val / 1000.00; /* convert mV to V, mA to A */ + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val.ui16 = 0; + state->reg.strval = "0.00"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case TBUF: + case BSOH: + case BCEF: + case VAC: /* "input.voltage" */ + if (reg_val != 0) { + state->reg.val.ui16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + } else { + state->reg.val.ui16 = 0; + state->reg.strval = "0"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case BSOC: /* "battery.charge" */ + if (reg_val != 0) { + state->reg.val.ui16 = reg_val; + double fval = (double )reg_val * regs[BSOC].scale; + n = snprintf(NULL, 0, "%.2f", fval); + if (ptr != NULL) { + free(ptr); + } + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + } else { + state->reg.val.ui16 = 0; + state->reg.strval = "0.00"; + } + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case BTMP: /* "battery.temperature" */ + case OTMP: /* "ups.temperature" */ + state->reg.val.ui16 = reg_val; + double fval = reg_val - 273.15; + n = snprintf(NULL, 0, "%.2f", fval); + char *fval_s = (char *)xmalloc(sizeof(char) * (n + 1)); + if (ptr != NULL) { + free(ptr); + } + ptr = fval_s; + sprintf(fval_s, "%.2f", fval); + state->reg.strval = fval_s; + upsdebugx(3, "get_dev_state: variable: %s", state->reg.strval); + break; + case PMNG: /* "ups.status" & "battery.charge" */ + if (reg_val == PMNG_BCKUP) { + state->power.state = PMNG_BCKUP; + state->power.info = pwrmng_i[PMNG_BCKUP]; + } else if (reg_val == PMNG_CHRGN) { + state->power.state = PMNG_CHRGN; + state->power.info = pwrmng_i[PMNG_CHRGN]; + } else if (reg_val == PMNG_BOOST) { + state->power.state = PMNG_BOOST; + state->power.info = pwrmng_i[PMNG_BOOST]; + } else if (reg_val == PMNG_NCHRG) { + state->power.state = PMNG_NCHRG; + state->power.info = pwrmng_i[PMNG_NCHRG]; + } + upsdebugx(3, "get_dev_state: power.state: %s", state->reg.strval); + break; + case PRDN: /* "ups.model" */ + for (i = 0; i < DEV_NUMOF_MODELS; i++) { + if (prdnm_i[i].val == reg_val) { + break; + } + } + state->product.val = reg_val; + state->product.name = prdnm_i[i].name; + upsdebugx(3, "get_dev_state: product.name: %s", state->product.name); + break; + case BSTA: + if (reg_val & BSTA_REVPOL_M) { + bsta->alrm[BSTA_REVPOL_I].actv = 1; + } else { + bsta->alrm[BSTA_REVPOL_I].actv = 0; + } + if (reg_val & BSTA_NOCNND_M) { + bsta->alrm[BSTA_NOCNND_I].actv = 1; + } else { + bsta->alrm[BSTA_NOCNND_I].actv = 0; + } + if (reg_val & BSTA_CLSHCR_M) { + bsta->alrm[BSTA_CLSHCR_I].actv = 1; + } else { + bsta->alrm[BSTA_CLSHCR_I].actv = 0; + } + if (reg_val & BSTA_SULPHD_M) { + bsta->alrm[BSTA_SULPHD_I].actv = 1; + } else { + bsta->alrm[BSTA_SULPHD_I].actv = 0; + } + if (reg_val & BSTA_CHEMNS_M) { + bsta->alrm[BSTA_CHEMNS_I].actv = 1; + } else { + bsta->alrm[BSTA_CHEMNS_I].actv = 0; + } + if (reg_val & BSTA_CNNFLT_M) { + bsta->alrm[BSTA_CNNFLT_I].actv = 1; + } else { + bsta->alrm[BSTA_CNNFLT_I].actv = 0; + } + state->alrm = bsta; + break; + case SCSH: + if (reg_val & SHSC_HIRESI_M) { + shsc->alrm[SHSC_HIRESI_I].actv = 1; + } else { + shsc->alrm[SHSC_HIRESI_I].actv = 0; + } + if (reg_val & SHSC_LOCHEF_M) { + shsc->alrm[SHSC_LOCHEF_I].actv = 1; + } else { + shsc->alrm[SHSC_LOCHEF_I].actv = 0; + } + if (reg_val & SHSC_LOEFCP_M) { + shsc->alrm[SHSC_LOEFCP_I].actv = 1; + } else { + shsc->alrm[SHSC_LOEFCP_I].actv = 0; + } + if (reg_val & SHSC_LOWSOC_M) { + shsc->alrm[SHSC_LOWSOC_I].actv = 1; + } else { + shsc->alrm[SHSC_LOWSOC_I].actv = 0; + } + state->alrm = shsc; + break; + case BVAL: + if (reg_val & BVAL_HIALRM_M) { + bval->alrm[BVAL_HIALRM_I].actv = 1; + } else { + bval->alrm[BVAL_HIALRM_I].actv = 0; + } + if (reg_val & BVAL_LOALRM_M) { + bval->alrm[BVAL_LOALRM_I].actv = 1; + } else { + bval->alrm[BVAL_LOALRM_I].actv = 0; + } + if (reg_val & BVAL_BSTSFL_M) { + bval->alrm[BVAL_BSTSFL_I].actv = 1; + } else { + bval->alrm[BVAL_BSTSFL_I].actv = 0; + } + state->alrm = bval; + break; + case BTSF: + if (reg_val & BTSF_FCND_M) { + btsf->alrm[BTSF_FCND_I].actv = 1; + } else { + btsf->alrm[BTSF_FCND_I].actv = 0; + } + if (reg_val & BTSF_NCND_M) { + btsf->alrm[BTSF_NCND_I].actv = 1; + } else { + btsf->alrm[BTSF_NCND_I].actv = 0; + } + state->alrm = btsf; + break; + case DEVF: + if (reg_val & DEVF_RCALRM_M) { + devf->alrm[DEVF_RCALRM_I].actv = 1; + } else { + devf->alrm[DEVF_RCALRM_I].actv = 0; + } + if (reg_val & DEVF_INALRM_M) { + devf->alrm[DEVF_INALRM_I].actv = 1; + } else { + devf->alrm[DEVF_INALRM_I].actv = 0; + } + if (reg_val & DEVF_LFNAVL_M) { + devf->alrm[DEVF_LFNAVL_I].actv = 1; + } else { + devf->alrm[DEVF_LFNAVL_I].actv = 0; + } + state->alrm = devf; + break; + case VACA: + if (reg_val & VACA_HIALRM_M) { + vaca->alrm[VACA_HIALRM_I].actv = 1; + } else { + vaca->alrm[VACA_HIALRM_I].actv = 0; + } + if (reg_val == VACA_LOALRM_M) { + vaca->alrm[VACA_LOALRM_I].actv = 1; + } else { + vaca->alrm[VACA_LOALRM_I].actv = 0; + } + state->alrm = vaca; + break; + case MAIN: + if (reg_val & MAINS_AVAIL_M) { + mains->alrm[MAINS_AVAIL_I].actv = 1; + } else { + mains->alrm[MAINS_AVAIL_I].actv = 0; + } + if (reg_val == SHUTD_REQST_M) { + mains->alrm[SHUTD_REQST_I].actv = 1; + } else { + mains->alrm[SHUTD_REQST_I].actv = 0; + } + state->alrm = mains; + break; + case OBTA: + if (reg_val == OBTA_HIALRM_V) { + obta->alrm[OBTA_HIALRM_I].actv = 1; + } + state->alrm = obta; + break; + case BINH: + case FSD: + break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: + state->reg.val.ui16 = reg_val; + n = snprintf(NULL, 0, "%d", reg_val); + if (ptr != NULL) { + free(ptr); + } + char *reg_val_s = (char *)xmalloc(sizeof(char) * (n + 1)); + ptr = reg_val_s; + sprintf(reg_val_s, "%d", reg_val); + state->reg.strval = reg_val_s; + break; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif + } + return rval; +} + +/* get driver configuration parameters */ +void get_config_vars(void) +{ + + /* check if serial baud rate is set and get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set and get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set and get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set and get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set and get the value */ + if (testvar("dev_slave_id")) { + dev_slave_id = (int)strtol(getval("dev_slave_id"), NULL, 10); + } + upsdebugx(2, "dev_slave_id %d", dev_slave_id); + + /* check if response time out (s) is set and get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set and get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set and get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set and get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); +} + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port) +{ + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; +} + +/* reconnect to modbus server upon connection error */ +void modbus_reconnect(void) +{ + int rval; + + upsdebugx(1, "modbus_reconnect, trying to reconnect to modbus server"); + + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, dev_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", dev_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + + /* set modbus response timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_resp_to_s; + to.tv_usec = mod_resp_to_us; + /* void */ modbus_set_response_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ + + /* set modbus byte timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_byte_to_s; + to.tv_usec = mod_byte_to_us; + /* void */ modbus_set_byte_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ +} + diff --git a/drivers/adelsystem_cbi.h b/drivers/adelsystem_cbi.h new file mode 100644 index 0000000..023586b --- /dev/null +++ b/drivers/adelsystem_cbi.h @@ -0,0 +1,531 @@ +/* adelsystem_cbi.h - Driver for ADELSYSTEM CB/CBI DC-UPS + * + * Copyright (C) + * 2022 Dimitris Economou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * code indentation with tabstop=4 + */ + +#ifndef ADELSYSTEM_CBI_H +#define ADELSYSTEM_CBI_H + +#include + +/* UPS device details */ +#define DEVICE_MFR "ADELSYSTEM" +#define DEVICE_TYPE "DC-UPS" +#define DEVICE_MODEL "CBI2801224A" + +/* serial access parameters */ +#define BAUD_RATE 9600 +#define PARITY 'N' +#define DATA_BIT 8 +#define STOP_BIT 1 + +/* + * modbus response and byte timeouts + * us: 1 - 999999 + */ +#define MODRESP_TIMEOUT_s 0 +#define MODRESP_TIMEOUT_us 200000 +#define MODBYTE_TIMEOUT_s 0 +#define MODBYTE_TIMEOUT_us 50000 + +/* modbus access parameters */ +#define MODBUS_SLAVE_ID 5 + +/* number of modbus registers */ +#define MODBUS_NUMOF_REGS 98 + +/* max HOLDING registers */ +#define MAX_H_REGS 120 + +/* start HOLDING register index */ +#define H_REG_STARTIDX 0 + +/* read all regs preprocessor flag */ +#ifndef READALL_REGS +#define READALL_REGS 1 +#endif + +/* number of device models */ +#define DEV_NUMOF_MODELS 10 + +/* shutdown repeat on error */ +#define FSD_REPEAT_CNT 3 + +/* shutdown repeat interval in ms */ +#define FSD_REPEAT_INTRV 1500 + +/* definition of register type */ +enum regtype { + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING +}; +typedef enum regtype regtype_t; + +/* product name info, "device.model" */ +struct prodname { + uint16_t val; + char *name; +}; +typedef struct prodname prodname_t; +static prodname_t prdnm_i[] = { + {1, "CBI1235A"}, + {2, "CBI2420A"}, + {3, "CBI4810A"}, + {4, "CBI2801224"}, + {7, "CBI480W"}, + {8, "CB122410A"}, + {9, "CB480W"}, + {11, "CB12245AJ"}, + {12, "CB1235A"}, + {13, "CB2420A"}, + {14, "CB4810A"} +}; + +/* charging status info, "battery.charger.status" */ +static char *chrgs_i[] = { + "none", + "resting", /* recovering */ + "charging", /* bulk */ + "charging", /* absorb */ + "floating" /* float */ +}; +struct chrgs { + int state; + char *info; +}; +typedef struct chrgs chrgs_t; + +/* power management info, "ups.status", "battery.charger.status" */ +static char *pwrmng_i[] = { + "backup", /* "OB", "discharging" */ + "charging", /* "OL" */ + "boost", + "not charging" +}; +struct pwrmng { + int state; + char *info; +}; +typedef struct pwrmng pwrmng_t; + +/* general modbus register value */ +struct reg { + union val { + uint16_t ui16; + uint8_t ui8; + } val; + char *strval; +}; +typedef struct reg reg_t; + +/* general alarm struct */ +struct alrm { + int actv; /* active flag */ + char *descr; /* description field */ +}; +typedef struct alrm alrm_t; + +/* general alarm array */ +struct alrm_ar { + int alrm_c; /* alarm count */ + alrm_t *alrm; /* alarm array */ +}; +typedef struct alrm_ar alrm_ar_t; + +/* + * BIT MASKS and VALUES + */ + +/* Charging status */ +#define CHRG_NONE 0 +#define CHRG_RECV 1 +#define CHRG_BULK 2 +#define CHRG_ABSR 3 +#define CHRG_FLOAT 4 + +/* power management */ +#define PMNG_BCKUP 0 +#define PMNG_CHRGN 1 +#define PMNG_BOOST 2 +#define PMNG_NCHRG 3 + +/* product name */ +#define PRDN_MAX 14 + +/* Mains alarm masks */ +#define MAINS_AVAIL_M 0x0001 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_M 0x0002 /* shutdown requested */ + +/* Mains alarm indices */ +#define MAINS_AVAIL_I 0 /* 0: available (OL) 1: not available (OB) */ +#define SHUTD_REQST_I 1 /* shutdown requested */ + +/* AC input voltage alarm masks */ +#define VACA_HIALRM_M 0x0001 /* high alarm */ +#define VACA_LOALRM_M 0x0002 /* low alarm */ + +/* AC input voltage alarm indices */ +#define VACA_HIALRM_I 0 /* high alarm */ +#define VACA_LOALRM_I 1 /* low alarm */ + +/* Onboard temperature alarm value */ +#define OBTA_HIALRM_V 1 /* high alarm */ + +/* Onboard temperature alarm index */ +#define OBTA_HIALRM_I 0 /* high alarm */ + +/* Device failure alarm masks */ +#define DEVF_RCALRM_M 0x0001 /* rectifier failure */ +#define DEVF_INALRM_M 0x0006 /* internal failure */ +#define DEVF_LFNAVL_M 0x0008 /* lifetest not available */ + +/* Device failure alarm indices */ +#define DEVF_RCALRM_I 0 /* rectifier failure */ +#define DEVF_INALRM_I 1 /* internal failure */ +#define DEVF_LFNAVL_I 2 /* lifetest not available */ + +/* Battery temp sensor failure alarm masks */ +#define BTSF_FCND_M 0x0001 /* connection fault */ +#define BTSF_NCND_M 0x0002 /* not connected */ + +/* Battery temp sensor failure alarm indices */ +#define BTSF_FCND_I 0 /* connection fault */ +#define BTSF_NCND_I 1 /* not connected */ + +/* Battery voltage alarm masks */ +#define BVAL_HIALRM_M 0x0001 /* high voltage */ +#define BVAL_LOALRM_M 0x0002 /* low voltage */ +#define BVAL_BSTSFL_M 0x0004 /* battery start with battery flat */ + +/* Battery voltage alarm indices */ +#define BVAL_HIALRM_I 0 /* high voltage */ +#define BVAL_LOALRM_I 1 /* low voltage */ +#define BVAL_BSTSFL_I 2 /* battery start with battery flat */ + +/* SoH and SoC alarm masks */ +#define SHSC_HIRESI_M 0x0001 /* high internal resistance */ +#define SHSC_LOCHEF_M 0x0002 /* low charge efficiency */ +#define SHSC_LOEFCP_M 0x0004 /* low effective capacity */ +#define SHSC_LOWSOC_M 0x0040 /* low state of charge */ + +/* SoH and SoC alarm indices */ +#define SHSC_HIRESI_I 0 /* high internal resistance */ +#define SHSC_LOCHEF_I 1 /* low charge efficiency */ +#define SHSC_LOEFCP_I 2 /* low effective capacity */ +#define SHSC_LOWSOC_I 3 /* low state of charge */ + +/* Battery status alarm masks */ +#define BSTA_REVPOL_M 0x0001 /* reversed polarity */ +#define BSTA_NOCNND_M 0x0002 /* not connected */ +#define BSTA_CLSHCR_M 0x0004 /* cell short circuit */ +#define BSTA_SULPHD_M 0x0008 /* sulphated */ +#define BSTA_CHEMNS_M 0x0010 /* chemistry not supported */ +#define BSTA_CNNFLT_M 0x0020 /* connection fault */ + +/* Battery status alarm indices */ +#define BSTA_REVPOL_I 0 /* reversed polarity */ +#define BSTA_NOCNND_I 1 /* not connected */ +#define BSTA_CLSHCR_I 2 /* cell short circuit */ +#define BSTA_SULPHD_I 3 /* sulphated */ +#define BSTA_CHEMNS_I 4 /* chemistry not supported */ +#define BSTA_CNNFLT_I 5 /* connection fault */ + +/* Allocate alarm arrays */ +static inline +alrm_ar_t *alloc_alrm_ar(int as, size_t n) +{ + alrm_ar_t *ret = xcalloc(sizeof(alrm_t) + n, 1); + if (ret) { + memcpy(ret, + &(alrm_ar_t const) { + .alrm_c = as + }, + sizeof(alrm_ar_t) + ); + } + return ret; +} + +/* Initialize alarm arrays */ +static inline +void alrm_ar_init(alrm_ar_t *ar_ptr, alrm_t *a_ptr, int as) +{ + ar_ptr->alrm_c = as; + ar_ptr->alrm = a_ptr; +} + +/* input mains and shutdown alarms */ +static alrm_t mains_ar[] = { + {0, "input voltage not available"}, + {0, "ups shutdown requested"} +}; +static int mains_c = 2; +static alrm_ar_t *mains; + +/* AC input voltage alarms */ +static alrm_t vaca_ar[] = { + {0, "input voltage high alarm"}, + {0, "input voltage low alarm"} +}; +static int vaca_c = 2; +static alrm_ar_t *vaca; + +/* device failure alarms */ +static alrm_t devf_ar[] = { + {0, "UPS rectifier failure"}, + {0, "UPS internal failure"}, + {0, "UPS lifetest not available"} +}; +static int devf_c = 3; +static alrm_ar_t *devf; + +/* battery sensor failure alarms */ +static alrm_t btsf_ar[] = { + {0, "battery temp sensor connection fault"}, + {0, "battery temp sensor not connected"} +}; +static int btsf_c = 2; +static alrm_ar_t *btsf; + +/* battery voltage alarms */ +static alrm_t bval_ar[] = { + {0, "battery high voltage"}, + {0, "battery low voltage"}, + {0, "battery start with battery flat"} +}; +static int bval_c = 3; +static alrm_ar_t *bval; + +/* battery SoH and SoC alarms */ +static alrm_t shsc_ar[] = { + {0, "battery high internal resistance"}, + {0, "battery low charge efficiency"}, + {0, "battery low effective capacity"}, + {0, "battery low state of charge"} +}; +static int shsc_c = 4; +static alrm_ar_t *shsc; + +/* battery status alarm */ +static alrm_t bsta_ar[] = { + {0, "battery reversed polarity"}, + {0, "battery not connected"}, + {0, "battery cell short circuit"}, + {0, "battery sulphated"}, + {0, "battery chemistry not supported"}, + {0, "battery connection fault"} +}; +static int bsta_c = 4; +static alrm_ar_t *bsta; + +/* onboard temperature alarm */ +static alrm_t obta_ar[] = { + {0, "onboard temperature high"} +}; +static int obta_c = 4; +static alrm_ar_t *obta; + +/* UPS device reg enum */ +enum devreg { + CHRG = 4, /* Charging status, "battery.charger.status" */ + BATV = 7, /* Battery voltage, "battery.voltage" */ + BCEF = 18, /* Battery charge efficiency factor (CEF) */ + BSOH = 20, /* Battery state-of-health */ + BSOC = 22, /* Battery state-of-charge, "battery.charge" */ + BTMP = 25, /* Battery temperature in Kelvin units, "battery.temperature" */ + PMNG = 5, /* Power management, "ups.status" */ + OTMP = 28, /* Onboard temperature, "ups.temperature" */ + PRDN = 67, /* Product name, "ups.model" */ + VAC = 29, /* AC voltage, "input.voltage" */ + LVDC = 10, /* Load voltage, "output.voltage" */ + LCUR = 19, /* Load current, "output.current" */ + BINH = 87, /* Backup inhibit */ + FSD = 40, /* Force shutdown */ + TBUF = 103, /* Time buffering, "battery.runtime" */ + BSTA = 31, /* Battery status alarms */ + SCSH, /* SoH and SoC alarms */ + BVAL = 34, /* Battery voltage alarm */ + BTSF = 43, /* Battery temp sensor failure */ + DEVF = 42, /* Device failure */ + OBTA = 46, /* On board temp alarm */ + VACA = 44, /* VAC alarms */ + MAIN /* Mains status */ +}; +typedef enum devreg devreg_t; + +/* UPS register attributes */ +struct regattr { + int num; + int saddr; /* register start address */ + int xaddr; /* register hex address */ + float scale; /* scale */ + regtype_t type; /* register type */ +}; +typedef struct regattr regattr_t; + +/* UPS device state info union */ +union devstate { + prodname_t product; /* ups model name */ + chrgs_t charge; /* charging status */ + pwrmng_t power; /* ups status */ + reg_t reg; /* state register*/ + alrm_ar_t *alrm; /* alarm statuses */ +}; + +typedef union devstate devstate_t; + +/* device register memory image */ +static uint16_t regs_data[MAX_H_REGS]; + +/* ADELSYSTEM CBI registers */ +static regattr_t regs[] = { + {40001, 0, 0, 1, HOLDING}, /* Address of slave unit */ + {40002, 0, 0, 1, HOLDING}, /* Baud rate for serial communication */ + {40003, 0, 0, 1, HOLDING}, /* Parity bit for serial communication */ + {40004, 0, 0, 1, HOLDING}, + {40005, 0, 0, 1, HOLDING}, /* Charging status */ + {40006, 0, 0, 1, HOLDING}, /* Power management + * 0:Backup 1:Charging 2:boost 3:Not charging + */ + {40007, 0, 0, 1, HOLDING}, /* Nominal output voltage */ + {40008, 0, 0, 1, HOLDING}, /* Battery voltage */ + {40009, 0, 0, 1, HOLDING}, /* Parameter map version ID */ + {40010, 0, 0, 1, HOLDING}, /* Software ID */ + {40011, 0, 0, 1, HOLDING}, /* Output load voltage */ + {40012, 0, 0, 1, HOLDING}, + {40013, 0, 0, 1, HOLDING}, + {40014, 0, 0, 1, HOLDING}, /* Battery charge current */ + {40015, 0, 0, 1, HOLDING}, + {40016, 0, 0, .1, HOLDING}, /* Battery capacity consumed */ + {40017, 0, 0, 1, HOLDING}, /* Battery discharge current */ + {40018, 0, 0, .1, HOLDING}, /* Effective battery capacity */ + {40019, 0, 0, 1, HOLDING}, /* Battery charge efficiency factor (CEF) */ + {40020, 0, 0, 1, HOLDING}, /* Output load current */ + {40021, 0, 0, 1, HOLDING}, /* Battery state-of-health */ + {40022, 0, 0, 1, HOLDING}, /* Time remaining to 100% discharge */ + {40023, 0, 0, .1, HOLDING}, /* Battery state-of-charge */ + {40024, 0, 0, 1, HOLDING}, /* Battery type currently selected */ + {40025, 0, 0, 1, HOLDING}, + {40026, 0, 0, 1, HOLDING}, /* Battery temperature in Kelvin units */ + {40027, 0, 0, 1, HOLDING}, /* Configuration mode */ + {40028, 0, 0, .1, HOLDING}, /* Battery net internal resistance */ + {40029, 0, 0, 1, HOLDING}, /* On-board temperature */ + {40030, 0, 0, 1, HOLDING}, /* AC input voltage */ + {40031, 0, 0, 1, HOLDING}, + {40032, 0, 0, 1, HOLDING}, /* Battery status alarm */ + {40033, 0, 0, 1, HOLDING}, /* Battery State of Charge and State of Health */ + {40034, 0, 0, 1, HOLDING}, /* Load output off duration after PC shutdown */ + {40035, 0, 0, 1, HOLDING}, /* Battery voltage alarm */ + {40036, 0, 0, 1, HOLDING}, /* Low AC input voltage alarm threshold */ + {40037, 0, 0, 1, HOLDING}, /* High AC input voltage alarm threshold */ + {40038, 0, 0, 1, HOLDING}, /* Load alarm */ + {40039, 0, 0, 1, HOLDING}, /* Device variant */ + {40040, 0, 0, 1, HOLDING}, + {40041, 0, 0, 1, HOLDING}, /* Force shutdown */ + {40042, 0, 0, 1, HOLDING}, + {40043, 0, 0, 1, HOLDING}, /* Device failure */ + {40044, 0, 0, 1, HOLDING}, /* Battery temperature sensor failure */ + {40045, 0, 0, 1, HOLDING}, /* AC input voltage alarm */ + {40046, 0, 0, 1, HOLDING}, /* Mains status */ + {40047, 0, 0, 1, HOLDING}, /* On board temperature alarm */ + {40048, 0, 0, 1, HOLDING}, /* Number of charge cycles completed */ + {40049, 0, 0, 1, HOLDING}, /* Charge cycles not completed */ + {40050, 0, 0, .1, HOLDING}, /* Ah charged */ + {40051, 0, 0, 1, HOLDING}, /* Total run time */ + {40052, 0, 0, 1, HOLDING}, /* Number of low battery voltage events */ + {40053, 0, 0, 1, HOLDING}, /* Number of high battery voltage events */ + {40054, 0, 0, 1, HOLDING}, /* Number of low VAC events at mains input */ + {40055, 0, 0, 1, HOLDING}, /* Number of High VAC events at mains input */ + {40056, 0, 0, 1, HOLDING}, /* Number of over temperature inside events */ + {40057, 0, 0, 1, HOLDING}, /* Number of mains-backup transitions */ + {40058, 0, 0, 1, HOLDING}, /* Number power boost events */ + {40059, 0, 0, 1, HOLDING}, /* Highest battery voltage */ + {40060, 0, 0, 1, HOLDING}, /* Highest output load voltage */ + {40061, 0, 0, .1, HOLDING}, /* Maximum depth of discharge */ + {40062, 0, 0, 1, HOLDING}, /* Lowest battery voltage */ + {40063, 0, 0, 1, HOLDING}, /* Lowest output load voltage */ + {40064, 0, 0, .1, HOLDING}, /* Average depth of discharge */ + {40065, 0, 0, 1, HOLDING}, /* History clear all */ + {40066, 0, 0, 1, HOLDING}, /* Factory settings */ + {40067, 0, 0, 1, HOLDING}, /* Product name */ + {40068, 0, 0, 1, HOLDING}, + {40069, 0, 0, 1, HOLDING}, /* Reset internal battery model */ + {40070, 0, 0, 1, HOLDING}, + {40071, 0, 0, 1, HOLDING}, /* Deep discharge battery prevention */ + {40072, 0, 0, 1, HOLDING}, /* Maximum charge current */ + {40073, 0, 0, 1, HOLDING}, /* Bulk voltage */ + {40074, 0, 0, 1, HOLDING}, /* Max bulk timer */ + {40075, 0, 0, 1, HOLDING}, /* Min bulk timer */ + {40076, 0, 0, 1, HOLDING}, + {40077, 0, 0, 1, HOLDING}, /* Absorption voltage */ + {40078, 0, 0, 1, HOLDING}, /* Max absorption timer */ + {40079, 0, 0, 1, HOLDING}, /* Min absorption timer */ + {40080, 0, 0, 1, HOLDING}, /* Return Amperes to float */ + {40081, 0, 0, 1, HOLDING}, /* Return amps timer */ + {40082, 0, 0, 1, HOLDING}, /* Float voltage */ + {40083, 0, 0, 1, HOLDING}, /* Force boost charge */ + {40084, 0, 0, 1, HOLDING}, /* Return to bulk voltage from float */ + {40085, 0, 0, 1, HOLDING}, /* Return to bulk delay */ + {40086, 0, 0, 1, HOLDING}, + {40087, 0, 0, 1, HOLDING}, /* Switchoff voltage without mains */ + {40088, 0, 0, 1, HOLDING}, /* Backup Inhibit 0 = Backup allowed + * 1 = Backup not allowed + */ + {40089, 0, 0, 1, HOLDING}, /* Number of battery cells */ + {40090, 0, 0, 1, HOLDING}, /* Temperature compensation coefficient */ + {40091, 0, 0, 1, HOLDING}, + {40092, 0, 0, 1, HOLDING}, /* Lifetest enable */ + {40093, 0, 0, 1, HOLDING}, /* Max alarm temp */ + {40094, 0, 0, 1, HOLDING}, /* Min alarm temp */ + {40095, 0, 0, 1, HOLDING}, + {40096, 0, 0, 1, HOLDING}, + {40097, 0, 0, 1, HOLDING}, /* Low battery threshold */ + {40098, 0, 0, 1, HOLDING}, /* SoC/SoH test period */ + {40099, 0, 0, 1, HOLDING}, /* Manual SoC/SoH test request */ + {40100, 0, 0, 1, HOLDING}, /* SoC/SoH test possible */ + {40101, 0, 0, .1, HOLDING}, /* Nominal battery internal resistance */ + {40102, 0, 0, .1, HOLDING}, /* Nominal battery cables resistance */ + {40103, 0, 0, 1, HOLDING}, /* Firmware ID */ + {40104, 0, 0, 1, HOLDING}, /* Time buffering */ + {40105, 0, 0, .1, HOLDING}, /* Battery capacity C20 */ + {40106, 0, 0, .1, HOLDING}, /* Battery Capacity C10 */ + {40107, 0, 0, 1, HOLDING}, /* Device switchoff delay */ + {40108, 0, 0, .1, HOLDING}, /* Battery Capacity C5 */ + {40109, 0, 0, .1, HOLDING}, /* Battery Capacity C2 */ + {40110, 0, 0, 1, HOLDING}, + {40111, 0, 0, 1, HOLDING}, /* PC power supply removal delay */ + {40112, 0, 0, .1, HOLDING}, /* Battery Capacity C1 */ + {40113, 0, 0, .1, HOLDING}, /* Low state-of-charge */ + {40114, 0, 0, 1, HOLDING}, + {40115, 0, 0, 1, HOLDING}, + {40116, 0, 0, 1, HOLDING}, + {40117, 0, 0, 1, HOLDING}, + {40118, 0, 0, 1, HOLDING}, + {40119, 0, 0, 1, HOLDING}, + {40120, 0, 0, 1, HOLDING} /* Zero-SoC reference */ +}; +#endif /* ADELSYSTEM_CBI_H */ + diff --git a/drivers/al175.c b/drivers/al175.c new file mode 100644 index 0000000..f014435 --- /dev/null +++ b/drivers/al175.c @@ -0,0 +1,1356 @@ +/* + * al175.c - NUT support for Eltek AL175 alarm module. + * AL175 shall be in COMLI mode. + * + * Copyright (C) 2004-2013 Marine & Bridge Navigation Systems + * Author: Kirill Smelkov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * - NOTE the following document is referenced in this driver: + * + * TE-36862-B4 "COMLI COMMUNICATION PROTOCOL IMPLEMENTED IN PRS SYSTEMS", + * by Eltek A/S + * + * + * - AL175 debug levels: + * + * 1 user-level trace (status, instcmd, etc...) + * 2 status decode errors + * 3 COMLI proto handling errors + * 4 raw IO trace + * + */ + +#include "main.h" +#include "serial.h" +#include "timehead.h" + +#include +#include +#include +#include +#include + +#include "nut_stdint.h" +typedef uint8_t byte_t; + + +#define DRIVER_NAME "Eltek AL175/COMLI driver" +#define DRIVER_VERSION "0.13" + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Kirill Smelkov \n" \ + "Marine & Bridge Navigation Systems ", + DRV_EXPERIMENTAL, + { NULL } +}; + + +#define STX 0x02 +#define ETX 0x03 +#define ACK 0x06 + + +/************ + * RAW DATA * + ************/ + +/** + * raw_data buffer representation + */ +typedef struct { + byte_t *buf; /*!< the whole buffer address */ + size_t buf_size; /*!< the whole buffer size */ + + byte_t *begin; /*!< begin of content */ + byte_t *end; /*!< one-past-end of content */ +} raw_data_t; + + +/** + * pseudo-alloca raw_data buffer (alloca is not in POSIX) + * @param varp ptr-to local raw_data_t variable to which to alloca + * @param buf_array array allocated on stack which will be used as storage + * (must be auto-variable) + * @return alloca'ed memory as raw_data + * + * Example: + * + * raw_data_t ack; + * byte_t ack_buf[8]; + * + * raw_alloc_onstack(&ack, ack_buf); + */ +#define raw_alloc_onstack(varp, buf_array) do { \ + (varp)->buf = &(buf_array)[0]; \ + (varp)->buf_size = sizeof(buf_array); \ + \ + (varp)->begin = (varp)->buf; \ + (varp)->end = (varp)->buf; \ +} while (0) + + +/** + * xmalloc raw buffer + * @param size size in bytes + * @return xmalloc'ed memory as raw_data + */ +static raw_data_t raw_xmalloc(size_t size) +{ + raw_data_t data; + + data.buf = xmalloc(size); + data.buf_size = size; + + data.begin = data.buf; + data.end = data.buf; + + return data; +} + +/** + * free raw_data buffer + * @param buf raw_data buffer to free + */ +static void raw_free(raw_data_t *buf) +{ + free(buf->buf); + + buf->buf = NULL; + buf->buf_size = 0; + buf->begin = NULL; + buf->end = NULL; +} + + +/***************************************************************************/ + +/*************** + * COMLI types * + ***************/ + +/** + * COMLI message header info + * @see 1. INTRODUCTION + */ +typedef struct { + int id; /*!< Id[1:2] */ + int stamp; /*!< Stamp[3] */ + int type; /*!< Mess Type[4] */ +} msg_head_t; + +/** + * COMLI IO header info + * @see 1. INTRODUCTION + */ +typedef struct { + size_t addr; /*!< Addr[5:8] */ + size_t len; /*!< NOB[9:10] */ +} io_head_t; + +/** + * maximum allowed io.len value + */ +#define IO_LEN_MAX 0xff + +/** + * COMLI header info + * @see 1. INTRODUCTION + */ +typedef struct { + msg_head_t msg; /*!< message header [1:4] */ + io_head_t io; /*!< io header [5:10] */ +} comli_head_t; + + + +/****************** + * MISC UTILITIES * + ******************/ + +/** + * convert hex string to int + * @param head input string + * @param len string length + * @return parsed value (>=0) if success, -1 on error + */ +static long from_hex(const byte_t *head, unsigned len) +{ + long val=0; + + while (len-- != 0) { + int ch = *head; + + if (!isxdigit(ch)) + return -1; /* wrong character */ + + val *= 0x10; + + if (isdigit(ch)) { + val += (ch-'0'); + } + else { + /* ch = toupper(ch) without locale-related problems */ + if (ch < 'A') + ch += 'A' - 'a'; + + val += 0x0A + (ch-'A'); + } + + ++head; + } + + return val; +} + +/** + * compute checksum of a buffer + * @see 10. CHECKSUM BCC + * @param buf buffer address + * @param count no. of bytes in the buffer + * @return computed checksum + */ +static byte_t compute_bcc(const byte_t *buf, size_t count) +{ + byte_t bcc=0; + size_t i; + + for (i=0; i> 7 ) | + (byte_t)( (x & 0x40) >> 5 ) | + (byte_t)( (x & 0x20) >> 3 ) | + (byte_t)( (x & 0x10) >> 1 ) | + (byte_t)( (x & 0x08) << 1 ) | + (byte_t)( (x & 0x04) << 3 ) | + (byte_t)( (x & 0x02) << 5 ) | + (byte_t)( (x & 0x01) << 7 ); + *buf = x; + + ++buf; + --count; + } +} + + +/********************************************************************/ + +/* + * communication basics + * + * ME (Monitor Equipment) + * PRS (Power Rectifier System) /think of it as of UPS in common speak/ + * + * there are 2 types of transactions: + * + * 'ACTIVATE COMMAND' + * ME -> PRS (al_prep_activate) + * ME <- PRS [ack] (al_check_ack) + * + * + * 'READ REGISTER' + * ME -> PRS (al_prep_read_req) + * ME <- PRS [data] (al_parse_reply) + * + */ + +/******************** + * COMLI primitives * + ********************/ + + +/************************ + * COMLI: OUTPUT FRAMES * + ************************/ + +/** + * prepare COMLI sentence + * @see 1. INTRODUCTION + * @param dest [out] where to put the result + * @param h COMLI header info + * @param buf data part of the sentence + * @param count amount of data bytes in the sentence + * + * @note: the data are copied into the sentence "as-is", there is no conversion is done. + * if the caller wants to reverse bits it is necessary to call reverse_bits(...) prior + * to comli_prepare. + */ +static void comli_prepare(raw_data_t *dest, const comli_head_t *h, const void *buf, size_t count) +{ +/* + * 0 1 2 3 4 5 6 7 8 9 10 11 - - - N-1 N + * +-----+---------+-------+------+-------------------------+-----------+------------+-----+-----+ + * | STX | IDh IDl | Stamp | type | addr1 addr2 addr3 addr4 | NOBh NOBl | ...data... | ETX | BCC | + * +-----+---------+-------+------+-------------------------+-----------+------------+-----+-----+ + * + * ^ ^ + * | | + *begin end + */ + byte_t *out = dest->begin; + + + /* it's caller responsibility to allocate enough space. + else it is a bug in the program */ + if ( (out+11+count+2) > (dest->buf + dest->buf_size) ) + fatalx(EXIT_FAILURE, "too small dest in comli_prepare\n"); + + out[0] = STX; + snprintf((char *)out+1, 10+1, "%02X%1i%1i%04zX%02zX", h->msg.id, h->msg.stamp, h->msg.type, h->io.addr, h->io.len); + + memcpy(out+11, buf, count); + reverse_bits(out+11, count); + + + out[11+count] = ETX; + out[12+count] = compute_bcc(out+1, 10+count+1); + + dest->end = dest->begin + (11+count+2); +} + + + + +/** + * prepare AL175 read data request + * @see 2. MESSAGE TYPE 2 (COMMAND SENT FROM MONITORING EQUIPMENT) + * @param dest [out] where to put the result + * @param addr start address of requested area + * @param count no. of requested bytes + */ +static void al_prep_read_req(raw_data_t *dest, size_t addr, size_t count) +{ + comli_head_t h; + + h.msg.id = 0x14; + h.msg.stamp = 1; + h.msg.type = 2; + + h.io.addr = addr; + h.io.len = count; + + comli_prepare(dest, &h, NULL, 0); +} + + +/** + * prepare AL175 activate command + * @see 4. MESSAGE TYPE 0 (ACTIVATE COMMAND) + * @param dest [out] where to put the result + * @param cmd command type [11] + * @param subcmd command subtype [12] + * @param pr1 first parameter [13:14] + * @param pr2 second parameter [15:16] + * @param pr3 third parameter [17:18] + */ +static void al_prep_activate(raw_data_t *dest, byte_t cmd, byte_t subcmd, uint16_t pr1, uint16_t pr2, uint16_t pr3) +{ + comli_head_t h; + char data[8+1]; + + h.msg.id = 0x14; + h.msg.stamp = 1; + h.msg.type = 0; + + h.io.addr = 0x4500; + h.io.len = 8; + + /* NOTE: doc says we should use ASCII coding here, but the actual + * values are > 0x80, so we use binary coding. And have to + * make this "fit" into the char array required by snprintf */ + data[0] = (char)cmd; + data[1] = (char)subcmd; + + /* FIXME? One CI testcase builder claims here that + * warning: '%2X' directive output may be truncated writing + * between 2 and 4 bytes into a region of size between 3 and 5 + * [-Wformat-truncation=] + * but none others do, and I can't figure out how it thinks so :/ + * + * Per https://stackoverflow.com/questions/51534284/how-to-circumvent-format-truncation-warning-in-gcc + * https://www.mail-archive.com/gcc-bugs@gcc.gnu.org/msg521037.html + * and simlar googlable sources, this seems to be a bug-or-feature + * linked to non-zero optimization level and/or not checking for the + * return value (conveys runtime errors if any do happen). + */ + assert (pr1 <= UINT8_MAX); + assert (pr2 <= UINT8_MAX); + assert (pr3 <= UINT8_MAX); + if (0 > snprintf(data+2, 6+1, "%2X%2X%2X", pr1, pr2, pr3)) { + data[8] = '\0'; + } + + comli_prepare(dest, &h, data, 8); +} + +/*********************** + * COMLI: INPUT FRAMES * + ***********************/ + +/** + * check COMLI frame for correct layout and bcc + * @param f frame to check + * + * @return 0 (ok) -1 (error) + */ +static int comli_check_frame(/*const*/ raw_data_t f) +{ + int bcc; + byte_t *tail; + + if ( (f.end - f.begin) < 2 ) + return -1; + + if (*f.begin!=STX) + return -1; + + tail = f.end - 2; + if (tail <= f.begin) + return -1; + + if (tail[0]!=ETX) + return -1; + + bcc = compute_bcc(f.begin+1, (size_t)(f.end - f.begin) - 2 /*STX & BCC*/); + if (bcc!= tail[1]) + return -1; + + return 0; +} + + +/** + * parse reply header from PRS + * @see 3. MESSAGE TYPE 0 (REPLY FROM PRS ON MESSAGE TYPE 2) + * + * @param io [out] parsed io_header + * @param raw_reply_head [in] raw reply header from PRS + * @return 0 (ok), -1 (error) + * + * @see al_parse_reply + */ +static int al_parse_reply_head(io_head_t *io, const raw_data_t raw_reply_head) +{ +/* + * 0 1 2 3 4 5 6 7 8 9 10 + * +-----+---------+-------+------+-------------------------+-----------+-----------+ + * | STX | IDh IDl | Stamp | type | addr1 addr2 addr3 addr4 | NOBh NOBl | ......... | + * +-----+---------+-------+------+-------------------------+-----------+-----------+ + * + * ^ ^ + * | | + * begin end + */ + + size_t io_addr, io_len; + const byte_t *reply_head = raw_reply_head.begin - 1; + + if ( (raw_reply_head.end - raw_reply_head.begin) != 10) { + upsdebugx(3, "%s: wrong size\t(%i != 10)", __func__, (int)(raw_reply_head.end - raw_reply_head.begin)); + return -1; /* wrong size */ + } + + if (reply_head[1]!='0' || reply_head[2]!='0') { + upsdebugx(3, "%s: wrong id\t('%c%c' != '00')", __func__, reply_head[1], reply_head[2]); + return -1; /* wrong id */ + } + + if (reply_head[3]!='1') { + upsdebugx(3, "%s: wrong stamp\t('%c' != '1')", __func__, reply_head[3]); + return -1; /* wrong stamp */ + } + + if (reply_head[4]!='0') { + upsdebugx(3, "%s: wrong type\t('%c' != '0')", __func__, reply_head[4]); + return -1; /* wrong type */ + } + + /* Avoid signed/unsigned implicit conversion warnings + * At least, when shuffling a signed long into unsigned long, + * don't have to worry about overflows */ + io_addr = (size_t)from_hex(&reply_head[5], 4); + if (io_addr == -1UL) { + upsdebugx(3, "%s: invalid addr\t('%c%c%c%c')", __func__, + reply_head[5], reply_head[6], reply_head[7], reply_head[8]); + return -1; /* wrong addr */ + } + + io_len = (size_t)from_hex(&reply_head[9], 2); + if (io_len == -1UL) { + upsdebugx(3, "%s: invalid nob\t('%c%c')", __func__, reply_head[9], reply_head[10]); + return -1; /* wrong NOB */ + } + + if (io_len > IO_LEN_MAX) { + upsdebugx(3, "nob too big\t(%zu > %i)", io_len, IO_LEN_MAX); + return -1; /* too much data claimed */ + } + + io->addr = io_addr; + io->len = io_len; + + + return 0; +} + + +/** + * parse reply from PRS + * @see 3. MESSAGE TYPE 0 (REPLY FROM PRS ON MESSAGE TYPE 2) + * @param io_head [out] parsed io_header + * @param io_buf [in] [out] raw_data where to place incoming data (see ...data... below) + * @param raw_reply raw reply from PRS to check + * @return 0 (ok), -1 (error) + * + * @see al_parse_reply_head + */ +static int al_parse_reply(io_head_t *io_head, raw_data_t *io_buf, /*const*/ raw_data_t raw_reply) +{ +/* + * 0 1 2 3 4 5 6 7 8 9 10 11 - - - N-1 N + * +-----+---------+-------+------+-------------------------+-----------+------------+-----+-----+ + * | STX | IDh IDl | Stamp | type | addr1 addr2 addr3 addr4 | NOBh NOBl | ...data... | ETX | BCC | + * +-----+---------+-------+------+-------------------------+-----------+------------+-----+-----+ + * + * ^ ^ + * | | + * begin end + */ + + int err; + size_t i; + const byte_t *reply = NULL; + + /* 1: extract header and parse it */ + /*const*/ raw_data_t raw_reply_head = raw_reply; + + if (raw_reply_head.begin + 10 <= raw_reply_head.end) + raw_reply_head.end = raw_reply_head.begin + 10; + + err = al_parse_reply_head(io_head, raw_reply_head); + if (err==-1) + return -1; + + + /* 2: process data */ + reply = raw_reply.begin - 1; + + if ( (raw_reply.end - raw_reply.begin) != (ptrdiff_t)(10 + io_head->len)) { + upsdebugx(3, "%s: corrupt sentence\t(%i != %zi)", + __func__, (int)(raw_reply.end - raw_reply.begin), 10 + io_head->len); + return -1; /* corrupt sentence */ + } + + + /* extract the data */ + if (io_buf->buf_size < io_head->len) { + upsdebugx(3, "%s: too much data to fit in io_buf\t(%zu > %zu)", + __func__, io_head->len, io_buf->buf_size); + return -1; /* too much data to fit in io_buf */ + } + + io_buf->begin = io_buf->buf; + io_buf->end = io_buf->begin; + + for (i=0; ilen; ++i) + *(io_buf->end++) = reply[11+i]; + + assert(io_buf->end - io_buf->begin >= 0); + size_t io_buf_len = (size_t)(io_buf->end - io_buf->begin); + reverse_bits(io_buf->begin, io_buf_len ); + + upsdebug_hex(3, "\t\t--> payload", io_buf->begin, io_buf_len); + + return 0; /* all ok */ +} + + +/** + * check acknowledge from PRS + * @see 5. ACKNOWLEDGE FROM PRS + * @param raw_ack raw acknowledge from PRS to check + * @return 0 on success, -1 on error + */ +static int al_check_ack(/*const*/ raw_data_t raw_ack) +{ +/* + * 0 1 2 3 4 5 6 7 + * +-----+---------+-------+------+-----+-----+-----+ + * | STX | IDh IDl | Stamp | type | ACK | ETX | BCC | + * +-----+---------+-------+------+-----+-----+-----+ + * + * ^ ^ + * | | + * begin end + */ + + const byte_t *ack = raw_ack.begin - 1; + + if ( (raw_ack.end - raw_ack.begin) !=5) { + upsdebugx(3, "%s: wrong size\t(%i != 5)", __func__, (int)(raw_ack.end - raw_ack.begin)); + return -1; /* wrong size */ + } + + if (ack[1]!='0' || ack[2]!='0') { + upsdebugx(3, "%s: wrong id\t('%c%c' != '00')", __func__, ack[1], ack[2]); + return -1; /* wrong id */ + } + + /* the following in not mandated. it is just said it will be + * "same as one received". but we always send '1' (0x31) as stamp + * (see 4. MESSAGE TYPE 0 (ACTIVATE COMMAND). Hence, stamp checking + * is hardcoded here. + */ + if (ack[3]!='1') { + upsdebugx(3, "%s: wrong stamp\t('%c' != '1')", __func__, ack[3]); + return -1; /* wrong stamp */ + } + + if (ack[4]!='1') { + upsdebugx(3, "%s: wrong type\t('%c' != '1')", __func__, ack[4]); + return -1; /* wrong type */ + } + + if (ack[5]!=ACK) { + upsdebugx(3, "%s: wrong ack\t(0x%02X != 0x%02X)", __func__, ack[5], ACK); + return -1; /* wrong ack */ + } + + + return 0; +} + + + + + +/******************************************************************/ + + +/********** + * SERIAL * + **********/ + +/* clear any flow control (copy from powercom.c) */ +static void ser_disable_flow_control (void) +{ + struct termios tio; + + tcgetattr (upsfd, &tio); + + /* Clumsy rewrite of a one-liner + * tio.c_iflag &= ~ (IXON | IXOFF); + * to avoid type conversion warnings */ + tcflag_t x = (IXON | IXOFF); + tio.c_iflag &= ~ x; + tio.c_cc[VSTART] = _POSIX_VDISABLE; + tio.c_cc[VSTOP] = _POSIX_VDISABLE; + + upsdebugx(4, "Flow control disable"); + + /* disable any flow control */ + tcsetattr(upsfd, TCSANOW, &tio); +} + +static void flush_rx_queue() +{ + ser_flush_in(upsfd, "", /*verbose=*/nut_debug_level); +} + +/** + * transmit frame to PRS + * + * @param dmsg debug message prefix + * @param frame the frame to tansmit + * @return 0 (ok) -1 (error) + */ +static int tx(const char *dmsg, /*const*/ raw_data_t frame) +{ + ssize_t err; + + assert(frame.end - frame.begin >= 0); + size_t frame_len = (size_t)(frame.end - frame.begin); + + upsdebug_ascii(3, dmsg, frame.begin, frame_len); + + err = ser_send_buf(upsfd, frame.begin, frame_len ); + if (err==-1) { + upslogx(LOG_ERR, "failed to send frame to PRS: %s", strerror(errno)); + return -1; + } + + if (err != (ssize_t)frame_len) { + upslogx(LOG_ERR, "sent incomplete frame to PRS"); + return -1; + } + + return 0; +} + +/*********** + * CHATTER * + ***********/ + +static time_t T_io_begin; /* start of current I/O transaction */ +static int T_io_timeout; /* in seconds */ + +/* start new I/O transaction with maximum time limit */ +static void io_new_transaction(int timeout) +{ + T_io_begin = time(NULL); + T_io_timeout = timeout; +} + +/** + * get next character from input stream + * + * @param ch ptr-to where store result + * + * @return -1 (error) 0 (timeout) >0 (got it) + * + */ +static ssize_t get_char(char *ch) +{ + time_t now = time(NULL); + long rx_timeout; + + rx_timeout = T_io_timeout - (now - T_io_begin); + /* negative rx_timeout -> time already out */ + if (rx_timeout < 0) + return 0; + return ser_get_char(upsfd, ch, rx_timeout, 0); +} + + +/** + * get next characters from input stream + * + * @param buf ptr-to output buffer + * @param len buffer length + * + * @return -1 (error) 0 (timeout) >0 (no. of characters actually read) + * + */ +static ssize_t get_buf(byte_t *buf, size_t len) +{ + time_t now = time(NULL); + long rx_timeout; + + rx_timeout = T_io_timeout - (now - T_io_begin); + /* negative rx_timeout -> time already out */ + if (rx_timeout < 0) + return 0; + return ser_get_buf_len(upsfd, buf, len, rx_timeout, 0); +} + +/** + * scan incoming bytes for specific character + * + * @return 0 (got it) -1 (error) + */ +static int scan_for(char c) +{ + char in; + ssize_t err; + + while (1) { + err = get_char(&in); + if (err==-1 || err==0 /*timeout*/) + return -1; + + if (in==c) + break; + } + + return 0; +} + + +/** + * receive 'activate command' ACK from PRS + * + * @return 0 (ok) -1 (error) + */ +static int recv_command_ack() +{ + ssize_t err; + raw_data_t ack; + byte_t ack_buf[8]; + + /* 1: STX */ + err = scan_for(STX); + if (err==-1) + return -1; + + + raw_alloc_onstack(&ack, ack_buf); + *(ack.end++) = STX; + + + /* 2: ID1 ID2 STAMP MSG_TYPE ACK ETX BCC */ + err = get_buf(ack.end, 7); + if (err!=7) + return -1; + + ack.end += 7; + + /* frame constructed - let's verify it */ + assert (ack.end - ack.begin >= 0); + upsdebug_ascii(3, "rx (ack):\t\t", ack.begin, (size_t)(ack.end - ack.begin)); + + /* generic layout */ + err = comli_check_frame(ack); + if (err==-1) + return -1; + + /* shrink frame */ + ack.begin += 1; + ack.end -= 2; + + return al_check_ack(ack); +} + +/** + * receive 'read register' data from PRS + * @param io [out] io header of received data + * @param io_buf [in] [out] where to place incoming data + * + * @return 0 (ok) -1 (error) + */ +static int recv_register_data(io_head_t *io, raw_data_t *io_buf) +{ + ssize_t err; + int ret; + raw_data_t reply_head; + raw_data_t reply; + + byte_t reply_head_buf[11]; + + /* 1: STX */ + err = scan_for(STX); + if (err==-1) + return -1; + + raw_alloc_onstack(&reply_head, reply_head_buf); + *(reply_head.end++) = STX; + + + /* 2: ID1 ID2 STAMP MSG_TYPE ADDR1 ADDR2 ADDR3 ADDR4 LEN1 LEN2 */ + err = get_buf(reply_head.end, 10); + if (err!=10) + return -1; + + reply_head.end += 10; + + assert (reply_head.end - reply_head.begin >= 0); + upsdebug_ascii(3, "rx (head):\t", reply_head.begin, (size_t)(reply_head.end - reply_head.begin)); + + + /* 3: check header, extract IO info */ + reply_head.begin += 1; /* temporarily strip STX */ + + err = al_parse_reply_head(io, reply_head); + if (err==-1) + return -1; + + reply_head.begin -= 1; /* restore STX */ + + upsdebugx(4, "\t\t--> addr: 0x%zx len: 0x%zx", io->addr, io->len); + + /* 4: allocate space for full reply and copy header there */ + reply = raw_xmalloc(11/*head*/ + io->len/*data*/ + 2/*ETX BCC*/); + + assert (reply_head.end - reply_head.begin >= 0); + size_t reply_head_len = (size_t)(reply_head.end - reply_head.begin); + + memcpy(reply.end, reply_head.begin, reply_head_len); + reply.end += reply_head_len; + + /* 5: receive tail of the frame */ + err = get_buf(reply.end, io->len + 2); + if (err!=(int)(io->len+2)) { + upsdebugx(4, "rx_tail failed, err=%zi (!= %zi)", err, io->len+2); + ret = -1; goto out; + } + + reply.end += io->len + 2; + + + /* frame constructed, let's verify it */ + assert (reply.end - reply.begin >= 0); + upsdebug_ascii(3, "rx (head+data):\t", reply.begin, (size_t)(reply.end - reply.begin)); + + /* generic layout */ + err = comli_check_frame(reply); + if (err==-1) { + upsdebugx(3, "%s: corrupt frame", __func__); + ret = -1; goto out; + } + + /* shrink frame */ + reply.begin += 1; + reply.end -= 2; + + + /* XXX: a bit of processing duplication here */ + ret = al_parse_reply(io, io_buf, reply); + +out: + raw_free(&reply); + return ret; +} + + +/*****************************************************************/ + +/********************* + * AL175: DO COMMAND * + *********************/ + +/** + * do 'ACTIVATE COMMAND' + * + * @return 0 (ok) -1 (error) + */ +static int al175_do(byte_t cmd, byte_t subcmd, uint16_t pr1, uint16_t pr2, uint16_t pr3) +{ + int err; + raw_data_t CTRL_frame; + byte_t CTRL_frame_buf[512]; + + raw_alloc_onstack(&CTRL_frame, CTRL_frame_buf); + al_prep_activate(&CTRL_frame, cmd, subcmd, pr1, pr2, pr3); + + flush_rx_queue(); /* DROP */ + + err = tx("tx (ctrl):\t", CTRL_frame); /* TX */ + if (err==-1) + return -1; + + + return recv_command_ack(); /* RX */ +} + + +/** + * 'READ REGISTER' + * + */ +static int al175_read(byte_t *dst, size_t addr, size_t count) +{ + int err; + raw_data_t REQ_frame; + raw_data_t rx_data; + io_head_t io; + + byte_t REQ_frame_buf[512]; + + raw_alloc_onstack(&REQ_frame, REQ_frame_buf); + al_prep_read_req(&REQ_frame, addr, count); + + flush_rx_queue(); /* DROP */ + + err = tx("tx (req):\t", REQ_frame); /* TX */ + if (err==-1) + return -1; + + + rx_data.buf = dst; + rx_data.buf_size = count; + rx_data.begin = dst; + rx_data.end = dst; + + err = recv_register_data(&io, &rx_data); + if (err==-1) + return -1; + + if ((rx_data.end - rx_data.begin) < 0 || + (size_t)(rx_data.end - rx_data.begin) != count) + return -1; + + if ( (io.addr != addr) || (io.len != count) ) { + upsdebugx(3, "%s: io_head mismatch\t(%zx,%zx != %zx,%zx)", + __func__, io.addr, io.len, addr, count); + return -1; + } + + + return 0; +} + +/************* + * NUT STUFF * + *************/ + +/**************************** + * ACTIVATE COMMANDS table + * + * see 8. ACTIVATE COMMANDS + */ + +typedef uint16_t mm_t; /* minutes */ +typedef uint16_t VV_t; /* voltage */ + +#define Z1 , 0 +#define Z2 , 0, 0 +#define Z3 , 0, 0, 0 + +#define ACT int + +/* Declare to keep compiler happy even if some routines below are not used currently */ +ACT TOGGLE_PRS_ONOFF (void); +ACT CANCEL_BOOST (void); +ACT STOP_BATTERY_TEST (void); +ACT START_BATTERY_TEST (VV_t EndVolt, mm_t Minutes); +ACT SET_FLOAT_VOLTAGE (VV_t v); +ACT SET_BOOST_VOLTAGE (VV_t v); +ACT SET_HIGH_BATTERY_LIMIT (VV_t Vhigh); +ACT SET_LOW_BATTERY_LIMIT (VV_t Vlow); +ACT SET_DISCONNECT_LEVEL_AND_DELAY (VV_t level, mm_t delay); +ACT RESET_ALARMS (void); +ACT CHANGE_COMM_PROTOCOL (void); +ACT SET_VOLTAGE_AT_ZERO_T (VV_t v); +ACT SET_SLOPE_AT_ZERO_T (VV_t mv_per_degree); +ACT SET_MAX_TCOMP_VOLTAGE (VV_t v); +ACT SET_MIN_TCOMP_VOLTAGE (VV_t v); +ACT SWITCH_TEMP_COMP (uint16_t on); +ACT SWITCH_SYM_ALARM (void); + +/* Implement */ +ACT TOGGLE_PRS_ONOFF () { return al175_do(0x81, 0x80 Z3); } +ACT CANCEL_BOOST () { return al175_do(0x82, 0x80 Z3); } +ACT STOP_BATTERY_TEST () { return al175_do(0x83, 0x80 Z3); } +ACT START_BATTERY_TEST (VV_t EndVolt, mm_t Minutes) + { return al175_do(0x83, 0x81, EndVolt, Minutes Z1); } + +ACT SET_FLOAT_VOLTAGE (VV_t v) { return al175_do(0x87, 0x80, v Z2); } +ACT SET_BOOST_VOLTAGE (VV_t v) { return al175_do(0x87, 0x81, v Z2); } +ACT SET_HIGH_BATTERY_LIMIT (VV_t Vhigh) { return al175_do(0x87, 0x82, Vhigh Z2); } +ACT SET_LOW_BATTERY_LIMIT (VV_t Vlow) { return al175_do(0x87, 0x83, Vlow Z2); } + +ACT SET_DISCONNECT_LEVEL_AND_DELAY + (VV_t level, mm_t delay) + { return al175_do(0x87, 0x84, level, delay Z1); } + +ACT RESET_ALARMS () { return al175_do(0x88, 0x80 Z3); } +ACT CHANGE_COMM_PROTOCOL () { return al175_do(0x89, 0x80 Z3); } +ACT SET_VOLTAGE_AT_ZERO_T (VV_t v) { return al175_do(0x8a, 0x80, v Z2); } +ACT SET_SLOPE_AT_ZERO_T (VV_t mv_per_degree) + { return al175_do(0x8a, 0x81, mv_per_degree Z2); } + +ACT SET_MAX_TCOMP_VOLTAGE (VV_t v) { return al175_do(0x8a, 0x82, v Z2); } +ACT SET_MIN_TCOMP_VOLTAGE (VV_t v) { return al175_do(0x8a, 0x83, v Z2); } +ACT SWITCH_TEMP_COMP (uint16_t on) { return al175_do(0x8b, 0x80, on Z2); } + +ACT SWITCH_SYM_ALARM () { return al175_do(0x8c, 0x80 Z3); } + + +/** + * extract double value from a word + */ +static double d16(byte_t data[2]) +{ + return (data[1] + 0x100*data[0]) / 100.0; +} + +void upsdrv_updateinfo(void) +{ + /* int flags; */ + + byte_t x4000[9]; /* registers from 0x4000 to 0x4040 inclusive */ + byte_t x4048[2]; /* 0x4048 - 0x4050 */ + byte_t x4100[8]; /* 0x4100 - 0x4138 */ + byte_t x4180[8]; /* 0x4180 - 0x41b8 */ + byte_t x4300[2]; /* 0x4300 - 0x4308 */ + int err; + + double batt_current = 0.0; + + + upsdebugx(4, " "); + upsdebugx(4, "UPDATEINFO"); + upsdebugx(4, "----------"); + io_new_transaction(/*timeout=*/3); + +#define RECV(reg) do { \ + err = al175_read(x ## reg, 0x ## reg, sizeof(x ## reg)); \ + if (err==-1) { \ + dstate_datastale(); \ + return; \ + } \ +} while (0) + + RECV(4000); + RECV(4048); + RECV(4100); + RECV(4180); + RECV(4300); + + + status_init(); + + /* XXX non conformant with NUT naming & not well understood what they mean */ +#if 0 + /* 0x4000 DIGITAL INPUT 1-8 */ + dstate_setinfo("load.fuse", (x4000[0] & 0x80) ? "OK" : "BLOWN"); + dstate_setinfo("battery.fuse", (x4000[0] & 0x40) ? "OK" : "BLOWN"); + dstate_setinfo("symalarm.fuse", (x4000[0] & 0x20) ? "OK" : "BLOWN"); + + /* 0x4008 BATTERY INFORMATION */ + dstate_setinfo("battery.contactor", (x4000[1] & 0x80) ? "XX" : "YY"); /* FIXME */ + dstate_setinfo("load.contactor", (x4000[1] & 0x40) ? "XX" : "YY"); /* FIXME */ + dstate_setinfo("lvd.contactor", (x4000[1] & 0x20) ? "XX" : "YY"); /* FIXME */ +#endif + if (x4000[0] & 0x40){ + dstate_setinfo("battery.fuse", "FAIL"); + status_set("RB"); + }else{ + dstate_setinfo("battery.fuse", "OK"); + } + + if (x4000[0] & 0x20){ + dstate_setinfo("battery.symmetry", "FAIL"); + status_set("RB"); + }else{ + dstate_setinfo("battery.symmetry", "OK"); + } + + if (x4000[1] & 0x01) /* battery test running */ + status_set("TEST"); + + /* TODO: others from 0x4008 */ + + /* 0x4010 NOT USED */ + /* 0x4018 NOT USED */ + + switch (x4000[4]) { /* 0x4020 MAINS VOLTAGE STATUS */ + case 0: status_set("OL"); break; + case 1: status_set("OB"); break; + + case 2: /* doc: "not applicable" */ + default: + upsdebugx(2, "%s: invalid mains voltage status\t(%i)", __func__, x4000[4]); + } + + /* 0x4028 SYSTEM ON OFF STATUS */ + switch (x4000[5]) { + case 0: /* system on */ break; + case 1: status_set("OFF"); break; + + default: + upsdebugx(2, "%s: invalid system on/off status\t(%i)", __func__, x4000[5]); + } + + switch (x4000[6]) { /* 0x4030 BATTERY TEST FAIL */ + case 0: dstate_setinfo("ups.test.result", "OK"); + break; + + case 1: status_set("RB"); + dstate_setinfo("ups.test.result", "FAIL"); + break; + + default: + upsdebugx(2, "%s: invalid battery test fail\t(%i)", __func__, x4000[6]); + } + switch (x4000[7]) { /* 0x4038 BATTERY VOLTAGE STATUS */ + case 0: /* normal */ break; + case 1: status_set("LB"); break; + case 2: status_set("HB"); break; + + default: + upsdebugx(2, "%s: invalid battery voltage status\t(%i)", __func__, x4000[7]); + } + switch (x4000[8]) { /* 0x4040 POS./NEG. BATT. CURRENT */ + case 0: batt_current = +1.0; break; /* positive */ + case 1: batt_current = -1.0; break; /* negative */ + + default: + upsdebugx(2, "%s: invalid pos/neg battery current\t(%i)", __func__, x4000[8]); + } + + switch (x4048[0]) { /* 0x4048 BOOST STATUS */ + case 0: /* no boost */; break; + case 1: status_set("BOOST"); break; + + default: + upsdebugx(2, "%s: invalid boost status\t(%i)", __func__, x4048[0]); + } + + { + const char *v=NULL; + + switch (x4048[1]) { /* 0x4050 SYSTEM VOLTAGE STAT. */ + case 0: v = "48"; break; + case 1: v = "24"; break; + case 2: v = "12"; break; + case 3: v = "26"; break; + case 4: v = "60"; break; + + default: + upsdebugx(2, "%s: invalid system voltage status\t(%i)", __func__, x4048[1]); + } + + if (v) + dstate_setinfo("output.voltage.nominal", "%s", v); + } + + + /* 0x4100 BATTERY VOLTAGE REF */ + dstate_setinfo("battery.voltage.nominal", "%.2f", d16(x4100+0)); + + /* 0x4110 BOOST VOLTAGE REF */ + dstate_setinfo("input.transfer.boost.low", "%.2f", d16(x4100+2)); /* XXX: boost.high ? */ + + /* 0x4120 HIGH BATT VOLT REF XXX */ + /* 0x4130 LOW BATT VOLT REF XXX */ + + /* 0x4180 FLOAT VOLTAGE XXX */ + /* 0x4190 BATT CURRENT */ + batt_current *= d16(x4180+2); + dstate_setinfo("battery.current", "%.2f", batt_current); + + /* 0x41b0 LOAD CURRENT (output.current in NUT) */ + dstate_setinfo("output.current", "%.2f", d16(x4180+6)); + + /* 0x4300 BATTERY TEMPERATURE */ + dstate_setinfo("battery.temperature", "%.2f", d16(x4300+0)); + + + status_commit(); + + upsdebugx(1, "STATUS: %s", dstate_getinfo("ups.status")); + dstate_dataok(); + + + /* out: */ + return; + +} + +void upsdrv_shutdown(void) + __attribute__((noreturn)); + +void upsdrv_shutdown(void) +{ + /* TODO use TOGGLE_PRS_ONOFF for shutdown */ + + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ + + /* maybe try to detect the UPS here, but try a shutdown even if + it doesn't respond at first if possible */ + + /* replace with a proper shutdown function */ + fatalx(EXIT_FAILURE, "shutdown not supported"); + + /* you may have to check the line status since the commands + for toggling power are frequently different for OL vs. OB */ + + /* OL: this must power cycle the load if possible */ + + /* OB: the load must remain off until the power returns */ +} + + +static int instcmd(const char *cmdname, const char *extra) +{ + int err; + + upsdebugx(1, "INSTCMD: %s", cmdname); + + io_new_transaction(/*timeout=*/5); + + /* + * test.battery.start + * test.battery.stop + */ + + if (!strcasecmp(cmdname, "test.battery.start")) { + err = START_BATTERY_TEST(24, 1); + return (!err ? STAT_INSTCMD_HANDLED : STAT_INSTCMD_FAILED); + } + + if (!strcasecmp(cmdname, "test.battery.stop")) { + err = STOP_BATTERY_TEST(); + return (!err ? STAT_INSTCMD_HANDLED : STAT_INSTCMD_FAILED); + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; +} + +/* no help */ +void upsdrv_help(void) +{ +} + +/* no -x flags */ +void upsdrv_makevartable(void) +{ +} + +void upsdrv_initups(void) +{ + upsfd = ser_open(device_path); + ser_set_speed(upsfd, device_path, B9600); + + ser_disable_flow_control(); +} + +void upsdrv_cleanup(void) +{ + ser_close(upsfd, device_path); +} + + +void upsdrv_initinfo(void) +{ + /* TODO issue short io with UPS to detect it's presence */ + /* try to detect the UPS here - call fatal_with_errno(EXIT_FAILURE, ) if it fails */ + + dstate_setinfo("ups.mfr", "Eltek"); + dstate_setinfo("ups.model", "AL175"); + /* ... */ + + /* instant commands */ + dstate_addcmd ("test.battery.start"); + dstate_addcmd ("test.battery.stop"); + /* TODO rest instcmd(s) */ + + upsh.instcmd = instcmd; +} diff --git a/drivers/apc-ats-mib.c b/drivers/apc-ats-mib.c new file mode 100644 index 0000000..56a6779 --- /dev/null +++ b/drivers/apc-ats-mib.c @@ -0,0 +1,453 @@ +/* apcats-mib.c - subdriver to monitor apcats SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2012 Arnaud Quette + * 2016 Arnaud Quette + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-snmp-subdriver script. It must be customized! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "apc-ats-mib.h" + +#define APC_ATS_MIB_VERSION "0.6" + +#define APC_ATS_SYSOID ".1.3.6.1.4.1.318.1.3.11" +#define APC_ATS_OID_MODEL_NAME ".1.3.6.1.4.1.318.1.1.8.1.5.0" + +static info_lkp_t apc_ats_sensitivity_info[] = { + { 1, "high", NULL, NULL }, + { 2, "low", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t apc_ats_output_status_info[] = { + { 1, "OFF", NULL, NULL }, /* fail */ + { 2, "OL", NULL, NULL }, /* ok */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t apc_ats_outletgroups_name_info[] = { + { 1, "total", NULL, NULL }, + { 2, "bank1", NULL, NULL }, + { 3, "bank2", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t apc_ats_outletgroups_status_info[] = { + { 1, "OL", NULL, NULL }, /* normal */ + { 2, "", NULL, NULL }, /* lowload */ + { 3, "", NULL, NULL }, /* nearoverload */ + { 4, "OVER", NULL, NULL }, /* overload */ + { 0, NULL, NULL, NULL } +}; + +/* APC ATS Snmp2NUT lookup table */ +static snmp_info_t apc_ats_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* Device collection */ + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* ats2IdentManufacturer.0 = STRING: EATON */ + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* atsIdentModelNumber.0 = STRING: "AP7724" */ + { "device.model", ST_FLAG_STRING, SU_INFOSIZE, APC_ATS_OID_MODEL_NAME, NULL, SU_FLAG_OK, NULL }, + /* FIXME: RFC for device.firmware! */ + /* atsIdentHardwareRev.0 = STRING: "R01" */ + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* FIXME: RFC for device.firmware.aux! */ + /* atsIdentFirmwareRev.0 = STRING: "3.0.5" */ + { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.2.0", NULL, SU_FLAG_OK, NULL }, + /* atsIdentFirmwareDate.0 = STRING: "09/13/11" */ + /*{ "unmapped.atsIdentFirmwareDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.3.0", NULL, SU_FLAG_OK, NULL },*/ + /* atsIdentSerialNumber.0 = STRING: "5A1516T15268" */ + { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* FIXME: RFC for device.mfr.date! */ + /* atsIdentDateOfManufacture.0 = STRING: "04/18/2015" */ + { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.1.4.0", NULL, SU_FLAG_OK, NULL }, + /* atsConfigProductName.0 = STRING: "m-ups-04" */ + { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.4.1.0", NULL, SU_FLAG_OK, NULL }, + + /* Input collection */ + /* atsIdentNominalLineVoltage.0 = INTEGER: 230 */ + { "input.voltage.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.8.1.7.0", NULL, SU_FLAG_OK, NULL }, + /* atsIdentNominalLineFrequency.0 = INTEGER: 50 */ + { "input.frequency.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.8.1.8.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatusSelectedSource.0 = INTEGER: sourceB(2) */ + { "input.source", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.2.0", NULL, SU_FLAG_OK, NULL }, + /* atsConfigPreferredSource.0 = INTEGER: sourceB(2) */ + { "input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.318.1.1.8.4.2.0", NULL, SU_FLAG_OK, NULL }, + /* atsInputVoltage.1.1.1 = INTEGER: 216 */ + { "input.1.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.3.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputVoltage.2.1.1 = INTEGER: 215 */ + { "input.2.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.3.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputFrequency.1 = INTEGER: 50 */ + { "input.1.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputFrequency.2 = INTEGER: 50 */ + { "input.2.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + /* atsConfigVoltageSensitivity.0 = INTEGER: high(1) */ + { "input.sensitivity", ST_FLAG_RW, 1, ".1.3.6.1.4.1.318.1.1.8.4.4.0", NULL, SU_FLAG_OK, &apc_ats_sensitivity_info[0] }, + /* FIXME: RFC for input.count! */ + /* atsNumInputs.0 = INTEGER: 2 */ + { "input.count", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.1.0", NULL, SU_FLAG_OK, NULL }, + + /* Output collection */ + /* atsOutputFrequency.1 = INTEGER: 50 */ + { "output.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankOutputVoltage.1 = INTEGER: 215 */ + { "output.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.6.1", NULL, SU_FLAG_OK, NULL }, + + /* UPS collection */ + /* FIXME: RFC for device.status! */ + /* atsStatusVoltageOutStatus.0 = INTEGER: ok(2) */ + { "ups.status", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.15.0", NULL, SU_FLAG_OK, &apc_ats_output_status_info[0] }, + + /* Outlet groups collection */ + /* Note: prefer the OutputBank data to the ConfigBank ones */ + /* atsConfigBankTableSize.0 = INTEGER: 3 */ + /*{ "outlet.group.count", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.13.0", NULL, SU_FLAG_OK, NULL },*/ + /* atsOutputBankTableSize.0 = INTEGER: 3 */ + { "outlet.group.count", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.4.0", NULL, SU_FLAG_OK, NULL }, + /* atsConfigBankTableIndex.%i = INTEGER: %i */ + /*{ "outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.4.14.1.1.%i", NULL, SU_FLAG_OK, NULL },*/ + /* atsOutputBankTableIndex.%i = INTEGER: %i */ + { "outlet.group.%i.id", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.1.%i", NULL, SU_FLAG_OK | SU_OUTLET_GROUP, NULL }, + /* atsConfigBank.%i = INTEGER: total(1) */ + /*{ "outlet.group.%i.name", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.2.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP, &apc_ats_group_name_info[0] },*/ + /* atsOutputBank.1 = INTEGER: total(1) */ + { "outlet.group.%i.name", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.3.%i", NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP, &apc_ats_outletgroups_name_info[0] }, + /* atsOutputBankCurrent.%i = Gauge32: 88 */ + { "outlet.group.%i.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.4.%i", NULL, SU_OUTLET_GROUP, NULL }, + /* atsOutputBankState.%i = INTEGER: normal(1) */ + { "outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.5.%i", NULL, SU_OUTLET_GROUP, &apc_ats_outletgroups_status_info[0] }, + /* atsOutputBankOutputVoltage.%i = INTEGER: 215 */ + { "outlet.group.%i.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.6.%i", NULL, SU_OUTLET_GROUP, NULL }, + /* atsOutputBankPower.1 = INTEGER: 1883 */ + { "outlet.group.%i.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.15.%i", NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP, NULL }, + + +#if 0 /* FIXME: Remaining data to be processed */ + /* atsIdentDeviceRating.0 = INTEGER: 32 */ + { "unmapped.atsIdentDeviceRating", 0, 1, ".1.3.6.1.4.1.318.1.1.8.1.9.0", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationNumInputs.0 = INTEGER: 2 */ + { "unmapped.atsCalibrationNumInputs", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationNumInputPhases.0 = INTEGER: 1 */ + { "unmapped.atsCalibrationNumInputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.2.0", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationInputTableIndex.1.1.1 = INTEGER: 1 */ + { "unmapped.atsCalibrationInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationInputTableIndex.2.1.1 = INTEGER: 2 */ + { "unmapped.atsCalibrationInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.1.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationInputPhaseTableIndex.1.1.1 = INTEGER: 1 */ + { "unmapped.atsCalibrationInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationInputPhaseTableIndex.2.1.1 = INTEGER: 1 */ + { "unmapped.atsCalibrationInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.2.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsLineVoltageCalibrationFactor.1.1.1 = INTEGER: 487 */ + { "unmapped.atsLineVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.3.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsLineVoltageCalibrationFactor.2.1.1 = INTEGER: 488 */ + { "unmapped.atsLineVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.1.3.1.3.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationPowerSupplyVoltages.0 = INTEGER: 5 */ + { "unmapped.atsCalibrationPowerSupplyVoltages", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.1.0", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationPowerSupplyVoltageTableIndex.1 = INTEGER: 1 */ + { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationPowerSupplyVoltageTableIndex.2 = INTEGER: 2 */ + { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationPowerSupplyVoltageTableIndex.3 = INTEGER: 3 */ + { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.3", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationPowerSupplyVoltageTableIndex.4 = INTEGER: 4 */ + { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationPowerSupplyVoltageTableIndex.5 = INTEGER: 5 */ + { "unmapped.atsCalibrationPowerSupplyVoltageTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationPowerSupplyVoltage.1 = INTEGER: powerSupply24V(1) */ + { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationPowerSupplyVoltage.2 = INTEGER: powerSupply12V(2) */ + { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationPowerSupplyVoltage.3 = INTEGER: powerSupply(3) */ + { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.3", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationPowerSupplyVoltage.4 = INTEGER: powerSupply24VSourceB(4) */ + { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.4", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationPowerSupplyVoltage.5 = INTEGER: powerSupplyMinus12V(5) */ + { "unmapped.atsCalibrationPowerSupplyVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.2.5", NULL, SU_FLAG_OK, NULL }, + /* atsPowerSupplyVoltageCalibrationFactor.1 = INTEGER: 521 */ + { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* atsPowerSupplyVoltageCalibrationFactor.2 = INTEGER: 1076 */ + { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* atsPowerSupplyVoltageCalibrationFactor.3 = INTEGER: 2560 */ + { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.3", NULL, SU_FLAG_OK, NULL }, + /* atsPowerSupplyVoltageCalibrationFactor.4 = INTEGER: 521 */ + { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.4", NULL, SU_FLAG_OK, NULL }, + /* atsPowerSupplyVoltageCalibrationFactor.5 = INTEGER: 975 */ + { "unmapped.atsPowerSupplyVoltageCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.2.2.1.3.5", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationNumOutputs.0 = INTEGER: 1 */ + { "unmapped.atsCalibrationNumOutputs", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.1.0", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationNumOutputPhases.0 = INTEGER: 1 */ + { "unmapped.atsCalibrationNumOutputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.2.0", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationOutputTableIndex.1.phase1.1 = INTEGER: 1 */ + { "unmapped.atsCalibrationOutputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsCalibrationOutputPhasesTableIndex.1.phase1.1 = INTEGER: phase1(1) */ + { "unmapped.atsCalibrationOutputPhasesTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.3.1.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputCurrentCalibrationFactor.1.phase1.1 = INTEGER: 487 */ + { "unmapped.atsOutputCurrentCalibrationFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.8.2.3.3.1.3.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsControlResetATS.0 = INTEGER: none(1) */ + { "unmapped.atsControlResetATS", 0, 1, ".1.3.6.1.4.1.318.1.1.8.3.1.0", NULL, SU_FLAG_OK, NULL }, + /* atsControlClearAllAlarms.0 = INTEGER: -1 */ + { "unmapped.atsControlClearAllAlarms", 0, 1, ".1.3.6.1.4.1.318.1.1.8.3.2.0", NULL, SU_FLAG_OK, NULL }, + + /* atsConfigFrontPanelLockout.0 = INTEGER: enableFrontPanel(2) */ + { "unmapped.atsConfigFrontPanelLockout", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.3.0", NULL, SU_FLAG_OK, NULL }, + + /* atsConfigTransferVoltageRange.0 = INTEGER: medium(2) */ + { "unmapped.atsConfigTransferVoltageRange", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.5.0", NULL, SU_FLAG_OK, NULL }, + /* atsConfigCurrentLimit.0 = INTEGER: 32 */ + { "unmapped.atsConfigCurrentLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.6.0", NULL, SU_FLAG_OK, NULL }, + /* atsConfigResetValues.0 = INTEGER: -1 */ + { "unmapped.atsConfigResetValues", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.7.0", NULL, SU_FLAG_OK, NULL }, + /* atsConfigLineVRMS.0 = INTEGER: 230 */ + { "unmapped.atsConfigLineVRMS", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.8.0", NULL, SU_FLAG_OK, NULL }, + /* atsConfigLineVRMSNarrowLimit.0 = INTEGER: 16 */ + { "unmapped.atsConfigLineVRMSNarrowLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.9.0", NULL, SU_FLAG_OK, NULL }, + /* atsConfigLineVRMSMediumLimit.0 = INTEGER: 23 */ + { "unmapped.atsConfigLineVRMSMediumLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.10.0", NULL, SU_FLAG_OK, NULL }, + /* atsConfigLineVRMSWideLimit.0 = INTEGER: 30 */ + { "unmapped.atsConfigLineVRMSWideLimit", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.11.0", NULL, SU_FLAG_OK, NULL }, + /* atsConfigFrequencyDeviation.0 = INTEGER: two(2) */ + { "unmapped.atsConfigFrequencyDeviation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.12.0", NULL, SU_FLAG_OK, NULL }, + + /* Outlet groups collection */ + /* atsConfigBankLowLoadThreshold.1 = INTEGER: 0 */ + { "unmapped.atsConfigBankLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* atsConfigBankLowLoadThreshold.2 = INTEGER: 0 */ + { "unmapped.atsConfigBankLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* atsConfigBankLowLoadThreshold.3 = INTEGER: 0 */ + { "unmapped.atsConfigBankLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.3.3", NULL, SU_FLAG_OK, NULL }, + /* atsConfigBankNearOverLoadThreshold.1 = INTEGER: 28 */ + { "unmapped.atsConfigBankNearOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* atsConfigBankNearOverLoadThreshold.2 = INTEGER: 12 */ + { "unmapped.atsConfigBankNearOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.4.2", NULL, SU_FLAG_OK, NULL }, + /* atsConfigBankNearOverLoadThreshold.3 = INTEGER: 12 */ + { "unmapped.atsConfigBankNearOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.4.3", NULL, SU_FLAG_OK, NULL }, + /* atsConfigBankOverLoadThreshold.1 = INTEGER: 32 */ + { "unmapped.atsConfigBankOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* atsConfigBankOverLoadThreshold.2 = INTEGER: 16 */ + { "unmapped.atsConfigBankOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.5.2", NULL, SU_FLAG_OK, NULL }, + /* atsConfigBankOverLoadThreshold.3 = INTEGER: 16 */ + { "unmapped.atsConfigBankOverLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.14.1.5.3", NULL, SU_FLAG_OK, NULL }, + /* atsConfigPhaseTableSize.0 = INTEGER: 0 */ + { "unmapped.atsConfigPhaseTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.8.4.15.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatusCommStatus.0 = INTEGER: atsCommEstablished(2) */ + { "unmapped.atsStatusCommStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.1.0", NULL, SU_FLAG_OK, NULL }, + + /* atsStatusRedundancyState.0 = INTEGER: atsFullyRedundant(2) */ + { "unmapped.atsStatusRedundancyState", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.3.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatusOverCurrentState.0 = INTEGER: atsCurrentOK(2) */ + { "unmapped.atsStatusOverCurrentState", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.4.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatus5VPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ + { "unmapped.atsStatus5VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.5.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatus24VPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ + { "unmapped.atsStatus24VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatus24VSourceBPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ + { "unmapped.atsStatus24VSourceBPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.7.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatusPlus12VPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ + { "unmapped.atsStatusPlus12VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.8.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatusMinus12VPowerSupply.0 = INTEGER: atsPowerSupplyOK(2) */ + { "unmapped.atsStatusMinus12VPowerSupply", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.9.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatusSwitchStatus.0 = INTEGER: ok(2) */ + { "unmapped.atsStatusSwitchStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.10.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatusFrontPanel.0 = INTEGER: unlocked(2) */ + { "unmapped.atsStatusFrontPanel", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.11.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatusSourceAStatus.0 = INTEGER: ok(2) */ + { "unmapped.atsStatusSourceAStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.12.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatusSourceBStatus.0 = INTEGER: ok(2) */ + { "unmapped.atsStatusSourceBStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.13.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatusPhaseSyncStatus.0 = INTEGER: inSync(1) */ + { "unmapped.atsStatusPhaseSyncStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.14.0", NULL, SU_FLAG_OK, NULL }, + + /* atsStatusHardwareStatus.0 = INTEGER: ok(2) */ + { "unmapped.atsStatusHardwareStatus", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.1.16.0", NULL, SU_FLAG_OK, NULL }, + /* atsStatusResetMaxMinValues.0 = INTEGER: -1 */ + { "unmapped.atsStatusResetMaxMinValues", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.2.1.0", NULL, SU_FLAG_OK, NULL }, + + /* atsInputTableIndex.1 = INTEGER: 1 */ + { "unmapped.atsInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputTableIndex.2 = INTEGER: 2 */ + { "unmapped.atsInputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* atsNumInputPhases.1 = INTEGER: 1 */ + { "unmapped.atsNumInputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* atsNumInputPhases.2 = INTEGER: 1 */ + { "unmapped.atsNumInputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* atsInputVoltageOrientation.1 = INTEGER: singlePhase(2) */ + { "unmapped.atsInputVoltageOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputVoltageOrientation.2 = INTEGER: singlePhase(2) */ + { "unmapped.atsInputVoltageOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + + /* atsInputType.1 = INTEGER: main(2) */ + { "unmapped.atsInputType", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputType.2 = INTEGER: main(2) */ + { "unmapped.atsInputType", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.5.2", NULL, SU_FLAG_OK, NULL }, + /* atsInputName.1 = STRING: "Source A" */ + { "unmapped.atsInputName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.6.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputName.2 = STRING: "Source B" */ + { "unmapped.atsInputName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.8.5.3.2.1.6.2", NULL, SU_FLAG_OK, NULL }, + /* atsInputPhaseTableIndex.1.1.1 = INTEGER: 1 */ + { "unmapped.atsInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputPhaseTableIndex.2.1.1 = INTEGER: 2 */ + { "unmapped.atsInputPhaseTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.1.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputPhaseIndex.1.1.1 = INTEGER: 1 */ + { "unmapped.atsInputPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputPhaseIndex.2.1.1 = INTEGER: 1 */ + { "unmapped.atsInputPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.2.2.1.1", NULL, SU_FLAG_OK, NULL }, + + /* atsInputMaxVoltage.1.1.1 = INTEGER: -1 */ + { "unmapped.atsInputMaxVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputMaxVoltage.2.1.1 = INTEGER: -1 */ + { "unmapped.atsInputMaxVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.4.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputMinVoltage.1.1.1 = INTEGER: -1 */ + { "unmapped.atsInputMinVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.5.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputMinVoltage.2.1.1 = INTEGER: -1 */ + { "unmapped.atsInputMinVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.5.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputCurrent.1.1.1 = INTEGER: -1 */ + { "unmapped.atsInputCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.6.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputCurrent.2.1.1 = INTEGER: -1 */ + { "unmapped.atsInputCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.6.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputMaxCurrent.1.1.1 = INTEGER: -1 */ + { "unmapped.atsInputMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.7.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputMaxCurrent.2.1.1 = INTEGER: -1 */ + { "unmapped.atsInputMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.7.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputMinCurrent.1.1.1 = INTEGER: -1 */ + { "unmapped.atsInputMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.8.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputMinCurrent.2.1.1 = INTEGER: -1 */ + { "unmapped.atsInputMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.8.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputPower.1.1.1 = INTEGER: -1 */ + { "unmapped.atsInputPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.9.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputPower.2.1.1 = INTEGER: -1 */ + { "unmapped.atsInputPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.9.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputMaxPower.1.1.1 = INTEGER: -1 */ + { "unmapped.atsInputMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.10.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputMaxPower.2.1.1 = INTEGER: -1 */ + { "unmapped.atsInputMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.10.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputMinPower.1.1.1 = INTEGER: -1 */ + { "unmapped.atsInputMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.11.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsInputMinPower.2.1.1 = INTEGER: -1 */ + { "unmapped.atsInputMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.3.3.1.11.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsNumOutputs.0 = INTEGER: 1 */ + { "unmapped.atsNumOutputs", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.1.0", NULL, SU_FLAG_OK, NULL }, + /* atsOutputTableIndex.1 = INTEGER: 1 */ + { "unmapped.atsOutputTableIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* atsNumOutputPhases.1 = INTEGER: 1 */ + { "unmapped.atsNumOutputPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputVoltageOrientation.1 = INTEGER: singlePhase(2) */ + { "unmapped.atsOutputVoltageOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + + /* atsOutputPhase.1 = INTEGER: phase1(1) */ + { "unmapped.atsOutputPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputPhase.2 = INTEGER: phase1(1) */ + { "unmapped.atsOutputPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputPhase.3 = INTEGER: phase1(1) */ + { "unmapped.atsOutputPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.2.3", NULL, SU_FLAG_OK, NULL }, + + /* atsOutputBankMaxCurrent.1 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.7.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMaxCurrent.2 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.7.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMaxCurrent.3 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.7.3", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinCurrent.1 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.8.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinCurrent.2 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.8.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinCurrent.3 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinCurrent", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.8.3", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankLoad.1 = INTEGER: 1883 */ + { "unmapped.atsOutputBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.9.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankLoad.2 = INTEGER: 984 */ + { "unmapped.atsOutputBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.9.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankLoad.3 = INTEGER: 898 */ + { "unmapped.atsOutputBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.9.3", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMaxLoad.1 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.10.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMaxLoad.2 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.10.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMaxLoad.3 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.10.3", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinLoad.1 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.11.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinLoad.2 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.11.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinLoad.3 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.11.3", NULL, SU_FLAG_OK, NULL }, + + /* atsOutputBankPercentLoad.1 = INTEGER: 25 */ + { "unmapped.atsOutputBankPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.12.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankPercentLoad.2 = INTEGER: 13 */ + { "unmapped.atsOutputBankPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.12.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankPercentLoad.3 = INTEGER: 12 */ + { "unmapped.atsOutputBankPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.12.3", NULL, SU_FLAG_OK, NULL }, + + /* atsOutputBankMaxPercentLoad.1 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.13.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMaxPercentLoad.2 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.13.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMaxPercentLoad.3 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.13.3", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinPercentLoad.1 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.14.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinPercentLoad.2 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.14.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinPercentLoad.3 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinPercentLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.14.3", NULL, SU_FLAG_OK, NULL }, + + /* atsOutputBankMaxPower.1 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.16.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMaxPower.2 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.16.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMaxPower.3 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.16.3", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinPower.1 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.17.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinPower.2 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.17.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinPower.3 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.17.3", NULL, SU_FLAG_OK, NULL }, + + /* atsOutputBankPercentPower.1 = INTEGER: 25 */ + { "unmapped.atsOutputBankPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.18.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankPercentPower.2 = INTEGER: 13 */ + { "unmapped.atsOutputBankPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.18.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankPercentPower.3 = INTEGER: 12 */ + { "unmapped.atsOutputBankPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.18.3", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMaxPercentPower.1 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.19.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMaxPercentPower.2 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.19.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMaxPercentPower.3 = INTEGER: -1 */ + { "unmapped.atsOutputBankMaxPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.19.3", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinPercentPower.1 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.20.1", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinPercentPower.2 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.20.2", NULL, SU_FLAG_OK, NULL }, + /* atsOutputBankMinPercentPower.3 = INTEGER: -1 */ + { "unmapped.atsOutputBankMinPercentPower", 0, 1, ".1.3.6.1.4.1.318.1.1.8.5.4.5.1.20.3", NULL, SU_FLAG_OK, NULL }, +#endif /* 0 */ + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + +mib2nut_info_t apc_ats = { "apc_ats", APC_ATS_MIB_VERSION, NULL, APC_ATS_OID_MODEL_NAME, apc_ats_mib, APC_ATS_SYSOID, NULL }; diff --git a/drivers/apc-ats-mib.h b/drivers/apc-ats-mib.h new file mode 100644 index 0000000..7a44a04 --- /dev/null +++ b/drivers/apc-ats-mib.h @@ -0,0 +1,29 @@ +/* apcats-mib.h - subdriver to monitor apcats SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2012 Arnaud Quette + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef APCATS_MIB_H +#define APCATS_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t apc_ats; + +#endif /* APCATS_MIB_H */ diff --git a/drivers/apc-hid.c b/drivers/apc-hid.c index f76621f..36afaf1 100644 --- a/drivers/apc-hid.c +++ b/drivers/apc-hid.c @@ -27,34 +27,76 @@ */ #include "main.h" /* for getval() */ +#include "hidparser.h" /* for FindObject_with_ID_Node() */ #include "usbhid-ups.h" #include "apc-hid.h" #include "usb-common.h" -#define APC_HID_VERSION "APC HID 0.95" +#define APC_HID_VERSION "APC HID 0.98" /* APC */ #define APC_VENDORID 0x051d +/* Tweaks */ +static char * tweak_max_report[] = { + /* Back-UPS ES 700 does NOT overflow. */ + /* Back-UPS ES 725 does NOT overflow. */ + /* Back-UPS ES 525 overflows on ReportID 0x0c + (UPS.PowerSummary.RemainingCapacity). */ + "Back-UPS ES 525", + /* Back-UPS CS 650 overflows on ReportID 0x46 */ + "Back-UPS CS", + NULL}; + /* Don't use interrupt pipe on 5G models (used by proprietary protocol) */ -static void *disable_interrupt_pipe(void) +static void *disable_interrupt_pipe(USBDevice_t *device) { + NUT_UNUSED_VARIABLE(device); + if (use_interrupt_pipe == TRUE) { + /* FIXME? Suggest data from "device" to help the setup below? */ upslogx(LOG_INFO, "interrupt pipe disabled (add 'pollonly' flag to 'ups.conf' to get rid of this message)"); use_interrupt_pipe= FALSE; } return NULL; } +/* Some models need special tweaks */ +static void *general_apc_check(USBDevice_t *device) +{ + int i = 0; + + if (!device->Product) { + upslogx(LOG_WARNING, "device->Product is NULL so it is not possible to determine whether to activate max_report_size workaround"); + return NULL; + } + + /* Some models of Back-UPS overflow on some ReportID. + * This results in some data not being exposed and IO errors on + * WIN32, causing endless reconnection or driver's failure */ + + while( tweak_max_report[i] != NULL ) { + if(!strncmp(device->Product, tweak_max_report[i], + strlen(tweak_max_report[i]))) { + max_report_size = 1; + return NULL; + } + i++; + } + return NULL; +} + /* USB IDs device table */ static usb_device_id_t apc_usb_device_table[] = { + /* APC AP9584 Serial->USB kit */ + { USB_DEVICE(APC_VENDORID, 0x0000), NULL }, /* various models */ - { USB_DEVICE(APC_VENDORID, 0x0002), NULL }, + { USB_DEVICE(APC_VENDORID, 0x0002), general_apc_check }, /* various 5G models */ { USB_DEVICE(APC_VENDORID, 0x0003), disable_interrupt_pipe }, /* Terminating entry */ - { -1, -1, NULL } + { 0, 0, NULL } }; /* returns statically allocated string - must not use it again before @@ -86,53 +128,69 @@ static const char *apc_date_conversion_fun(double value) return buf; } -info_lkp_t apc_date_conversion[] = { - { 0, NULL, apc_date_conversion_fun } +static double apc_date_conversion_reverse(const char *date_string) +{ + int year, month, day; + long date; + + sscanf(date_string, "%04d/%02d/%02d", &year, &month, &day); + if(year >= 2070 || month > 12 || day > 31) + return 0; + year %= 100; + date = ((year / 10 & 0x0F) << 4) + (year % 10); + date += ((month / 10 & 0x0F) << 20) + ((month % 10) << 16); + date += ((day / 10 & 0x0F) << 12) + ((day % 10) << 8); + + return (double) date; +} + +static info_lkp_t apc_date_conversion[] = { + { 0, NULL, apc_date_conversion_fun, apc_date_conversion_reverse } }; /* This was determined empirically from observing a BackUPS LS 500 */ static info_lkp_t apcstatusflag_info[] = { - { 8, "!off", NULL }, /* Normal operation */ - { 16, "!off", NULL }, /* This occurs briefly during power-on, and corresponds to status 'DISCHRG'. */ - { 0, "off", NULL }, - { 0, NULL, NULL } + { 8, "!off", NULL, NULL }, /* Normal operation */ + { 16, "!off", NULL, NULL }, /* This occurs briefly during power-on, and corresponds to status 'DISCHRG'. */ + { 0, "off", NULL, NULL }, + { 0, NULL, NULL, NULL } }; /* Reason of the last battery transfer (from apcupsd) */ static info_lkp_t apc_linefailcause_vrange_info[] = { - { 1, "vrange", NULL }, /* Low line voltage */ - { 2, "vrange", NULL }, /* High line voltage */ - { 4, "vrange", NULL }, /* notch, spike, or blackout */ - { 8, "vrange", NULL }, /* Notch or blackout */ - { 9, "vrange", NULL }, /* Spike or blackout */ - { 0, "!vrange", NULL }, /* No transfers have ocurred */ - { 0, NULL, NULL } + { 1, "vrange", NULL, NULL }, /* Low line voltage */ + { 2, "vrange", NULL, NULL }, /* High line voltage */ + { 4, "vrange", NULL, NULL }, /* notch, spike, or blackout */ + { 8, "vrange", NULL, NULL }, /* Notch or blackout */ + { 9, "vrange", NULL, NULL }, /* Spike or blackout */ + { 0, "!vrange", NULL, NULL }, /* No transfers have ocurred */ + { 0, NULL, NULL, NULL } }; static info_lkp_t apc_linefailcause_frange_info[] = { - { 7, "frange", NULL }, /* Input frequency out of range */ - { 0, "!frange", NULL }, /* No transfers have ocurred */ - { 0, NULL, NULL } + { 7, "frange", NULL, NULL }, /* Input frequency out of range */ + { 0, "!frange", NULL, NULL }, /* No transfers have ocurred */ + { 0, NULL, NULL, NULL } }; #if 0 /* these input.transfer.reason can't be mapped at the moment... */ - { 3, "ripple", NULL }, /* Ripple */ - { 5, "self test", NULL }, /* Self Test or Discharge Calibration commanded - * Test usage, front button, or 2 week self test */ - { 6, "forced", NULL }, /* DelayBeforeShutdown or APCDelayBeforeShutdown */ - { 10, "forced", NULL }, /* Graceful shutdown by accessories */ - { 11, "self test", NULL }, /* Test usage invoked */ - { 12, "self test", NULL }, /* Front button initiated self test */ - { 13, "self test", NULL }, /* 2 week self test */ - { 0, NULL, NULL } + { 3, "ripple", NULL, NULL }, /* Ripple */ + { 5, "self test", NULL, NULL }, /* Self Test or Discharge Calibration commanded + * Test usage, front button, or 2 week self test */ + { 6, "forced", NULL, NULL }, /* DelayBeforeShutdown or APCDelayBeforeShutdown */ + { 10, "forced", NULL, NULL }, /* Graceful shutdown by accessories */ + { 11, "self test", NULL, NULL }, /* Test usage invoked */ + { 12, "self test", NULL, NULL }, /* Front button initiated self test */ + { 13, "self test", NULL, NULL }, /* 2 week self test */ + { 0, NULL, NULL, NULL } #endif static info_lkp_t apc_sensitivity_info[] = { - { 0, "low", NULL }, - { 1, "medium", NULL }, - { 2, "high", NULL }, - { 0, NULL, NULL } + { 0, "low", NULL, NULL }, + { 1, "medium", NULL, NULL }, + { 2, "high", NULL, NULL }, + { 0, NULL, NULL, NULL } }; /* --------------------------------------------------------------- */ @@ -276,7 +334,7 @@ static hid_info_t apc_hid2nut[] = { { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.1f", 0, NULL }, /* Back-UPS 500 */ { "battery.temperature", 0, 0, "UPS.Battery.Temperature", NULL, "%s", 0, kelvin_celsius_conversion }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_conversion }, - { "battery.mfr.date", 0, 0, "UPS.Battery.ManufacturerDate", NULL, "%s", 0, date_conversion }, + { "battery.mfr.date", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Battery.ManufacturerDate", NULL, "%s", HU_FLAG_SEMI_STATIC, date_conversion }, { "battery.mfr.date", 0, 0, "UPS.PowerSummary.APCBattReplaceDate", NULL, "%s", 0, apc_date_conversion }, /* Back-UPS 500, Back-UPS ES/CyberFort 500 */ { "battery.date", 0, 0, "UPS.Battery.APCBattReplaceDate", NULL, "%s", 0, apc_date_conversion }, /* Observed values: 0x0 on Back-UPS ES 650, 0x92501 on Back-UPS BF500 whose manufacture date was 2005/01/20 - this makes little sense but at least it's a valid date. */ @@ -344,7 +402,7 @@ static hid_info_t apc_hid2nut[] = { { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Input.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.high", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Input.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.sensitivity", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Input.APCSensitivity", NULL, "%s", HU_FLAG_SEMI_STATIC, apc_sensitivity_info }, - + /* Output page */ { "output.voltage", 0, 0, "UPS.Output.Voltage", NULL, "%.1f", 0, NULL }, { "output.voltage.nominal", 0, 0, "UPS.Output.ConfigVoltage", NULL, "%.1f", 0, NULL }, @@ -357,7 +415,7 @@ static hid_info_t apc_hid2nut[] = { /* { "ambient.temperature", 0, 0, "UPS.APCEnvironment.APCProbe2.Temperature", NULL, "%.1f", 0, kelvin_celsius_conversion }, { "ambient.humidity", 0, 0, "UPS.APCEnvironment.APCProbe2.Humidity", NULL, "%.1f", 0, NULL }, - */ +*/ /* instant commands. */ /* test.* split into subset while waiting for extradata support @@ -391,7 +449,7 @@ static hid_info_t apc_hid2nut[] = { { "shutdown.reboot", 0, 0, "UPS.APCGeneralCollection.APCDelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL }, /* used by APC BackUPS CS */ { "shutdown.return", 0, 0, "UPS.Output.APCDelayBeforeReboot", NULL, "1", HU_TYPE_CMD, NULL }, - + { "beeper.on", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, { "beeper.off", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, @@ -437,8 +495,7 @@ static const char *apc_format_serial(HIDDevice_t *hd) { * the device is supported by this subdriver, else 0. */ static int apc_claim(HIDDevice_t *hd) { - int status = is_usb_device_supported(apc_usb_device_table, hd->VendorID, - hd->ProductID); + int status = is_usb_device_supported(apc_usb_device_table, hd); switch (status) { @@ -459,6 +516,73 @@ static int apc_claim(HIDDevice_t *hd) { } } +/* apc_fix_report_desc + * + * The Back-UPS XS 1400U reports incorrect logical min/max values for the + * UPS.Input.ConfigVoltage and UPS.Input.Voltage when operating in a + * 220-240V region. Detect this and fix it. + * This same fix may be applicable to other APC UPS units as well, though + * the report IDs may be different. + */ +static int apc_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { + HIDData_t *pData; + int res = 0; + + int vendorID = pDev->VendorID; + int productID = pDev->ProductID; + if (vendorID != APC_VENDORID || productID != 0x0002) { + return 0; + } + + upsdebugx(3, "Attempting Report Descriptor fix for UPS: Vendor: %04x, Product: %04x", vendorID, productID); + + /* Look at the High Voltage Transfer logical max value: + * If the HVT logmax is greater than the configured or input voltage limit + * then the configured/input voltage limits are probably incorrect. + * Arbitrarily set the input voltage logical min/max to 0 .. 2*HVT logmax and the + * configured (nominal) input voltage logical max to 255 (it's a single byte value) + + * Path: UPS.Input.ConfigVoltage, Type: Feature, ReportID: 0x30, Offset: 0, Size: 8 + * Path: UPS.Input.Voltage, Type: Feature, ReportID: 0x31, Offset: 0, Size: 16 + * Path: UPS.Input.HighVoltageTransfer, Type: Feature, ReportID: 0x33, Offset: 0, Size: 16 + */ + + if ((pData=FindObject_with_ID_Node(pDesc_arg, 0x33, USAGE_POW_HIGH_VOLTAGE_TRANSFER))) { + long hvt_logmin = pData->LogMin; + long hvt_logmax = pData->LogMax; + upsdebugx(4, "Report Descriptor: highVoltageTransfer LogMin: %ld LogMax: %ld", hvt_logmin, hvt_logmax); + + if ((pData=FindObject_with_ID_Node(pDesc_arg, 0x31, USAGE_POW_VOLTAGE))) { + long voltage_logmin = pData->LogMin; + long voltage_logmax = pData->LogMax; + upsdebugx(4, "Report Descriptor: voltage LogMin: %ld LogMax: %ld", + voltage_logmin, voltage_logmax); + + if (hvt_logmax > voltage_logmax) { + pData->LogMin = 0; /* a reasonable lower limit for voltage */ + pData->LogMax = hvt_logmax * 2; /* it may be smoking at this point */ + upsdebugx(3, "Fixing Report Descriptor. Set voltage LogMin = %ld, LogMax = %ld", + pData->LogMin , pData->LogMax); + res = 1; + } + } + if ((pData=FindObject_with_ID_Node(pDesc_arg, 0x30, USAGE_POW_CONFIG_VOLTAGE))) { + long cvoltage_logmin = pData->LogMin; + long cvoltage_logmax = pData->LogMax; + upsdebugx(4, "Report Descriptor: configVoltage LogMin: %ld LogMax: %ld", + cvoltage_logmin, cvoltage_logmax); + + if (hvt_logmax > cvoltage_logmax) { + pData->LogMax = 255; + upsdebugx(3, "Fixing Report Descriptor. Set configVoltage LogMin = %ld, LogMax = %ld", + pData->LogMin , pData->LogMax); + res = 1; + } + } + } + return res; +} + subdriver_t apc_subdriver = { APC_HID_VERSION, apc_claim, @@ -467,4 +591,5 @@ subdriver_t apc_subdriver = { apc_format_model, apc_format_mfr, apc_format_serial, + apc_fix_report_desc, }; diff --git a/drivers/apc-hid.h b/drivers/apc-hid.h index 96b2d6d..ef828df 100644 --- a/drivers/apc-hid.h +++ b/drivers/apc-hid.h @@ -1,6 +1,6 @@ /* apc-hid.h - data to monitor APC USB/HID devices with NUT * - * Copyright (C) + * Copyright (C) * 2003 - 2005 Arnaud Quette * 2005 John Stamp * 2005 Peter Selinger diff --git a/drivers/apc-iem-mib.h b/drivers/apc-iem-mib.h new file mode 100644 index 0000000..65904e6 --- /dev/null +++ b/drivers/apc-iem-mib.h @@ -0,0 +1,19 @@ +#ifndef APC_IEM_MIB_H +#define APC_IEM_MIB_H + +/* + * FIXME: The below is needed because the main driver body uses this to determine + * whether a conversion from Fahrenheit to Celsius is needed (which really should + * be solved in subdriver specific formatting functions, like we do in usbhid-ups + * This is used in both snmp-ups.c and apc.c logics. + */ + +/* IEM ambient variables */ +/* IEM: integrated environment monitor probe */ + +#define APCC_OID_IEM_TEMP ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1" +#define APCC_OID_IEM_TEMP_UNIT ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.5.1" +#define APCC_IEM_FAHRENHEIT 2 +#define APCC_OID_IEM_HUMID ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.6.1" + +#endif /* APC_IEM_MIB_H */ diff --git a/drivers/apc-mib.c b/drivers/apc-mib.c index 6b44fd6..1ac0a27 100644 --- a/drivers/apc-mib.c +++ b/drivers/apc-mib.c @@ -1,8 +1,9 @@ /* apc-mib.c - data to monitor APC SNMP devices (Powernet MIB) with NUT * - * Copyright (C) 2002-2003 - * Dmitry Frolov - * Arnaud Quette + * Copyright (C) + * 2002-2003 - Dmitry Frolov + * 2002-2012 - Arnaud Quette + * 2012 - Chew Hong Gunn (high precision values) * * Sponsored by MGE UPS SYSTEMS * @@ -25,81 +26,114 @@ #include "apc-mib.h" -#define APCC_MIB_VERSION "1.1" +#define APCC_MIB_VERSION "1.6" + +#define APC_UPS_DEVICE_MODEL ".1.3.6.1.4.1.318.1.1.1.1.1.1.0" +/* FIXME: Find a better oid_auto_check vs sysOID for this one? */ +#define APC_UPS_SYSOID APC_UPS_DEVICE_MODEL /* Other APC sysOID: - * + * * examples found on the Net and other sources: * 'enterprises.apc.products.system.smartUPS.smartUPS700' + * - from fence agents * '.1.3.6.1.4.1.318.1.3.4.5': ApcRPDU, * '.1.3.6.1.4.1.318.1.3.4.4': ApcMSP + * - from Bill Seligman + * .1.3.6.1.4.1.318.1.3.2.11 + * .1.3.6.1.4.1.318.1.3.2.12 + * .1.3.6.1.4.1.318.1.3.27 + * .1.3.6.1.4.1.318.1.3.2.7 + * .1.3.6.1.4.1.318.1.3.2.8 */ -/* TODO: find the right sysOID for this MIB +/* TODO: find the right sysOID for this MIB * Ie ".1.3.6.1.4.1.318.1.1.1" or ".1.3.6.1.4.1.318" or? */ +/* .1.3.6.1.4.1.318.1.1.1 + * enterprise^ + * apc ---------^ + * products ------^ + * hardware --------^ + * ups ---------------^ + * ref: ftp://ftp.apc.com/apc/public/software/pnetmib/mib/404/powernet404.mib + */ + /* info elements */ #define APCC_OID_BATT_STATUS ".1.3.6.1.4.1.318.1.1.1.2.1.1.0" /* Defines for APCC_OID_BATT_STATUS */ static info_lkp_t apcc_batt_info[] = { - { 1, "" }, /* unknown */ - { 2, "" }, /* batteryNormal */ - { 3, "LB" }, /* batteryLow */ - { 0, "NULL" } + { 1, "", NULL, NULL }, /* unknown */ + { 2, "", NULL, NULL }, /* batteryNormal */ + { 3, "LB", NULL, NULL }, /* batteryLow */ + { 0, NULL, NULL, NULL } } ; #define APCC_OID_POWER_STATUS ".1.3.6.1.4.1.318.1.1.1.4.1.1.0" /* Defines for APCC_OID_POWER_STATUS */ static info_lkp_t apcc_pwr_info[] = { - { 1, "" }, /* unknown */ - { 2, "OL" }, /* onLine */ - { 3, "OB" }, /* onBattery */ - { 4, "BOOST" }, /* onSmartBoost */ - { 5, "OFF" }, /* timedSleeping */ - { 6, "OFF" }, /* softwareBypass */ - { 7, "OFF" }, /* off */ - { 8, "" }, /* rebooting */ - { 9, "BYPASS" }, /* switchedBypass */ - { 10, "BYPASS" }, /* hardwareFailureBypass */ - { 11, "OFF" }, /* sleepingUntilPowerReturn */ - { 12, "TRIM" }, /* onSmartTrim */ - { 0, "NULL" } + { 1, "", NULL, NULL }, /* unknown */ + { 2, "OL", NULL, NULL }, /* onLine */ + { 3, "OB", NULL, NULL }, /* onBattery */ + { 4, "OL BOOST", NULL, NULL }, /* onSmartBoost */ + { 5, "OFF", NULL, NULL }, /* timedSleeping */ + { 6, "OFF", NULL, NULL }, /* softwareBypass */ + { 7, "OFF", NULL, NULL }, /* off */ + { 8, "", NULL, NULL }, /* rebooting */ + { 9, "BYPASS", NULL, NULL }, /* switchedBypass */ + { 10, "BYPASS", NULL, NULL }, /* hardwareFailureBypass */ + { 11, "OFF", NULL, NULL }, /* sleepingUntilPowerReturn */ + { 12, "OL TRIM", NULL, NULL }, /* onSmartTrim */ + { 0, NULL, NULL, NULL } } ; #define APCC_OID_CAL_RESULTS ".1.3.6.1.4.1.318.1.1.1.7.2.6.0" static info_lkp_t apcc_cal_info[] = { - { 1, "" }, /* Calibration Successful */ - { 2, "" }, /* Calibration not done, battery capacity below 100% */ - { 3, "CAL" }, /* Calibration in progress */ - { 0, "NULL" } + { 1, "", NULL, NULL }, /* Calibration Successful */ + { 2, "", NULL, NULL }, /* Calibration not done, battery capacity below 100% */ + { 3, "CAL", NULL, NULL }, /* Calibration in progress */ + { 0, NULL, NULL, NULL } }; #define APCC_OID_NEEDREPLBATT ".1.3.6.1.4.1.318.1.1.1.2.2.4.0" static info_lkp_t apcc_battrepl_info[] = { - { 1, "" }, /* No battery needs replacing */ - { 2, "RB" }, /* Batteries need to be replaced */ - { 0, "NULL" } + { 1, "", NULL, NULL }, /* No battery needs replacing */ + { 2, "RB", NULL, NULL }, /* Batteries need to be replaced */ + { 0, NULL, NULL, NULL } }; #define APCC_OID_TESTDIAGRESULTS ".1.3.6.1.4.1.318.1.1.1.7.2.3.0" static info_lkp_t apcc_testdiag_results[] = { - { 1, "Ok" }, - { 2, "Failed" }, - { 3, "InvalidTest" }, - { 4, "TestInProgress"}, - { 0, "NULL" } + { 1, "Ok", NULL, NULL }, + { 2, "Failed", NULL, NULL }, + { 3, "InvalidTest", NULL, NULL }, + { 4, "TestInProgress", NULL, NULL }, + { 0, NULL, NULL, NULL } }; #define APCC_OID_SENSITIVITY ".1.3.6.1.4.1.318.1.1.1.5.2.7.0" static info_lkp_t apcc_sensitivity_modes[] = { - { 1, "auto" }, - { 2, "low" }, - { 3, "medium" }, - { 4, "high" }, - { 0, "NULL" } + { 1, "auto", NULL, NULL }, + { 2, "low", NULL, NULL }, + { 3, "medium", NULL, NULL }, + { 4, "high", NULL, NULL }, + { 0, NULL, NULL, NULL } }; +#define APCC_OID_TRANSFERREASON "1.3.6.1.4.1.318.1.1.1.3.2.5.0" +static info_lkp_t apcc_transfer_reasons[] = { + { 1, "noTransfer", NULL, NULL }, + { 2, "highLineVoltage", NULL, NULL }, + { 3, "brownout", NULL, NULL }, + { 4, "blackout", NULL, NULL }, + { 5, "smallMomentarySag", NULL, NULL }, + { 6, "deepMomentarySag", NULL, NULL }, + { 7, "smallMomentarySpike", NULL, NULL }, + { 8, "largeMomentarySpike", NULL, NULL }, + { 9, "selfTest", NULL, NULL }, + { 10, "rateOfVoltageChange", NULL, NULL } +}; /* --- */ @@ -117,13 +151,23 @@ static info_lkp_t apcc_sensitivity_modes[] = { static snmp_info_t apcc_mib[] = { + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* info elements. */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.1.1.1.1.0", "Generic Powernet SNMP device", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.1.1.2.3.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.1.1.2.2.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL }, + { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.1.0", "", SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE, NULL }, + { "input.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.2.0", "", SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE, NULL }, + { "input.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.3.0", "", SU_FLAG_OK | SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE, NULL }, { "input.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.1.0", "", SU_FLAG_OK, NULL }, + { "input.voltage.maximum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.2.0", "", SU_FLAG_OK, NULL }, + { "input.voltage.minimum", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.3.0", "", SU_FLAG_OK, NULL }, { "input.phases", ST_FLAG_STRING, 2, ".1.3.6.1.4.1.318.1.1.1.9.2.2.1.2.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "input.L1-L2.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.3.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L2-L3.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.3.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, @@ -144,10 +188,14 @@ static snmp_info_t apcc_mib[] = { { "input.L2.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.8.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.L3.current.minimum", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.3.1.8.1.1.3", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.2.2.1.4.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, + { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.3.3.4.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "input.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.1.3.2.4.0", "", SU_FLAG_OK, NULL }, { "input.transfer.low", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.3.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, { "input.transfer.high", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.2.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, + { "input.transfer.reason", ST_FLAG_STRING, 1, APCC_OID_TRANSFERREASON, "", SU_TYPE_INT | SU_FLAG_OK, apcc_transfer_reasons }, { "input.sensitivity", ST_FLAG_STRING | ST_FLAG_RW, 1, APCC_OID_SENSITIVITY, "", SU_TYPE_INT | SU_FLAG_OK, apcc_sensitivity_modes }, + { "ups.power", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.9.0", "", SU_FLAG_OK, NULL }, + { "ups.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.8.0", "", SU_FLAG_OK, NULL }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_POWER_STATUS, "OFF", SU_FLAG_OK | SU_STATUS_PWR, apcc_pwr_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_BATT_STATUS, "", @@ -156,27 +204,37 @@ static snmp_info_t apcc_mib[] = { SU_FLAG_OK | SU_STATUS_CAL, apcc_cal_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_NEEDREPLBATT, "", SU_FLAG_OK | SU_STATUS_RB, apcc_battrepl_info }, + { "ups.temperature", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.2.0", "", SU_FLAG_OK|SU_FLAG_UNIQUE, NULL }, { "ups.temperature", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.2.0", "", SU_FLAG_OK, NULL }, + { "ups.load", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.3.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "ups.load", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.3.0", "", SU_FLAG_OK, NULL }, { "ups.firmware", ST_FLAG_STRING, 16, ".1.3.6.1.4.1.318.1.1.1.1.2.1.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.10.0", "", SU_FLAG_OK, NULL }, - { "ups.delay.start", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.9.0", "", SU_FLAG_OK, NULL }, + { "ups.delay.shutdown", ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.10.0", "", SU_TYPE_TIME | SU_FLAG_OK, NULL }, + { "ups.delay.start", ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.9.0", "", SU_TYPE_TIME | SU_FLAG_OK, NULL }, + { "battery.charge", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.1.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "battery.charge", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.1.0", "", SU_FLAG_OK, NULL }, { "battery.charge.restart", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.6.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, { "battery.runtime", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.3.0", "", SU_FLAG_OK, NULL }, { "battery.runtime.low", ST_FLAG_STRING | ST_FLAG_RW, 3, ".1.3.6.1.4.1.318.1.1.1.5.2.8.0", "", SU_FLAG_OK, NULL }, + { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.4.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "battery.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.8.0", "", SU_FLAG_OK, NULL }, { "battery.voltage.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.7.0", "", SU_FLAG_OK, NULL }, + { "battery.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.5.0", "", SU_FLAG_OK|SU_FLAG_UNIQUE, NULL }, { "battery.current", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.9.0", "", SU_FLAG_OK, NULL }, + { "battery.current.total", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.2.3.6.0", "", SU_FLAG_OK, NULL }, { "battery.packs", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.5.0", "", SU_FLAG_OK, NULL }, { "battery.packs.bad", 0, 1, ".1.3.6.1.4.1.318.1.1.1.2.2.6.0", "", SU_FLAG_OK, NULL }, - { "battery.date", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.2.1.3.0", "", SU_FLAG_OK | SU_FLAG_STATIC | SU_TYPE_STRING, NULL }, - { "ups.id", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.1.1.2.0", "", SU_FLAG_OK | SU_FLAG_STATIC | SU_TYPE_STRING, NULL }, + { "battery.date", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.2.1.3.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL }, + { "ups.id", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.1.1.2.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL }, { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, APCC_OID_TESTDIAGRESULTS, "", SU_FLAG_OK, apcc_testdiag_results }, + { "ups.test.date", ST_FLAG_STRING | ST_FLAG_RW, 8, ".1.3.6.1.4.1.318.1.1.1.7.2.4.0", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL }, + { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.1.0", "", SU_FLAG_OK | SU_FLAG_UNIQUE, NULL }, { "output.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.1.0", "", SU_FLAG_OK, NULL }, { "output.phases", ST_FLAG_STRING, 2, ".1.3.6.1.4.1.318.1.1.1.9.3.2.1.2.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.9.3.2.1.4.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, + { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.2.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "output.frequency", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.2.0", "", SU_FLAG_OK, NULL }, + { "output.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.1.4.3.4.0", "", SU_FLAG_OK|SU_FLAG_NEGINVALID|SU_FLAG_UNIQUE, NULL }, { "output.current", 0, 1, ".1.3.6.1.4.1.318.1.1.1.4.2.4.0", "", SU_FLAG_OK, NULL }, { "output.L1-L2.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.3.1.1.1", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, { "output.L2-L3.voltage", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.3.3.1.3.1.1.2", "", SU_FLAG_OK|SU_FLAG_NEGINVALID, NULL }, @@ -213,43 +271,37 @@ static snmp_info_t apcc_mib[] = { /* Measure-UPS ambient variables */ /* Environmental sensors (AP9612TH and others) */ { "ambient.temperature", 0, 1, ".1.3.6.1.4.1.318.1.1.2.1.1.0", "", SU_FLAG_OK, NULL }, - { "ambient.temperature.high", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.3.1", "", SU_FLAG_OK, NULL }, - { "ambient.temperature.low", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.4.1", "", SU_FLAG_OK, NULL }, + { "ambient.1.temperature.alarm.high", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.3.1", "", SU_FLAG_OK, NULL }, + { "ambient.1.temperature.alarm.low", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.4.1", "", SU_FLAG_OK, NULL }, { "ambient.humidity", 0, 1, ".1.3.6.1.4.1.318.1.1.2.1.2.0", "", SU_FLAG_OK, NULL }, - { "ambient.humidity.high", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.6.1", "", SU_FLAG_OK, NULL }, - { "ambient.humidity.low", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.7.1", "", SU_FLAG_OK, NULL }, - - /* IEM ambient variables */ + { "ambient.1.humidity.alarm.high", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.6.1", "", SU_FLAG_OK, NULL }, + { "ambient.1.humidity.alarm.low", 0, 1, ".1.3.6.1.4.1.318.1.1.10.1.2.2.1.7.1", "", SU_FLAG_OK, NULL }, /* IEM: integrated environment monitor probe */ -#define APCC_OID_IEM_TEMP ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1" -#define APCC_OID_IEM_TEMP_UNIT ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.5.1" -#define APCC_IEM_FAHRENHEIT 2 -#define APCC_OID_IEM_HUMID ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.6.1" { "ambient.temperature", 0, 1, APCC_OID_IEM_TEMP, "", SU_FLAG_OK, NULL }, { "ambient.humidity", 0, 1, APCC_OID_IEM_HUMID, "", SU_FLAG_OK, NULL }, /* instant commands. */ - { "load.off", 0, 2, ".1.3.6.1.4.1.318.1.1.1.6.2.1.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "load.on", 0, 2, ".1.3.6.1.4.1.318.1.1.1.6.2.6.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "shutdown.stayoff", 0, 3, ".1.3.6.1.4.1.318.1.1.1.6.2.1.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "load.off", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.1.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "load.on", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.6.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "shutdown.stayoff", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.1.0", "3", SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* { CMD_SDRET, 0, APCC_REBOOT_GRACEFUL, APCC_OID_REBOOT, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, */ - { "shutdown.return", 0, 2, ".1.3.6.1.4.1.318.1.1.1.6.1.1.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "test.failure.start", 0, 2, ".1.3.6.1.4.1.318.1.1.1.6.2.4.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "test.panel.start", 0, 2, ".1.3.6.1.4.1.318.1.1.1.6.2.5.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "bypass.start", 0, 2, ".1.3.6.1.4.1.318.1.1.1.6.2.7.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "bypass.stop", 0, 3, ".1.3.6.1.4.1.318.1.1.1.6.2.7.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "test.battery.start", 0, 2, ".1.3.6.1.4.1.318.1.1.1.7.2.2.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "calibrate.start", 0, 2, ".1.3.6.1.4.1.318.1.1.1.7.2.5.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "calibrate.stop", 0, 3, ".1.3.6.1.4.1.318.1.1.1.7.2.5.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - { "reset.input.minmax", 0, 2, ".1.3.6.1.4.1.318.1.1.1.9.1.1.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "shutdown.return", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.1.1.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "test.failure.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.4.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "test.panel.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.5.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "bypass.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.7.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "bypass.stop", 0, 1, ".1.3.6.1.4.1.318.1.1.1.6.2.7.0", "3", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "test.battery.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.7.2.2.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "calibrate.start", 0, 1, ".1.3.6.1.4.1.318.1.1.1.7.2.5.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "calibrate.stop", 0, 1, ".1.3.6.1.4.1.318.1.1.1.7.2.5.0", "3", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "reset.input.minmax", 0, 1, ".1.3.6.1.4.1.318.1.1.1.9.1.1.0", "2", SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; -mib2nut_info_t apc = { "apcc", APCC_MIB_VERSION, APCC_OID_POWER_STATUS, ".1.3.6.1.4.1.318.1.1.1.1.1.1.0", apcc_mib }; +mib2nut_info_t apc = { "apcc", APCC_MIB_VERSION, APCC_OID_POWER_STATUS, APC_UPS_DEVICE_MODEL, apcc_mib, APC_UPS_SYSOID, NULL }; /* vim:ts=4:sw=4:et: diff --git a/drivers/apc-mib.h b/drivers/apc-mib.h index e5aeb07..0b510ea 100644 --- a/drivers/apc-mib.h +++ b/drivers/apc-mib.h @@ -3,15 +3,7 @@ #include "main.h" #include "snmp-ups.h" - -/* - * FIXME: The below is needed because the main driver body uses this to determine - * whether a conversion from Fahrenheit to Celsius is needed (which really should - * be solved in subdriver specific formatting functions, like we do in usbhid-ups - */ -#define APCC_OID_IEM_TEMP ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.4.1" -#define APCC_OID_IEM_TEMP_UNIT ".1.3.6.1.4.1.318.1.1.10.2.3.2.1.5.1" -#define APCC_IEM_FAHRENHEIT 2 +#include "apc-iem-mib.h" extern mib2nut_info_t apc; diff --git a/drivers/apc-pdu-mib.c b/drivers/apc-pdu-mib.c new file mode 100644 index 0000000..38963d5 --- /dev/null +++ b/drivers/apc-pdu-mib.c @@ -0,0 +1,972 @@ +/* apc-pdu-mib.c - subdriver to monitor APC PDU using PowerNet-MIB SNMP with NUT + * + * Copyright (C) 2016 - Eaton + * Authors: Tomas Halman + * Arnaud Quette + * + * Based on initial work and data from Opengear + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "apc-pdu-mib.h" + +#define APC_PDU_MIB_VERSION "0.4" + +#define APC_PDU_MIB_SYSOID_RPDU ".1.3.6.1.4.1.318.1.3.4.4" +#define APC_PDU_MIB_SYSOID_RPDU2 ".1.3.6.1.4.1.318.1.3.4.5" +#define APC_PDU_MIB_SYSOID_MSP ".1.3.6.1.4.1.318.1.3.4.6" +#define APC_PDU_DEVICE_MODEL ".1.3.6.1.4.1.318.1.1.4.1.4.0" + + +static info_lkp_t apc_pdu_sw_outlet_status_info[] = { + { 1, "on", NULL, NULL }, + { 2, "off", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t apc_pdu_sw_outlet_switchability_info[] = { + { 1, "yes", NULL, NULL }, + { 2, "yes", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* POWERNET-MIB Snmp2NUT lookup table */ +static snmp_info_t apc_pdu_mib[] = { + + /* Device page */ + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "APC", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* sPDUIdentModelNumber.0 = STRING: "AP7900" */ + { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.4.0", + "Switched ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, + SU_FLAG_STALE | SU_FLAG_OK, NULL }, + { "device.description", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.1.5.0", NULL, + SU_FLAG_STALE | SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, + SU_FLAG_STALE | SU_FLAG_OK, NULL }, + /* FIXME: to be RFC'ed */ + { "device.uptime", 0, 1, ".1.3.6.1.2.1.1.3.0", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID, NULL }, + /* sPDUIdentSerialNumber.0 = STRING: "5A1234E00874" */ + { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.5.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* sPDUIdentModelNumber.0 = STRING: "AP7900" */ + { "device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.4.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* sPDUIdentHardwareRev.0 = STRING: "B2" */ + { "device.version", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + + /* sPDUIdentFirmwareRev.0 = STRING: "v3.7.3" */ + /* FIXME: to be moved to device.firmware */ + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.2.0", NULL, SU_FLAG_OK, NULL }, + /* sPDUIdentDateOfManufacture.0 = STRING: "08/13/2012" */ + /* FIXME: to be moved to the device collection! */ + { "ups.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.1.3.0", NULL, SU_FLAG_OK, NULL }, + + /* Input */ + /* rPDUIdentDevicePowerWatts.0 = INTEGER: 0 */ + { "input.realpower", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.16.0", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadStatusLoad.1 = Gauge32: 0 */ + { "input.current", 0, 0.1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentDeviceLinetoLineVoltage.0 = INTEGER: 120 */ + { "input.voltage.nominal", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.15.0", NULL, SU_FLAG_OK | SU_FLAG_NEGINVALID, NULL }, + + /* Outlets */ + { "outlet.count", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.%i", "%i", + SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, NULL }, + /* sPDUOutletCtlName.%i = STRING: "Testing Name" */ + { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.4.2.1.4.%i", NULL, + SU_FLAG_STALE | SU_FLAG_OK | SU_OUTLET, NULL }, + /* sPDUOutletCtl.1 = INTEGER: outletOn(1) */ + { "outlet.%i.status", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.4.2.1.3.%i", NULL, + SU_FLAG_OK | SU_OUTLET, &apc_pdu_sw_outlet_status_info[0] }, + /* Also use this OID to determine switchability ; its presence means "yes" */ + /* sPDUOutletCtl.1 = INTEGER: outletOn(1) */ + { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.4.2.1.3.%i", "yes", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, &apc_pdu_sw_outlet_switchability_info[0] }, + + + +#if 0 /* keep following scan for future development */ + + /* sPDUMasterControlSwitch.0 = INTEGER: noCommand(6) */ + { "unmapped.sPDUMasterControlSwitch", 0, 1, ".1.3.6.1.4.1.318.1.1.4.2.1.0", NULL, SU_FLAG_OK, NULL }, + /* sPDUMasterState.0 = STRING: "On On On On On On On On " */ + { "unmapped.sPDUMasterState", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.2.2.0", NULL, SU_FLAG_OK, NULL }, + /* sPDUMasterPending.0 = STRING: "No No No No No No No No " */ + { "unmapped.sPDUMasterPending", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.2.3.0", NULL, SU_FLAG_OK, NULL }, + /* sPDUMasterConfigPowerOn.0 = INTEGER: 0 */ + { "unmapped.sPDUMasterConfigPowerOn", 0, 1, ".1.3.6.1.4.1.318.1.1.4.3.1.0", NULL, SU_FLAG_OK, NULL }, + /* sPDUMasterConfigReboot.0 = INTEGER: 0 */ + { "unmapped.sPDUMasterConfigReboot", 0, 1, ".1.3.6.1.4.1.318.1.1.4.3.2.0", NULL, SU_FLAG_OK, NULL }, + /* sPDUMasterConfigPDUName.0 = STRING: "RackPDU" */ + { "unmapped.sPDUMasterConfigPDUName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.3.3.0", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletControlTableSize.0 = INTEGER: 8 */ + + /* sPDUOutletControlIndex.1 = INTEGER: 1 */ + { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletControlIndex.2 = INTEGER: 2 */ + { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletControlIndex.3 = INTEGER: 3 */ + { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.3", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletControlIndex.4 = INTEGER: 4 */ + { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletControlIndex.5 = INTEGER: 5 */ + { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletControlIndex.6 = INTEGER: 6 */ + { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletControlIndex.7 = INTEGER: 7 */ + { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletControlIndex.8 = INTEGER: 8 */ + { "unmapped.sPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPending.1 = INTEGER: noCommandPending(2) */ + { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPending.2 = INTEGER: noCommandPending(2) */ + { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPending.3 = INTEGER: noCommandPending(2) */ + { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.3", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPending.4 = INTEGER: noCommandPending(2) */ + { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.4", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPending.5 = INTEGER: noCommandPending(2) */ + { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.5", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPending.6 = INTEGER: noCommandPending(2) */ + { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.6", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPending.7 = INTEGER: noCommandPending(2) */ + { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.7", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPending.8 = INTEGER: noCommandPending(2) */ + { "unmapped.sPDUOutletPending", 0, 1, ".1.3.6.1.4.1.318.1.1.4.4.2.1.2.8", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletConfigTableSize.0 = INTEGER: 8 */ + { "unmapped.sPDUOutletConfigTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.1.0", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletConfigIndex.1 = INTEGER: 1 */ + { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletConfigIndex.2 = INTEGER: 2 */ + { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletConfigIndex.3 = INTEGER: 3 */ + { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.3", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletConfigIndex.4 = INTEGER: 4 */ + { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletConfigIndex.5 = INTEGER: 5 */ + { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletConfigIndex.6 = INTEGER: 6 */ + { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletConfigIndex.7 = INTEGER: 7 */ + { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletConfigIndex.8 = INTEGER: 8 */ + { "unmapped.sPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOnTime.1 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOnTime.2 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOnTime.3 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.3", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOnTime.4 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.4", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOnTime.5 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.5", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOnTime.6 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.6", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOnTime.7 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.7", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOnTime.8 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.2.8", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletName.1 = STRING: "Testing Name" */ + { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletName.2 = STRING: "Testing 2" */ + { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletName.3 = STRING: "Outlet 3" */ + { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.3", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletName.4 = STRING: "Outlet 4" */ + { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.4", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletName.5 = STRING: "Outlet 5" */ + { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.5", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletName.6 = STRING: "Outlet 6" */ + { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.6", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletName.7 = STRING: "Outlet 7" */ + { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.7", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletName.8 = STRING: "Outlet 8" */ + { "unmapped.sPDUOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.4.5.2.1.3.8", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOffTime.1 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOffTime.2 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOffTime.3 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.3", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOffTime.4 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.4", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOffTime.5 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.5", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOffTime.6 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.6", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOffTime.7 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.7", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletPowerOffTime.8 = INTEGER: 0 */ + { "unmapped.sPDUOutletPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.4.8", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletRebootDuration.1 = INTEGER: 5 */ + { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletRebootDuration.2 = INTEGER: 5 */ + { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.2", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletRebootDuration.3 = INTEGER: 5 */ + { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.3", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletRebootDuration.4 = INTEGER: 5 */ + { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.4", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletRebootDuration.5 = INTEGER: 5 */ + { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.5", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletRebootDuration.6 = INTEGER: 5 */ + { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.6", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletRebootDuration.7 = INTEGER: 5 */ + { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.7", NULL, SU_FLAG_OK, NULL }, + /* sPDUOutletRebootDuration.8 = INTEGER: 5 */ + { "unmapped.sPDUOutletRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.4.5.2.1.5.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentName.0 = STRING: "RackPDU" */ + { "unmapped.rPDUIdentName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentHardwareRev.0 = STRING: "B2" */ + { "unmapped.rPDUIdentHardwareRev", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.2.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentFirmwareRev.0 = STRING: "v3.7.3" */ + { "unmapped.rPDUIdentFirmwareRev", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.3.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentDateOfManufacture.0 = STRING: "08/13/2012" */ + { "unmapped.rPDUIdentDateOfManufacture", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.4.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentModelNumber.0 = STRING: "AP7900" */ + { "unmapped.rPDUIdentModelNumber", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.5.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentSerialNumber.0 = STRING: "5A1234E00874" */ + { "unmapped.rPDUIdentSerialNumber", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentDeviceRating.0 = INTEGER: 12 */ + { "unmapped.rPDUIdentDeviceRating", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.7.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentDeviceNumOutlets.0 = INTEGER: 8 */ + { "unmapped.rPDUIdentDeviceNumOutlets", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.8.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentDeviceNumPhases.0 = INTEGER: 1 */ + { "input.phases", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.9.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* rPDUIdentDeviceNumBreakers.0 = INTEGER: 0 */ + { "unmapped.rPDUIdentDeviceNumBreakers", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.10.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentDeviceBreakerRating.0 = INTEGER: 0 */ + { "unmapped.rPDUIdentDeviceBreakerRating", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.11.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentDeviceOrientation.0 = INTEGER: orientHorizontal(1) */ + { "unmapped.rPDUIdentDeviceOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.12.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentDeviceOutletLayout.0 = INTEGER: seqPhaseToNeutral(1) */ + { "unmapped.rPDUIdentDeviceOutletLayout", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.13.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentDeviceDisplayOrientation.0 = INTEGER: displayNormal(1) */ + { "unmapped.rPDUIdentDeviceDisplayOrientation", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.14.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentDeviceLinetoLineVoltage.0 = INTEGER: 120 */ + { "unmapped.rPDUIdentDeviceLinetoLineVoltage", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.15.0", NULL, SU_FLAG_OK, NULL }, + + /* rPDUIdentDevicePowerFactor.0 = INTEGER: 1000 */ + { "unmapped.rPDUIdentDevicePowerFactor", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.17.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUIdentDevicePowerVA.0 = INTEGER: 0 */ + { "unmapped.rPDUIdentDevicePowerVA", 0, 1, ".1.3.6.1.4.1.318.1.1.12.1.18.0", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadDevMaxPhaseLoad.0 = INTEGER: 12 */ + { "unmapped.rPDULoadDevMaxPhaseLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadDevNumPhases.0 = INTEGER: 1 */ + { "unmapped.rPDULoadDevNumPhases", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.2.0", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadDevMaxBankLoad.0 = INTEGER: 0 */ + { "unmapped.rPDULoadDevMaxBankLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.3.0", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadDevNumBanks.0 = INTEGER: 0 */ + { "unmapped.rPDULoadDevNumBanks", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.4.0", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadDevBankTableSize.0 = INTEGER: 0 */ + { "unmapped.rPDULoadDevBankTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.5.0", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadDevMaxOutletTableSize.0 = INTEGER: 0 */ + { "unmapped.rPDULoadDevMaxOutletTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.1.7.0", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadPhaseConfigIndex.phase1 = INTEGER: phase1(1) */ + { "unmapped.rPDULoadPhaseConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadPhaseConfigLowLoadThreshold.phase1 = INTEGER: 0 */ + { "unmapped.rPDULoadPhaseConfigLowLoadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadPhaseConfigNearOverloadThreshold.phase1 = INTEGER: 8 */ + { "unmapped.rPDULoadPhaseConfigNearOverloadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadPhaseConfigOverloadThreshold.phase1 = INTEGER: 12 */ + { "unmapped.rPDULoadPhaseConfigOverloadThreshold", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadPhaseConfigAlarm.phase1 = INTEGER: noLoadAlarm(1) */ + { "unmapped.rPDULoadPhaseConfigAlarm", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.2.1.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadStatusIndex.1 = INTEGER: 1 */ + { "unmapped.rPDULoadStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadStatusLoadState.1 = INTEGER: phaseLoadNormal(1) */ + { "unmapped.rPDULoadStatusLoadState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadStatusPhaseNumber.1 = INTEGER: 1 */ + { "unmapped.rPDULoadStatusPhaseNumber", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* rPDULoadStatusBankNumber.1 = INTEGER: 0 */ + { "unmapped.rPDULoadStatusBankNumber", 0, 1, ".1.3.6.1.4.1.318.1.1.12.2.3.1.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletDevCommand.0 = INTEGER: noCommandAll(1) */ + { "unmapped.rPDUOutletDevCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletDevColdstartDelay.0 = INTEGER: 0 */ + { "unmapped.rPDUOutletDevColdstartDelay", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.2.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletDevNumCntrlOutlets.0 = INTEGER: 8 */ + { "unmapped.rPDUOutletDevNumCntrlOutlets", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.3.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletDevNumTotalOutlets.0 = INTEGER: 8 */ + { "unmapped.rPDUOutletDevNumTotalOutlets", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.4.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletDevMonitoredOutlets.0 = INTEGER: 0 */ + { "unmapped.rPDUOutletDevMonitoredOutlets", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.1.5.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletPhaseIndex.phase1 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.2.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletPhaseOverloadRestriction.phase1 = INTEGER: alwaysAllowTurnON(1) */ + { "unmapped.rPDUOutletPhaseOverloadRestriction", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.2.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlIndex.1 = INTEGER: 1 */ + { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlIndex.2 = INTEGER: 2 */ + { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlIndex.3 = INTEGER: 3 */ + { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlIndex.4 = INTEGER: 4 */ + { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlIndex.5 = INTEGER: 5 */ + { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlIndex.6 = INTEGER: 6 */ + { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlIndex.7 = INTEGER: 7 */ + { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlIndex.8 = INTEGER: 8 */ + { "unmapped.rPDUOutletControlIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletName.1 = STRING: "Testing Name" */ + { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletName.2 = STRING: "Testing 2" */ + { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletName.3 = STRING: "Outlet 3" */ + { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletName.4 = STRING: "Outlet 4" */ + { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletName.5 = STRING: "Outlet 5" */ + { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletName.6 = STRING: "Outlet 6" */ + { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletName.7 = STRING: "Outlet 7" */ + { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletName.8 = STRING: "Outlet 8" */ + { "unmapped.rPDUOutletControlOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.2.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletPhase.1 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletPhase.2 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletPhase.3 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletPhase.4 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletPhase.5 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletPhase.6 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletPhase.7 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletPhase.8 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletControlOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.3.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletCommand.1 = INTEGER: immediateOn(1) */ + { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletCommand.2 = INTEGER: immediateOn(1) */ + { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletCommand.3 = INTEGER: immediateOn(1) */ + { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletCommand.4 = INTEGER: immediateOn(1) */ + { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletCommand.5 = INTEGER: immediateOn(1) */ + { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletCommand.6 = INTEGER: immediateOn(1) */ + { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletCommand.7 = INTEGER: immediateOn(1) */ + { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletCommand.8 = INTEGER: immediateOn(1) */ + { "unmapped.rPDUOutletControlOutletCommand", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.4.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletBank.1 = INTEGER: 0 */ + { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletBank.2 = INTEGER: 0 */ + { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletBank.3 = INTEGER: 0 */ + { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletBank.4 = INTEGER: 0 */ + { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletBank.5 = INTEGER: 0 */ + { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletBank.6 = INTEGER: 0 */ + { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletBank.7 = INTEGER: 0 */ + { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletControlOutletBank.8 = INTEGER: 0 */ + { "unmapped.rPDUOutletControlOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.3.1.1.5.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigIndex.1 = INTEGER: 1 */ + { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigIndex.2 = INTEGER: 2 */ + { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigIndex.3 = INTEGER: 3 */ + { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigIndex.4 = INTEGER: 4 */ + { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigIndex.5 = INTEGER: 5 */ + { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigIndex.6 = INTEGER: 6 */ + { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigIndex.7 = INTEGER: 7 */ + { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigIndex.8 = INTEGER: 8 */ + { "unmapped.rPDUOutletConfigIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletName.1 = STRING: "Testing Name" */ + { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletName.2 = STRING: "Testing 2" */ + { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletName.3 = STRING: "Outlet 3" */ + { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletName.4 = STRING: "Outlet 4" */ + { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletName.5 = STRING: "Outlet 5" */ + { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletName.6 = STRING: "Outlet 6" */ + { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletName.7 = STRING: "Outlet 7" */ + { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletName.8 = STRING: "Outlet 8" */ + { "unmapped.rPDUOutletConfigOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.2.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletPhase.1 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletPhase.2 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletPhase.3 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletPhase.4 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletPhase.5 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletPhase.6 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletPhase.7 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletPhase.8 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletConfigOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.3.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOnTime.1 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOnTime.2 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOnTime.3 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOnTime.4 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOnTime.5 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOnTime.6 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOnTime.7 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOnTime.8 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOnTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.4.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOffTime.1 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOffTime.2 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOffTime.3 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOffTime.4 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOffTime.5 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOffTime.6 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOffTime.7 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigPowerOffTime.8 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigPowerOffTime", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.5.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigRebootDuration.1 = INTEGER: 5 */ + { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigRebootDuration.2 = INTEGER: 5 */ + { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigRebootDuration.3 = INTEGER: 5 */ + { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigRebootDuration.4 = INTEGER: 5 */ + { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigRebootDuration.5 = INTEGER: 5 */ + { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigRebootDuration.6 = INTEGER: 5 */ + { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigRebootDuration.7 = INTEGER: 5 */ + { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigRebootDuration.8 = INTEGER: 5 */ + { "unmapped.rPDUOutletConfigRebootDuration", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.6.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletBank.1 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletBank.2 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletBank.3 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletBank.4 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletBank.5 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletBank.6 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletBank.7 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigOutletBank.8 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.1.1.7.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletConfigMonitoredTableSize.0 = INTEGER: 0 */ + { "unmapped.rPDUOutletConfigMonitoredTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.4.2.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusIndex.1 = INTEGER: 1 */ + { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusIndex.2 = INTEGER: 2 */ + { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusIndex.3 = INTEGER: 3 */ + { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusIndex.4 = INTEGER: 4 */ + { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusIndex.5 = INTEGER: 5 */ + { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusIndex.6 = INTEGER: 6 */ + { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusIndex.7 = INTEGER: 7 */ + { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusIndex.8 = INTEGER: 8 */ + { "unmapped.rPDUOutletStatusIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletName.1 = STRING: "Testing Name" */ + { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletName.2 = STRING: "Testing 2" */ + { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletName.3 = STRING: "Outlet 3" */ + { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletName.4 = STRING: "Outlet 4" */ + { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletName.5 = STRING: "Outlet 5" */ + { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletName.6 = STRING: "Outlet 6" */ + { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletName.7 = STRING: "Outlet 7" */ + { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletName.8 = STRING: "Outlet 8" */ + { "unmapped.rPDUOutletStatusOutletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.2.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletPhase.1 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletPhase.2 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletPhase.3 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletPhase.4 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletPhase.5 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletPhase.6 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletPhase.7 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletPhase.8 = INTEGER: phase1(1) */ + { "unmapped.rPDUOutletStatusOutletPhase", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.3.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletState.1 = INTEGER: outletStatusOn(1) */ + { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletState.2 = INTEGER: outletStatusOn(1) */ + { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletState.3 = INTEGER: outletStatusOn(1) */ + { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletState.4 = INTEGER: outletStatusOn(1) */ + { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletState.5 = INTEGER: outletStatusOn(1) */ + { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletState.6 = INTEGER: outletStatusOn(1) */ + { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletState.7 = INTEGER: outletStatusOn(1) */ + { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletState.8 = INTEGER: outletStatusOn(1) */ + { "unmapped.rPDUOutletStatusOutletState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.4.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusCommandPending.1 = INTEGER: outletStatusNoCommandPending(2) */ + { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusCommandPending.2 = INTEGER: outletStatusNoCommandPending(2) */ + { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusCommandPending.3 = INTEGER: outletStatusNoCommandPending(2) */ + { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusCommandPending.4 = INTEGER: outletStatusNoCommandPending(2) */ + { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusCommandPending.5 = INTEGER: outletStatusNoCommandPending(2) */ + { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusCommandPending.6 = INTEGER: outletStatusNoCommandPending(2) */ + { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusCommandPending.7 = INTEGER: outletStatusNoCommandPending(2) */ + { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusCommandPending.8 = INTEGER: outletStatusNoCommandPending(2) */ + { "unmapped.rPDUOutletStatusCommandPending", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.5.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletBank.1 = INTEGER: 0 */ + { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletBank.2 = INTEGER: 0 */ + { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletBank.3 = INTEGER: 0 */ + { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletBank.4 = INTEGER: 0 */ + { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletBank.5 = INTEGER: 0 */ + { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletBank.6 = INTEGER: 0 */ + { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletBank.7 = INTEGER: 0 */ + { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusOutletBank.8 = INTEGER: 0 */ + { "unmapped.rPDUOutletStatusOutletBank", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.6.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusLoad.1 = Gauge32: 0 */ + { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusLoad.2 = Gauge32: 0 */ + { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.2", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusLoad.3 = Gauge32: 0 */ + { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.3", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusLoad.4 = Gauge32: 0 */ + { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.4", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusLoad.5 = Gauge32: 0 */ + { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.5", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusLoad.6 = Gauge32: 0 */ + { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.6", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusLoad.7 = Gauge32: 0 */ + { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.7", NULL, SU_FLAG_OK, NULL }, + /* rPDUOutletStatusLoad.8 = Gauge32: 0 */ + { "unmapped.rPDUOutletStatusLoad", 0, 1, ".1.3.6.1.4.1.318.1.1.12.3.5.1.1.7.8", NULL, SU_FLAG_OK, NULL }, + /* rPDUPowerSupply1Status.0 = INTEGER: powerSupplyOneOk(1) */ + { "unmapped.rPDUPowerSupply1Status", 0, 1, ".1.3.6.1.4.1.318.1.1.12.4.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUPowerSupply2Status.0 = INTEGER: powerSupplyTwoOk(1) */ + { "unmapped.rPDUPowerSupply2Status", 0, 1, ".1.3.6.1.4.1.318.1.1.12.4.1.2.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUPowerSupplyAlarm.0 = INTEGER: allAvailablePowerSuppliesOK(1) */ + { "unmapped.rPDUPowerSupplyAlarm", 0, 1, ".1.3.6.1.4.1.318.1.1.12.4.1.3.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUStatusBankTableSize.0 = INTEGER: 0 */ + { "unmapped.rPDUStatusBankTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.1.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUStatusPhaseTableSize.0 = INTEGER: 1 */ + { "unmapped.rPDUStatusPhaseTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.3.0", NULL, SU_FLAG_OK, NULL }, + /* rPDUStatusPhaseIndex.1 = INTEGER: 1 */ + { "unmapped.rPDUStatusPhaseIndex", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUStatusPhaseNumber.1 = INTEGER: 1 */ + { "unmapped.rPDUStatusPhaseNumber", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.4.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUStatusPhaseState.1 = INTEGER: phaseLoadNormal(1) */ + { "unmapped.rPDUStatusPhaseState", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.4.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* rPDUStatusOutletTableSize.0 = INTEGER: 0 */ + { "unmapped.rPDUStatusOutletTableSize", 0, 1, ".1.3.6.1.4.1.318.1.1.12.5.5.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.1.0 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.1.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.1.1 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.2.1 = STRING: "Rack PDU_ISX" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.3.1 = STRING: "5A1234E00874" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.4.1 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.5.1 = STRING: "Rack PDU" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.6.1 = STRING: "1" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.6.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.7.1 = STRING: "Unknown" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.7.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.8.1 = INTEGER: 255 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.8.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.9.1 = STRING: "RackPDU" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.9.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.10.1 = INTEGER: 255 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.10.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.11.1 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.11.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.12.1 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.12.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.13.1 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.2.1.13.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.2.1.14.1 = STRING: "SB-1" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.2.1.14.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.3.0 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.3.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.4.1.1.1 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.4.1.1.2 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.4.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.4.1.2.1 = STRING: "5A1234E00874" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.4.1.2.2 = STRING: "5A1234E00874" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.4.1.3.1 = STRING: "apc_hw02_aos_373.bin" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.4.1.3.2 = STRING: "apc_hw02_rpdu_373.bin" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.4.1.4.1 = STRING: "v3.7.3" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.4.1.4.2 = STRING: "v3.7.3" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.4.1.4.2", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.5.0 = INTEGER: 19 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.5.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.1 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.2 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.3 = INTEGER: 3 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.3", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.4 = INTEGER: 4 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.5 = INTEGER: 5 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.6 = INTEGER: 6 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.7 = INTEGER: 7 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.8 = INTEGER: 8 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.9 = INTEGER: 9 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.9", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.10 = INTEGER: 10 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.10", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.11 = INTEGER: 11 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.11", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.12 = INTEGER: 12 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.12", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.13 = INTEGER: 13 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.13", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.14 = INTEGER: 14 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.14", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.15 = INTEGER: 15 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.15", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.16 = INTEGER: 16 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.16", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.17 = INTEGER: 17 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.17", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.18 = INTEGER: 18 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.18", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.1.19 = INTEGER: 19 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.1.19", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.1 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.2 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.3 = INTEGER: 3 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.3", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.4 = INTEGER: 4 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.4", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.5 = INTEGER: 5 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.5", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.6 = INTEGER: 6 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.6", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.7 = INTEGER: 7 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.7", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.8 = INTEGER: 8 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.8", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.9 = INTEGER: 9 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.9", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.10 = INTEGER: 10 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.10", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.11 = INTEGER: 11 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.11", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.12 = INTEGER: 12 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.12", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.13 = INTEGER: 13 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.13", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.14 = INTEGER: 14 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.14", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.15 = INTEGER: 15 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.15", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.16 = INTEGER: 16 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.16", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.17 = INTEGER: 17 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.17", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.18 = INTEGER: 18 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.18", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.2.19 = INTEGER: 19 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.2.19", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.1 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.2 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.3 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.3", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.4 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.4", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.5 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.5", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.6 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.6", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.7 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.7", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.8 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.8", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.9 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.9", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.10 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.10", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.11 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.11", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.12 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.12", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.13 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.13", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.14 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.14", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.15 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.15", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.16 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.16", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.17 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.17", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.18 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.18", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.3.19 = STRING: "0" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.3.19", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.1 = STRING: "MTYx" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.2 = STRING: "MjM=" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.2", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.3 = STRING: "ODA=" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.3", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.4 = STRING: "OTk1MA==" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.4", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.5 = STRING: "OTk1MA==" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.5", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.6 = STRING: "NDQz" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.6", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.7 = STRING: "MjI=" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.7", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.8 = STRING: "MjI=" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.8", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.9 = STRING: "MTIz" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.9", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.10 = STRING: "MjU=" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.10", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.11 = STRING: "MjE=" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.11", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.12 = STRING: "MjE=" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.12", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.13 = STRING: "Njg=" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.13", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.14 = STRING: "NTQ2" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.14", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.15 = STRING: "MTgxMg==" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.15", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.16 = STRING: "MTYx" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.16", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.17 = STRING: "MjE=" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.17", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.18 = STRING: "MjE=" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.18", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.4.19 = STRING: "OTk1MQ==" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.6.1.4.19", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.1 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.2 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.2", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.3 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.3", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.4 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.4", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.5 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.5", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.6 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.6", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.7 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.7", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.8 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.8", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.9 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.9", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.10 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.10", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.11 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.11", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.12 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.12", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.13 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.13", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.14 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.14", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.15 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.15", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.16 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.16", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.17 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.17", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.18 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.18", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.6.1.5.19 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.6.1.5.19", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.7.0 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.7.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.8.1.1.1 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.8.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.8.1.2.1 = STRING: "power" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.8.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.8.1.3.1 = STRING: "pdu" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.8.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.8.1.4.1 = STRING: "rpdu" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.8.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.8.1.5.1 = STRING: "version7" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.2.8.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.8.1.6.1 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.8.1.6.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.9.0 = INTEGER: 0 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.9.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.10.0 = INTEGER: 0 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.10.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.2.12.0 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.2.12.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.4.1.0 = INTEGER: 0 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.4.1.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.4.3.0 = STRING: ",<1 digit type identifier>" */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.4.3.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.4.4.0 = INTEGER: 0 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.4.4.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.1.0 = INTEGER: 12 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.1.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.1.1 = INTEGER: 1 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.1.2 = INTEGER: 2 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.1.3 = INTEGER: 3 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.3", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.1.4 = INTEGER: 4 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.1.5 = INTEGER: 5 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.1.6 = INTEGER: 6 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.1.7 = INTEGER: 7 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.1.8 = INTEGER: 8 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.1.9 = INTEGER: 9 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.9", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.1.10 = INTEGER: 10 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.10", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.1.11 = INTEGER: 11 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.11", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.1.12 = INTEGER: 12 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.1.12", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.2.1 = INTEGER: 3841 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.2.2 = INTEGER: 3843 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.2.3 = INTEGER: 3845 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.3", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.2.4 = INTEGER: 3848 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.4", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.2.5 = INTEGER: 3862 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.5", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.2.6 = INTEGER: 3864 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.6", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.2.7 = INTEGER: 3856 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.7", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.2.8 = INTEGER: 3858 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.8", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.2.9 = INTEGER: 3860 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.9", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.2.10 = INTEGER: 3871 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.10", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.2.11 = INTEGER: 3873 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.11", NULL, SU_FLAG_OK, NULL }, + /* experimental.5.2.1.2.12 = INTEGER: 3875 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.5.2.1.2.12", NULL, SU_FLAG_OK, NULL }, + /* experimental.6.1.1.0 = Hex-STRING: 07 00 00 00 */ + { "unmapped.experimental", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.318.1.4.6.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.7.1.0 = INTEGER: 4 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.7.1.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.7.3.0 = INTEGER: 4 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.7.3.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.7.4.0 = INTEGER: 3 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.7.4.0", NULL, SU_FLAG_OK, NULL }, + /* experimental.7.5.0 = INTEGER: 3 */ + { "unmapped.experimental", 0, 1, ".1.3.6.1.4.1.318.1.4.7.5.0", NULL, SU_FLAG_OK, NULL }, +#endif /* scan result */ + + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + +mib2nut_info_t apc_pdu_rpdu = { "apc_pdu", APC_PDU_MIB_VERSION, NULL, APC_PDU_DEVICE_MODEL, apc_pdu_mib, APC_PDU_MIB_SYSOID_RPDU, NULL }; +mib2nut_info_t apc_pdu_rpdu2 = { "apc_pdu", APC_PDU_MIB_VERSION, NULL, APC_PDU_DEVICE_MODEL, apc_pdu_mib, APC_PDU_MIB_SYSOID_RPDU2, NULL }; +mib2nut_info_t apc_pdu_msp = { "apc_pdu", APC_PDU_MIB_VERSION, NULL, APC_PDU_DEVICE_MODEL, apc_pdu_mib, APC_PDU_MIB_SYSOID_MSP, NULL }; diff --git a/drivers/apc-pdu-mib.h b/drivers/apc-pdu-mib.h new file mode 100644 index 0000000..224330a --- /dev/null +++ b/drivers/apc-pdu-mib.h @@ -0,0 +1,31 @@ +/* powernet-mib-mib.h - subdriver to monitor PowerNet-MIB SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2012 Arnaud Quette + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef APC_PDU_MIB_H +#define APC_PDU_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t apc_pdu_rpdu; +extern mib2nut_info_t apc_pdu_rpdu2; +extern mib2nut_info_t apc_pdu_msp; + +#endif /* APC_PDU_MIB_H */ diff --git a/drivers/apcsmart-old.c b/drivers/apcsmart-old.c index 5c06372..0a2f451 100644 --- a/drivers/apcsmart-old.c +++ b/drivers/apcsmart-old.c @@ -22,9 +22,10 @@ #include "main.h" #include "serial.h" #include "apcsmart-old.h" +#include "nut_stdint.h" #define DRIVER_NAME "APC Smart protocol driver" -#define DRIVER_VERSION "2.1" +#define DRIVER_VERSION "2.2" static upsdrv_info_t table_info = { "APC command table", @@ -77,7 +78,7 @@ static apc_vartab_t *vartab_lookup_name(const char *var) static const char *convert_data(apc_vartab_t *cmd_entry, const char *upsval) { static char tmp[128]; - int tval; + long tval; switch(cmd_entry->flags & APC_FORMATMASK) { case APC_F_PERCENT: @@ -97,14 +98,14 @@ static const char *convert_data(apc_vartab_t *cmd_entry, const char *upsval) tval = 60 * 60 * strtol(upsval, NULL, 10); - snprintf(tmp, sizeof(tmp), "%d", tval); + snprintf(tmp, sizeof(tmp), "%ld", tval); return tmp; case APC_F_MINUTES: /* Convert to seconds - NUT standard time measurement */ tval = 60 * strtol(upsval, NULL, 10); /* Ignore errors - Theres not much we can do */ - snprintf(tmp, sizeof(tmp), "%d", tval); + snprintf(tmp, sizeof(tmp), "%ld", tval); return tmp; case APC_F_REASON: @@ -197,9 +198,9 @@ static void alert_handler(char ch) ups_status_set(); } -static int read_buf(char *buf, size_t buflen) +static ssize_t read_buf(char *buf, size_t buflen) { - int ret; + ssize_t ret; ret = ser_get_line_alert(upsfd, buf, buflen, ENDCHAR, POLL_IGNORE, POLL_ALERT, alert_handler, SER_WAIT_SEC, SER_WAIT_USEC); @@ -213,9 +214,9 @@ static int read_buf(char *buf, size_t buflen) return ret; } -static int poll_data(apc_vartab_t *vt) +static ssize_t poll_data(apc_vartab_t *vt) { - int ret; + ssize_t ret; char tmp[SMALLBUF]; if ((vt->flags & APC_PRESENT) == 0) @@ -251,7 +252,7 @@ static int poll_data(apc_vartab_t *vt) /* check for support or just update a named variable */ static int query_ups(const char *var, int first) { - int ret; + ssize_t ret; char temp[256]; const char *ptr; apc_vartab_t *vt; @@ -270,7 +271,7 @@ static int query_ups(const char *var, int first) return 0; /* empty the input buffer (while allowing the alert handler to run) */ - ret = ser_get_line_alert(upsfd, temp, sizeof(temp), ENDCHAR, + ret = ser_get_line_alert(upsfd, temp, sizeof(temp), ENDCHAR, POLL_IGNORE, POLL_ALERT, alert_handler, 0, 0); ret = ser_send_char(upsfd, vt->cmd); @@ -280,7 +281,7 @@ static int query_ups(const char *var, int first) return 0; } - ret = ser_get_line_alert(upsfd, temp, sizeof(temp), ENDCHAR, + ret = ser_get_line_alert(upsfd, temp, sizeof(temp), ENDCHAR, POLL_IGNORE, POLL_ALERT, alert_handler, SER_WAIT_SEC, SER_WAIT_USEC); @@ -305,7 +306,9 @@ static void do_capabilities(void) { const char *ptr, *entptr; char upsloc, temp[512], cmd, loc, etmp[16], *endtemp; - int nument, entlen, i, matrix, ret, valid; + int matrix, valid; + size_t i, nument, entlen; + ssize_t ret; apc_vartab_t *vt; upsdebugx(1, "APC - About to get capabilities string"); @@ -327,7 +330,7 @@ static void do_capabilities(void) } /* note different IGN set since ^Z returns things like # */ - ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, + ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, MINIGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); if ((ret < 1) || (!strcmp(temp, "NA"))) { @@ -347,7 +350,7 @@ static void do_capabilities(void) if (temp[0] != '#') { upsdebugx(1, "Unrecognized capability start char %c", temp[0]); upsdebugx(1, "Please report this error [%s]", temp); - upslogx(LOG_ERR, "ERROR: unknown capability start char %c!", + upslogx(LOG_ERR, "ERROR: unknown capability start char %c!", temp[0]); return; @@ -375,7 +378,7 @@ static void do_capabilities(void) if (quirk_capability_overflow) return; - fatalx(EXIT_FAILURE, + fatalx(EXIT_FAILURE, "Capability string has overflowed\n" "Please report this error\n" "ERROR: capability overflow!" @@ -384,8 +387,17 @@ static void do_capabilities(void) cmd = ptr[0]; loc = ptr[1]; - nument = ptr[2] - 48; - entlen = ptr[3] - 48; + if (ptr[2] < 48 || ptr[3] < 48) { + upsdebugx(0, + "%s: nument (%d) or entlen (%d) out of range", + __func__, (ptr[2] - 48), (ptr[3] - 48)); + fatalx(EXIT_FAILURE, + "nument or entlen out of range\n" + "Please report this error\n" + "ERROR: capability overflow!"); + } + nument = (size_t)ptr[2] - 48; + entlen = (size_t)ptr[3] - 48; entptr = &ptr[4]; vt = vartab_lookup_char(cmd); @@ -393,7 +405,7 @@ static void do_capabilities(void) /* mark this as writable */ if (valid) { - upsdebugx(1, "Supported capability: %02x (%c) - %s", + upsdebugx(1, "Supported capability: %02x (%c) - %s", cmd, loc, vt->name); dstate_setflags(vt->name, ST_FLAG_RW); @@ -417,7 +429,7 @@ static void do_capabilities(void) static int update_status(void) { - int ret; + ssize_t ret; char buf[SMALLBUF]; upsdebugx(4, "update_status"); @@ -449,7 +461,7 @@ static int update_status(void) static void oldapcsetup(void) { - int ret = 0; + ssize_t ret = 0; /* really old models ignore REQ_MODEL, so find them first */ ret = query_ups("ups.model", 1); @@ -492,7 +504,7 @@ static void protocol_verify(unsigned char cmd) /* handle special data for our two strings */ if (apc_vartab[i].flags & APC_STRING) { - dstate_setflags(apc_vartab[i].name, + dstate_setflags(apc_vartab[i].name, ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux(apc_vartab[i].name, APC_STRLEN); @@ -524,8 +536,8 @@ static void protocol_verify(unsigned char cmd) if (found) return; - if (isprint(cmd)) - upsdebugx(1, "protocol_verify: 0x%02x [%c] unrecognized", + if (isprint((size_t)cmd)) + upsdebugx(1, "protocol_verify: 0x%02x [%c] unrecognized", cmd, cmd); else upsdebugx(1, "protocol_verify: 0x%02x unrecognized", cmd); @@ -534,7 +546,7 @@ static void protocol_verify(unsigned char cmd) /* some hardware is a special case - hotwire the list of cmdchars */ static int firmware_table_lookup(void) { - int ret; + ssize_t ret; unsigned int i, j; char buf[SMALLBUF]; @@ -547,7 +559,7 @@ static int firmware_table_lookup(void) return 0; } - ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, IGNCHARS, + ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); /* @@ -594,21 +606,22 @@ static int firmware_table_lookup(void) /* matched - run the cmdchars from the table */ for (j = 0; j < strlen(compat_tab[i].cmdchars); j++) - protocol_verify(compat_tab[i].cmdchars[j]); + protocol_verify((const unsigned char)(compat_tab[i].cmdchars[j])); return 1; /* matched */ } } upsdebugx(2, "Not found in table - trying normal method"); - return 0; + return 0; } static void getbaseinfo(void) { unsigned int i; - int ret = 0; - char *alrts, *cmds, temp[512]; + ssize_t ret = 0; + char *alrts, temp[512]; + unsigned char *cmds; /* * try firmware lookup first; we could start with 'a', but older models @@ -630,7 +643,7 @@ static void getbaseinfo(void) return; } - ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, + ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); if ((ret < 1) || (!strcmp(temp, "NA"))) { @@ -652,17 +665,17 @@ static void getbaseinfo(void) } *alrts++ = 0; - cmds = strchr(alrts, '.'); + cmds = (unsigned char*)strchr(alrts, '.'); if (cmds == NULL) { fatalx(EXIT_FAILURE, "Unable to find APC command string"); } *cmds++ = 0; - for (i = 0; i < strlen(cmds); i++) + for (i = 0; i < strlen((char *)cmds); i++) protocol_verify(cmds[i]); /* if capabilities are supported, add them here */ - if (strchr(cmds, APC_CAPABILITY)) + if (strchr((char *)cmds, APC_CAPABILITY)) do_capabilities(); upsdebugx(1, "APC - UPS capabilities determined"); @@ -672,7 +685,8 @@ static void getbaseinfo(void) static int do_cal(int start) { char temp[256]; - int tval, ret; + long tval; + ssize_t ret; ret = ser_send_char(upsfd, APC_STATUS); @@ -710,7 +724,7 @@ static int do_cal(int start) ret = read_buf(temp, sizeof(temp)); if ((ret < 1) || (!strcmp(temp, "NA")) || (!strcmp(temp, "NO"))) { - upslogx(LOG_WARNING, "Stop calibration failed: %s", + upslogx(LOG_WARNING, "Stop calibration failed: %s", temp); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } @@ -747,7 +761,8 @@ static int do_cal(int start) /* get the UPS talking to us in smart mode */ static int smartmode(void) { - int ret, tries; + ssize_t ret; + int tries; char temp[256]; for (tries = 0; tries < 5; tries++) { @@ -759,7 +774,7 @@ static int smartmode(void) return 0; } - ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, + ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); if (ret > 0) @@ -778,7 +793,7 @@ static int smartmode(void) } /* eat the response (might be NA, might be something else) */ - ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, + ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); } @@ -788,7 +803,7 @@ static int smartmode(void) /* * all shutdown commands should respond with 'OK' or '*' */ -static int sdok(void) +static long sdok(void) { char temp[16]; @@ -805,8 +820,10 @@ static int sdok(void) } /* soft hibernate: S - working only when OB, otherwise ignored */ -static int sdcmd_S(int dummy) +static long sdcmd_S(long dummy) { + NUT_UNUSED_VARIABLE(dummy); + ser_flush_in(upsfd, IGNCHARS, nut_debug_level); upsdebugx(1, "Issuing soft hibernate"); @@ -816,7 +833,7 @@ static int sdcmd_S(int dummy) } /* soft hibernate, hack version for CS 350 */ -static int sdcmd_CS(int tval) +static long sdcmd_CS(long tval) { upsdebugx(1, "Using CS 350 'force OB' shutdown method"); if (tval & APC_STAT_OL) { @@ -832,9 +849,10 @@ static int sdcmd_CS(int tval) * note: works differently for older and new models, see help function for * detailed info */ -static int sdcmd_ATn(int cnt) +static long sdcmd_ATn(long cnt) { - int n = 0, mmax, ret; + long n = 0; + long mmax, ret; const char *strval; char timer[4]; @@ -847,10 +865,13 @@ static int sdcmd_ATn(int cnt) n = 0; } - snprintf(timer, sizeof(timer), "%.*d", cnt, n); + if (cnt > INT_MAX || cnt < 0) { + fatalx(EXIT_FAILURE, "Error: %s: cnt (%ld) is out of range", __func__, cnt); + } + snprintf(timer, sizeof(timer), "%.*ld", (int)cnt, n); ser_flush_in(upsfd, IGNCHARS, nut_debug_level); - upsdebugx(1, "Issuing hard hibernate with %d minutes additional wakeup delay", n*6); + upsdebugx(1, "Issuing hard hibernate with %ld minutes additional wakeup delay", n*6); ser_send_char(upsfd, APC_CMD_GRACEDOWN); usleep(CMDLONGDELAY); @@ -874,8 +895,10 @@ static int sdcmd_ATn(int cnt) } /* shutdown: K - delayed poweroff */ -static int sdcmd_K(int dummy) +static long sdcmd_K(long dummy) { + NUT_UNUSED_VARIABLE(dummy); + ser_flush_in(upsfd, IGNCHARS, nut_debug_level); upsdebugx(1, "Issuing delayed poweroff"); @@ -887,8 +910,10 @@ static int sdcmd_K(int dummy) } /* shutdown: Z - immediate poweroff */ -static int sdcmd_Z(int dummy) +static long sdcmd_Z(long dummy) { + NUT_UNUSED_VARIABLE(dummy); + ser_flush_in(upsfd, IGNCHARS, nut_debug_level); upsdebugx(1, "Issuing immediate poweroff"); @@ -899,7 +924,7 @@ static int sdcmd_Z(int dummy) return sdok(); } -static int (*sdlist[])(int) = { +static long (*sdlist[])(long) = { sdcmd_S, sdcmd_ATn, /* for @nnn version */ sdcmd_K, @@ -917,9 +942,9 @@ static int (*sdlist[])(int) = { #define SDCNT 6 -static void upsdrv_shutdown_simple(int status) +static void upsdrv_shutdown_simple(long status) { - unsigned int sdtype = 0; + long sdtype = 0; char *strval; if ((strval = getval("sdtype"))) { @@ -975,7 +1000,7 @@ static void upsdrv_shutdown_simple(int status) } } -static void upsdrv_shutdown_advanced(int status) +static void upsdrv_shutdown_advanced(long status) { const char *strval; const char deforder[] = {48 + SDIDX_S, @@ -984,7 +1009,7 @@ static void upsdrv_shutdown_advanced(int status) 48 + SDIDX_Z, 0}; size_t i; - int n; + long n; strval = getval("advorder"); @@ -1026,7 +1051,8 @@ static void upsdrv_shutdown_advanced(int status) void upsdrv_shutdown(void) { char temp[32]; - int ret, status; + ssize_t ret; + long status; if (!smartmode()) upsdebugx(1, "SM detection failed. Trying a shutdown command anyway"); @@ -1066,7 +1092,7 @@ static void init_serial_0095B(void) static void update_info_normal(void) { - int i; + size_t i; upsdebugx(3, "update_info_normal: starting"); @@ -1086,7 +1112,7 @@ static void update_info_normal(void) static void update_info_all(void) { - int i; + size_t i; upsdebugx(3, "update_info_all: starting"); @@ -1103,7 +1129,8 @@ static void update_info_all(void) static int setvar_enum(apc_vartab_t *vt, const char *val) { - int i, ret; + int i; + ssize_t ret; char orig[256], temp[256]; const char *ptr; @@ -1183,7 +1210,7 @@ static int setvar_enum(apc_vartab_t *vt, const char *val) vt->name); return STAT_SET_HANDLED; /* FUTURE: failed */ - } + } } upslogx(LOG_ERR, "setvar: gave up after 6 tries for %s", @@ -1197,8 +1224,8 @@ static int setvar_enum(apc_vartab_t *vt, const char *val) static int setvar_string(apc_vartab_t *vt, const char *val) { - unsigned int i; - int ret; + size_t i; + ssize_t ret; char temp[256]; ser_flush_in(upsfd, IGNCHARS, nut_debug_level); @@ -1232,7 +1259,7 @@ static int setvar_string(apc_vartab_t *vt, const char *val) usleep(UPSDELAY); for (i = 0; i < strlen(val); i++) { - ret = ser_send_char(upsfd, val[i]); + ret = ser_send_char(upsfd, (const unsigned char)(val[i])); if (ret != 1) { upslog_with_errno(LOG_ERR, "setvar_string: ser_send_char failed"); @@ -1301,7 +1328,7 @@ static int setvar(const char *varname, const char *val) /* actually send the instcmd's char to the ups */ static int do_cmd(apc_cmdtab_t *ct) { - int ret; + ssize_t ret; char buf[SMALLBUF]; ser_flush_in(upsfd, IGNCHARS, nut_debug_level); @@ -1374,13 +1401,14 @@ static int instcmd(const char *cmdname, const char *extra) ct = &apc_cmdtab[i]; if (!ct) { - upslogx(LOG_WARNING, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_WARNING, "instcmd: unknown command [%s] [%s]", + cmdname, extra); return STAT_INSTCMD_UNKNOWN; } if ((ct->flags & APC_PRESENT) == 0) { - upslogx(LOG_WARNING, "instcmd: command [%s] is not supported", - cmdname); + upslogx(LOG_WARNING, "instcmd: command [%s] [%s] is not supported", + cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -1395,7 +1423,7 @@ static int instcmd(const char *cmdname, const char *extra) /* nothing special here */ return do_cmd(ct); -} +} /* install pointers to functions for msg handlers called from msgparse */ static void setuphandlers(void) @@ -1439,7 +1467,7 @@ void upsdrv_initinfo(void) const char *pmod, *pser; if (!smartmode()) { - fatalx(EXIT_FAILURE, + fatalx(EXIT_FAILURE, "Unable to detect an APC Smart protocol UPS on port %s\n" "Check the cabling, port name or model name and try again", device_path ); diff --git a/drivers/apcsmart-old.h b/drivers/apcsmart-old.h index f143cfa..6c24da7 100644 --- a/drivers/apcsmart-old.h +++ b/drivers/apcsmart-old.h @@ -18,6 +18,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef NUT_APCSMART_OLD_H_SEEN +#define NUT_APCSMART_OLD_H_SEEN 1 + #include #include #include "serial.h" @@ -57,14 +60,14 @@ /* status bits */ -#define APC_STAT_CAL 1 /* calibration */ -#define APC_STAT_TRIM 2 /* SmartTrim */ -#define APC_STAT_BOOST 4 /* SmartBoost */ -#define APC_STAT_OL 8 /* on line */ -#define APC_STAT_OB 16 /* on battery */ -#define APC_STAT_OVER 32 /* overload */ -#define APC_STAT_LB 64 /* low battery */ -#define APC_STAT_RB 128 /* replace battery */ +#define APC_STAT_CAL 1L /* calibration */ +#define APC_STAT_TRIM 2L /* SmartTrim */ +#define APC_STAT_BOOST 4L /* SmartBoost */ +#define APC_STAT_OL 8L /* on line */ +#define APC_STAT_OB 16L /* on battery */ +#define APC_STAT_OVER 32L /* overload */ +#define APC_STAT_LB 64L /* low battery */ +#define APC_STAT_RB 128L /* replace battery */ /* serial protocol: special commands - initialization and such */ #define APC_STATUS 'Q' @@ -105,10 +108,10 @@ typedef struct { const char *name; /* the variable name */ unsigned int flags; /* various flags */ - char cmd; /* command character */ + unsigned char cmd; /* command character */ } apc_vartab_t; -apc_vartab_t apc_vartab[] = { +static apc_vartab_t apc_vartab[] = { { "ups.firmware.old", 0, 'V' }, { "ups.firmware", 0, 'b' }, @@ -141,7 +144,7 @@ apc_vartab_t apc_vartab[] = { { "input.transfer.low", APC_F_VOLT, 'l' }, { "input.transfer.high", APC_F_VOLT, 'u' }, - { "input.transfer.reason", + { "input.transfer.reason", APC_POLL|APC_F_REASON, 'G' }, { "input.voltage.maximum", @@ -151,30 +154,30 @@ apc_vartab_t apc_vartab[] = { { "output.current", APC_POLL|APC_F_AMP, '/' }, { "output.voltage", APC_POLL|APC_F_VOLT, 'O' }, - { "output.voltage.nominal", + { "output.voltage.nominal", APC_F_VOLT, 'o' }, { "ambient.humidity", APC_POLL|APC_F_PERCENT, 'h' }, - { "ambient.humidity.high", + { "ambient.humidity.high", APC_F_PERCENT, '{' }, - { "ambient.humidity.low", + { "ambient.humidity.low", APC_F_PERCENT, '}' }, - { "ambient.temperature", + { "ambient.temperature", APC_POLL|APC_F_CELSIUS, 't' }, - { "ambient.temperature.high", + { "ambient.temperature.high", APC_F_CELSIUS, '[' }, - { "ambient.temperature.low", + { "ambient.temperature.low", APC_F_CELSIUS, ']' }, { "battery.date", APC_STRING, 'x' }, { "battery.charge", APC_POLL|APC_F_PERCENT, 'f' }, - { "battery.charge.restart", + { "battery.charge.restart", APC_F_PERCENT, 'e' }, { "battery.voltage", APC_POLL|APC_F_VOLT, 'B' }, - { "battery.voltage.nominal", + { "battery.voltage.nominal", 0, 'g' }, { "battery.runtime", APC_POLL|APC_F_MINUTES, 'j' }, @@ -183,7 +186,7 @@ apc_vartab_t apc_vartab[] = { { "battery.packs", APC_F_DEC, '>' }, { "battery.packs.bad", APC_F_DEC, '<' }, - { "battery.alarm.threshold", + { "battery.alarm.threshold", 0, 'k' }, /* todo: @@ -215,10 +218,10 @@ apc_vartab_t apc_vartab[] = { typedef struct { const char *name; int flags; - char cmd; + unsigned char cmd; } apc_cmdtab_t; -apc_cmdtab_t apc_cmdtab[] = +static apc_cmdtab_t apc_cmdtab[] = { { "load.off", APC_NASTY|APC_REPEAT, APC_CMD_OFF }, { "load.on", APC_REPEAT, APC_CMD_ON }, @@ -246,7 +249,7 @@ apc_cmdtab_t apc_cmdtab[] = /* compatibility with hardware that doesn't do APC_CMDSET ('a') */ -struct { +static struct { const char *firmware; const char *cmdchars; int flags; @@ -289,3 +292,5 @@ struct { { NULL, NULL, 0 }, }; + +#endif /* NUT_APCSMART_OLD_H_SEEN */ diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 93d46ff..bb82e19 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -3,7 +3,7 @@ * * Copyright (C) 1999 Russell Kroll * (C) 2000 Nigel Metheringham - * (C) 2011 Michal Soltys + * (C) 2011+ Michal Soltys * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,14 +20,17 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "main.h" /* Must be first, includes "config.h" */ + #include #include #include #include +#include /* strcasecmp() */ -#include "main.h" #include "serial.h" #include "timehead.h" +#include "nut_stdint.h" #include "apcsmart.h" #include "apcsmart_tabs.h" @@ -43,7 +46,7 @@ upsdrv_info_t upsdrv_info = { { &apc_tab_info, NULL } }; -static int ups_status = 0; +static long ups_status = 0; /* some forwards */ @@ -53,6 +56,15 @@ static int sdcmd_K(const void *); static int sdcmd_Z(const void *); static int sdcmd_CS(const void *); +/* + * following table *must* match order defined in the man page, namely: + * 0:: soft hibernate (*S*) + * 1:: hard hibernate (*@*) + * 2:: delayed poweroff (*K*) + * 3:: instant poweroff (*Z*) + * 4:: "force OB hack" (*CS*) + */ + static int (*sdlist[])(const void *) = { sdcmd_S, sdcmd_AT, @@ -61,65 +73,81 @@ static int (*sdlist[])(const void *) = { sdcmd_CS, }; -#define SDIDX_S 0 #define SDIDX_AT 1 -#define SDIDX_K 2 -#define SDIDX_Z 3 -#define SDIDX_CS 4 -#define SDCNT ((int)(sizeof(sdlist)/sizeof(sdlist[0]))) -static apc_vartab_t *vartab_lookup_char(char cmdchar) +/* + * note: both lookup functions MUST be used after variable detection is + * completed - that is after deprecate_vars() call; the general reason for this + * is 1:n and n:1 nut <-> apc mappings, which are not determined prior to the + * detection + */ +static apc_vartab_t *vt_lookup_char(char cmdchar) { int i; for (i = 0; apc_vartab[i].name != NULL; i++) - if (apc_vartab[i].cmd == cmdchar) + if ((apc_vartab[i].flags & APC_PRESENT) && + apc_vartab[i].cmd == cmdchar) return &apc_vartab[i]; return NULL; } -static apc_vartab_t *vartab_lookup_name(const char *var) +static apc_vartab_t *vt_lookup_name(const char *var) { int i; for (i = 0; apc_vartab[i].name != NULL; i++) - if (!(apc_vartab[i].flags & APC_DEPR) && + if ((apc_vartab[i].flags & APC_PRESENT) && !strcasecmp(apc_vartab[i].name, var)) return &apc_vartab[i]; return NULL; } -/* FUTURE: change to use function pointers */ +static const char *prtchr(char x) +{ + static size_t curr = 24; + static char info[32]; + + curr = (curr + 8) & 0x1F; + snprintf(info + curr, 8, isprint((size_t)x) ? "%c" : "0x%02x", x); + + return info + curr; +} static int rexhlp(const char *rex, const char *val) { + static const char *empty = ""; int ret; regex_t mbuf; + if (!rex || !*rex) + return 1; + if (!val) + val = empty; regcomp(&mbuf, rex, REG_EXTENDED|REG_NOSUB); - ret = regexec(&mbuf, val, 0,0,0); + ret = regexec(&mbuf, val, 0, 0, 0); regfree(&mbuf); - return ret; + return !ret; } /* convert APC formatting to NUT formatting */ /* TODO: handle errors better */ -static const char *convert_data(apc_vartab_t *cmd_entry, const char *upsval) +static const char *convert_data(apc_vartab_t *vt, const char *upsval) { static char temp[APC_LBUF]; - int tval; + long tval; /* this should never happen */ if (strlen(upsval) >= sizeof(temp)) { - upslogx(LOG_CRIT, "length of [%s] too long", cmd_entry->name); - strncpy(temp, upsval, sizeof(temp) - 1); + upslogx(LOG_CRIT, "%s: the length of [%s] is too big", __func__, vt->name); + memcpy(temp, upsval, sizeof(temp) - 1); temp[sizeof(temp) - 1] = '\0'; return temp; } - switch(cmd_entry->flags & APC_F_MASK) { + switch (vt->flags & APC_F_MASK) { case APC_F_PERCENT: case APC_F_VOLT: case APC_F_AMP: @@ -137,14 +165,14 @@ static const char *convert_data(apc_vartab_t *cmd_entry, const char *upsval) tval = 60 * 60 * strtol(upsval, NULL, 10); - snprintf(temp, sizeof(temp), "%d", tval); + snprintf(temp, sizeof(temp), "%ld", tval); return temp; case APC_F_MINUTES: /* Convert to seconds - NUT standard time measurement */ tval = 60 * strtol(upsval, NULL, 10); /* Ignore errors - there's not much we can do */ - snprintf(temp, sizeof(temp), "%d", tval); + snprintf(temp, sizeof(temp), "%ld", tval); return temp; case APC_F_REASON: @@ -161,15 +189,105 @@ static const char *convert_data(apc_vartab_t *cmd_entry, const char *upsval) } } - upslogx(LOG_NOTICE, "Unable to handle conversion of [%s]", cmd_entry->name); + /* this should never happen */ + upslogx(LOG_CRIT, "%s: unable to convert [%s]", __func__, vt->name); strcpy(temp, upsval); return temp; } +/* report differences if tcsetattr != tcgetattr, return otherwise */ + +/* + * Aix compatible names + */ +#if defined(VWERSE) && !defined(VWERASE) +#define VWERASE VWERSE +#endif /* VWERSE && !VWERASE */ + +#if defined(VDISCRD) && !defined(VDISCARD) +#define VDISCARD VDISCRD +#endif /* VDISCRD && !VDISCARD */ + +static void apc_ser_diff(struct termios *tioset, struct termios *tioget) +{ + size_t i; + const char dir[] = { 's', 'g' }; + struct termios *tio[] = { tioset, tioget }; + struct cchar { + const char *name; + unsigned int sub; + }; + const struct cchar cchars1[] = { +#ifdef VDISCARD + { "discard", VDISCARD }, +#endif +#ifdef VDSUSP + { "dsusp", VDSUSP }, +#endif + { "eof", VEOF }, + { "eol", VEOL }, + { "eol2", VEOL2 }, + { "erase", VERASE }, +#ifdef VINTR + { "intr", VINTR }, +#endif + { "kill", VKILL }, + { "lnext", VLNEXT }, + { "min", VMIN }, + { "quit", VQUIT }, +#ifdef VREPRINT + { "reprint", VREPRINT }, +#endif + { "start", VSTART }, +#ifdef VSTATUS + { "status", VSTATUS }, +#endif + { "stop", VSTOP }, + { "susp", VSUSP }, + { "time", VTIME }, + { "werase", VWERASE }, + { NULL, 0 }, + }, *cp; + + /* clear status flags so that they don't affect our binary compare */ +#if defined(PENDIN) || defined(FLUSHO) + for (i = 0; i < sizeof(tio)/sizeof(tio[0]); i++) { +#ifdef PENDIN + tio[i]->c_lflag &= ~(unsigned int)PENDIN; +#endif +#ifdef FLUSHO + tio[i]->c_lflag &= ~(unsigned int)FLUSHO; +#endif + } +#endif /* defined(PENDIN) || defined(FLUSHO) */ + + if (!memcmp(tio[0], tio[1], sizeof(*tio[0]))) + return; + + upslogx(LOG_NOTICE, "%s: device reports different attributes than requested", device_path); + + /* + * According to the manual the most common problem is mis-matched + * combinations of input and output baud rates. If the combination is + * not supported then neither are changed. This should not be a + * problem here since we set them both to the same extremely common + * rate of 2400. + */ + + for (i = 0; i < sizeof(tio)/sizeof(tio[0]); i++) { + upsdebugx(1, "tc%cetattr(): gfmt1:cflag=%x:iflag=%x:lflag=%x:oflag=%x:", dir[i], + (unsigned int) tio[i]->c_cflag, (unsigned int) tio[i]->c_iflag, + (unsigned int) tio[i]->c_lflag, (unsigned int) tio[i]->c_oflag); + for (cp = cchars1; cp->name; ++cp) + upsdebugx(1, "\t%s=%x:", cp->name, tio[i]->c_cc[cp->sub]); + upsdebugx(1, "\tispeed=%d:ospeed=%d", (int) cfgetispeed(tio[i]), (int) cfgetospeed(tio[i])); + } +} + static void apc_ser_set(void) { struct termios tio, tio_chk; - char *cable; + char *val; /* * this must be called before the rest, as ser_set_speed() performs @@ -177,24 +295,42 @@ static void apc_ser_set(void) */ ser_set_speed(upsfd, device_path, B2400); + val = getval("cable"); + if (val && !strcasecmp(val, ALT_CABLE_1)) { + if (ser_set_dtr(upsfd, 1) == -1) + fatalx(EXIT_FAILURE, "%s: ser_set_dtr(%s) failed", __func__, device_path); + if (ser_set_rts(upsfd, 0) == -1) + fatalx(EXIT_FAILURE, "%s: ser_set_rts(%s) failed", __func__, device_path); + } + + /* + * that's all if we want simple non canonical mode; this is meant as a + * compatibility measure for windows systems and perhaps some + * problematic serial cards/converters + */ + if ((val = getval("ttymode")) && !strcmp(val, "raw")) + return; + memset(&tio, 0, sizeof(tio)); errno = 0; if (tcgetattr(upsfd, &tio)) - fatal_with_errno(EXIT_FAILURE, "tcgetattr(%s)", device_path); + fatal_with_errno(EXIT_FAILURE, "%s: tcgetattr(%s)", __func__, device_path); /* set port mode: common stuff, canonical processing */ tio.c_cflag |= (CS8 | CLOCAL | CREAD); tio.c_lflag |= ICANON; - tio.c_lflag &= ~ISIG; +#ifdef NOKERNINFO + tio.c_lflag |= NOKERNINFO; +#endif + tio.c_lflag &= ~(unsigned int)(ISIG | IEXTEN); tio.c_iflag |= (IGNCR | IGNPAR); - tio.c_iflag &= ~(IXON | IXOFF); + tio.c_iflag &= ~(unsigned int)(IXON | IXOFF); tio.c_cc[VEOL] = '*'; /* specially handled in apc_read() */ - #ifdef _POSIX_VDISABLE tio.c_cc[VERASE] = _POSIX_VDISABLE; tio.c_cc[VKILL] = _POSIX_VDISABLE; @@ -202,14 +338,8 @@ static void apc_ser_set(void) tio.c_cc[VEOL2] = _POSIX_VDISABLE; #endif -#if 0 - /* unused in canonical mode: */ - tio.c_cc[VMIN] = 1; - tio.c_cc[VTIME] = 0; -#endif - if (tcflush(upsfd, TCIOFLUSH)) - fatal_with_errno(EXIT_FAILURE, "tcflush(%s)", device_path); + fatal_with_errno(EXIT_FAILURE, "%s: tcflush(%s)", __func__, device_path); /* * warn: @@ -218,21 +348,13 @@ static void apc_ser_set(void) * test. */ if (tcsetattr(upsfd, TCSANOW, &tio)) - fatal_with_errno(EXIT_FAILURE, "tcsetattr(%s)", device_path); + fatal_with_errno(EXIT_FAILURE, "%s: tcsetattr(%s)", __func__, device_path); memset(&tio_chk, 0, sizeof(tio_chk)); if (tcgetattr(upsfd, &tio_chk)) - fatal_with_errno(EXIT_FAILURE, "tcgetattr(%s)", device_path); - if (memcmp(&tio_chk, &tio, sizeof(tio))) - fatalx(EXIT_FAILURE, "unable to set the required attributes (%s)", device_path); + fatal_with_errno(EXIT_FAILURE, "%s: tcgetattr(%s)", __func__, device_path); - cable = getval("cable"); - if (cable && !strcasecmp(cable, ALT_CABLE_1)) { - if (ser_set_dtr(upsfd, 1) == -1) - fatalx(EXIT_FAILURE, "ser_set_dtr() failed (%s)", device_path); - if (ser_set_rts(upsfd, 0) == -1) - fatalx(EXIT_FAILURE, "ser_set_rts() failed (%s)", device_path); - } + apc_ser_diff(&tio, &tio_chk); } static void ups_status_set(void) @@ -265,44 +387,44 @@ static void alert_handler(char ch) { switch (ch) { case '!': /* clear OL, set OB */ - upsdebugx(4, "alert_handler: OB"); + upsdebugx(1, "%s: %s", __func__, "OB"); ups_status &= ~APC_STAT_OL; ups_status |= APC_STAT_OB; break; case '$': /* clear OB, set OL */ - upsdebugx(4, "alert_handler: OL"); + upsdebugx(1, "%s: %s", __func__, "OL"); ups_status &= ~APC_STAT_OB; ups_status |= APC_STAT_OL; break; case '%': /* set LB */ - upsdebugx(4, "alert_handler: LB"); + upsdebugx(1, "%s: %s", __func__, "LB"); ups_status |= APC_STAT_LB; break; case '+': /* clear LB */ - upsdebugx(4, "alert_handler: not LB"); + upsdebugx(1, "%s: %s", __func__, "not LB"); ups_status &= ~APC_STAT_LB; break; case '#': /* set RB */ - upsdebugx(4, "alert_handler: RB"); + upsdebugx(1, "%s: %s", __func__, "RB"); ups_status |= APC_STAT_RB; break; case '?': /* set OVER */ - upsdebugx(4, "alert_handler: OVER"); + upsdebugx(1, "%s: %s", __func__, "OVER"); ups_status |= APC_STAT_OVER; break; case '=': /* clear OVER */ - upsdebugx(4, "alert_handler: not OVER"); + upsdebugx(1, "%s: %s", __func__, "not OVER"); ups_status &= ~APC_STAT_OVER; break; default: - upsdebugx(4, "alert_handler got 0x%02x (unhandled)", ch); + upsdebugx(1, "%s: got 0x%02x (unhandled)", __func__, ch); break; } @@ -313,13 +435,19 @@ static void alert_handler(char ch) * we need a tiny bit different processing due to '*' and canonical mode; the * function is subtly different from generic ser_get_line_alert() */ -static int apc_read(char *buf, size_t buflen, int flags) +#define apc_read(b, l, f) apc_read_i(b, l, f, __func__, __LINE__) +static ssize_t apc_read_i(char *buf, size_t buflen, int flags, const char *fn, unsigned int ln) { const char *iset = IGN_CHARS, *aset = ""; size_t count = 0; - int i, ret, sec = 3, usec = 0; + ssize_t i, ret; + int sec = 3, usec = 0; char temp[APC_LBUF]; + if (buflen > (size_t)SSIZE_MAX) { + fatalx (EXIT_FAILURE, "Error: apc_read_i called with buflen too large"); + } + if (upsfd == -1) return 0; if (flags & SER_D0) { @@ -355,9 +483,17 @@ static int apc_read(char *buf, size_t buflen, int flags) errno = 0; ret = select_read(upsfd, temp, sizeof(temp), sec, usec); + /* partial timeout (non-canon only paranoid check) */ + if (ret == 0 && count) { + ser_comm_fail("serial port partial timeout: %u(%s)", ln, fn); + return -1; + } /* error or no timeout allowed */ if (ret < 0 || (ret == 0 && !(flags & SER_TO))) { - ser_comm_fail("%s", ret ? strerror(errno) : "timeout"); + if (ret) + ser_comm_fail("serial port read error: %u(%s): %s", ln, fn, strerror(errno)); + else + ser_comm_fail("serial port read timeout: %u(%s)", ln, fn); return ret; } /* ok, timeout is acceptable */ @@ -365,22 +501,28 @@ static int apc_read(char *buf, size_t buflen, int flags) /* * but it doesn't imply ser_comm_good * - * to be more precise - we might be in comm_fail - * condition, trying to "nudge" the UPS with some - * command obviously expecting timeout if the comm is - * still lost. This would result with filling logs with - * confusing comm lost/comm re-established pairs. Thus - * - just return here. + * for example we might be in comm_fail condition, + * trying to "nudge" the UPS with some command + * obviously expecting timeout if the comm is still + * lost. This would result with filling logs with + * confusing comm lost/comm re-established pairs due to + * successful serial writes */ - return count; + return 0; } /* parse input */ for (i = 0; i < ret; i++) { + /* overflow read */ + if (count == buflen - 1) { + ser_comm_fail("serial port read overflow: %u(%s)", ln, fn); + tcflush(upsfd, TCIFLUSH); + return -1; + } /* standard "line received" condition */ - if ((count == buflen - 1) || (temp[i] == ENDCHAR)) { + if (temp[i] == ENDCHAR) { ser_comm_good(); - return count; + return (ssize_t)count; } /* * '*' is set as a secondary EOL; convert to 'OK' only as a @@ -399,7 +541,7 @@ static int apc_read(char *buf, size_t buflen, int flags) errno = 0; ret = select_read(upsfd, temp, sizeof(temp), 1, 0); if (ret < 0) { - ser_comm_fail("%s", strerror(errno)); + ser_comm_fail("serial port read error: %u(%s): %s", ln, fn, strerror(errno)); return ret; } buf[0] = 'O'; @@ -422,14 +564,30 @@ static int apc_read(char *buf, size_t buflen, int flags) } ser_comm_good(); - return count; + /* buflen range limited above */ + return (ssize_t)count; } -static int apc_write(unsigned char code) + +#define apc_write(code) apc_write_i(code, __func__, __LINE__) +static ssize_t apc_write_i(unsigned char code, const char *fn, unsigned int ln) { + ssize_t ret; errno = 0; + if (upsfd == -1) return 0; - return ser_send_char(upsfd, code); + + ret = ser_send_char(upsfd, code); + /* + * Formally any write() sould never return 0, if the count != 0. For + * the sake of handling any obscure nonsense, we consider such return + * as a failure - thus <= condition; either way, LE is pretty hard + * condition hardly ever happening; + */ + if (ret <= 0) + ser_comm_fail("serial port write error: %u(%s): %s", ln, fn, strerror(errno)); + + return ret; } /* @@ -445,16 +603,13 @@ static int apc_write(unsigned char code) * We also set errno to something usable, so outside upslog calls don't output * confusing "success". */ -static int apc_write_long(const char *code) +#define apc_write_long(code) apc_write_long_i(code, __func__, __LINE__) +static ssize_t apc_write_long_i(const char *code, const char *fn, unsigned int ln) { char temp[APC_LBUF]; - int ret; - errno = 0; + ssize_t ret; - if (upsfd == -1) - return 0; - - ret = ser_send_char(upsfd, *code); + ret = apc_write_i((const unsigned char)(*code), fn, ln); if (ret != 1) return ret; /* peek for the answer - anything at this point is failure */ @@ -464,19 +619,22 @@ static int apc_write_long(const char *code) return -1; } - return ser_send_pace(upsfd, 50000, "%s", code + 1); + ret = ser_send_pace(upsfd, 50000, "%s", code + 1); + if (ret >= 0) + ret++; + /* see remark in plain apc_write() */ + if (ret != (int)strlen(code)) + ser_comm_fail("serial port write error: %u(%s): %s", ln, fn, strerror(errno)); + return ret; } -static int apc_write_rep(unsigned char code) +#define apc_write_rep(code) apc_write_rep_i(code, __func__, __LINE__) +static ssize_t apc_write_rep_i(unsigned char code, const char *fn, unsigned int ln) { char temp[APC_LBUF]; - int ret; - errno = 0; + ssize_t ret; - if (upsfd == -1) - return 0; - - ret = ser_send_char(upsfd, code); + ret = apc_write_i(code, fn, ln); if (ret != 1) return ret; /* peek for the answer - anything at this point is failure */ @@ -485,12 +643,12 @@ static int apc_write_rep(unsigned char code) errno = ECANCELED; return -1; } - usleep(1300000); - ret = ser_send_char(upsfd, code); - if (ret != 1) - return ret; - return 2; + + ret = apc_write_i(code, fn, ln); + if (ret >= 0) + ret++; + return ret; } /* all flags other than SER_AA are ignored */ @@ -500,7 +658,8 @@ static void apc_flush(int flags) if (flags & SER_AA) { tcflush(upsfd, TCOFLUSH); - while(apc_read(temp, sizeof(temp), SER_D0|SER_TO|SER_AA)); + /* TODO */ + while(apc_read(temp, sizeof(temp), SER_D0|SER_TO|SER_AA) > 0); } else { tcflush(upsfd, TCIOFLUSH); /* tcflush(upsfd, TCIFLUSH); */ @@ -508,216 +667,239 @@ static void apc_flush(int flags) } } +/* apc specific wrappers around set/del info - to handle "packed" variables */ +static void apc_dstate_delinfo(apc_vartab_t *vt, int skip) +{ + char *name, *nidx; + int c; + + /* standard not packed var */ + if (!(vt->flags & APC_PACK)) { + dstate_delinfo(vt->name); + return; + } + + if ( !(name = xmalloc(sizeof(char) * vt->nlen0)) ) { + upslogx(LOG_ERR, "apc_dstate_delinfo() failed to allocate buffer"); + return; + } + + strcpy(name, vt->name); + nidx = strstr(name,".0.") + 1; + + for (c = skip; c < vt->cnt; c++) { + *nidx = (char)('1' + c); + dstate_delinfo(name); + } + + vt->cnt = 0; + free(name); +} + +static void apc_dstate_setinfo(apc_vartab_t *vt, const char *upsval) +{ + char *name, *nidx; + char *temp, *vidx[APC_PACK_MAX], *com, *curr; + int c; + + /* standard not packed var */ + if (!(vt->flags & APC_PACK)) { + dstate_setinfo(vt->name, "%s", convert_data(vt, upsval)); + return; + } + + if ( !(name = xmalloc(sizeof(char) * vt->nlen0)) ) { + upslogx(LOG_ERR, "apc_dstate_setinfo() failed to allocate buffer"); + return; + } + + if ( !(temp = xmalloc(sizeof(char) * (strlen(upsval) + 1))) ) { + upslogx(LOG_ERR, "apc_dstate_setinfo() failed to allocate buffer"); + free(name); + return; + } + + /* we have to set proper name for dstate_setinfo() calls */ + strcpy(name, vt->name); + nidx = strstr(name,".0.") + 1; + + /* split the value string */ + strcpy(temp, upsval); + curr = temp; + c = 0; + do { + vidx[c] = curr; + com = strchr(curr, ','); + if (com) { + curr = com + 1; + *com = '\0'; + } + } while(++c < APC_PACK_MAX && com); + + /* + * unlikely, but keep things tidy - remove leftover values, if + * subsequent read returns less + */ + if (vt->cnt > c) + apc_dstate_delinfo(vt, c); + + /* unlikely - warn user if we have more than APC_PACK_MAX fields */ + if (c == APC_PACK_MAX && com) + upslogx(LOG_WARNING, + "packed variable %s [%s] longer than %d fields,\n" + "ignoring remaining fields", + vt->name, prtchr(vt->cmd), c); + + vt->cnt = c; + + while (c-- > 0) { + *nidx = (char)('1' + c); + if (*vidx[c]) + dstate_setinfo(name, "%s", convert_data(vt, vidx[c])); + else + dstate_setinfo(name, "N/A"); + } + + free(name); + free(temp); +} + static const char *preread_data(apc_vartab_t *vt) { - int ret; - char temp[APC_LBUF]; + ssize_t ret; + static char temp[APC_LBUF]; - upsdebugx(4, "preread_data: %s", vt->name); + upsdebugx(1, "%s: %s [%s]", __func__, vt->name, prtchr(vt->cmd)); apc_flush(0); - ret = apc_write(vt->cmd); + ret = apc_write((const unsigned char)vt->cmd); - if (ret != 1) { - upslogx(LOG_ERR, "preread_data: apc_write failed"); + if (ret != 1) return 0; - } ret = apc_read(temp, sizeof(temp), SER_TO); - if (ret < 0) { - upslogx(LOG_ERR, "preread_data: apc_read failed"); + if (ret < 1 || !strcmp(temp, "NA")) { + if (ret >= 0) + upslogx(LOG_ERR, "%s: %s [%s] timed out or not supported", __func__, vt->name, prtchr(vt->cmd)); return 0; } - if (!ret || !strcmp(temp, "NA")) { - upslogx(LOG_ERR, "preread_data: %s timed out or not supported", vt->name); - return 0; - } - - return convert_data(vt, temp); -} - -static void remove_var(const char *cal, apc_vartab_t *vt) -{ - const char *fmt; - char info[256]; - - if (isprint(vt->cmd)) - fmt = "[%c]"; - else - fmt = "[0x%02x]"; - - snprintf(info, sizeof(info), "%s%s%s", - "%s: verified variable [%s] (APC: ", - fmt, - ") returned NA" - ); - upsdebugx(1, info, cal, vt->name, vt->cmd); - - snprintf(info, sizeof(info), "%s%s%s", - "%s: removing [%s] (APC: ", - fmt, - ")" - ); - upsdebugx(1, info, cal, vt->name, vt->cmd); - - vt->flags &= ~APC_PRESENT; - dstate_delinfo(vt->name); + return temp; } static int poll_data(apc_vartab_t *vt) { - int ret; - char temp[APC_LBUF]; + char temp[APC_LBUF]; if (!(vt->flags & APC_PRESENT)) return 1; - upsdebugx(4, "poll_data: %s", vt->name); + upsdebugx(1, "%s: %s [%s]", __func__, vt->name, prtchr(vt->cmd)); apc_flush(SER_AA); - ret = apc_write(vt->cmd); - - if (ret != 1) { - upslogx(LOG_ERR, "poll_data: apc_write failed"); - dstate_datastale(); + if (apc_write((const unsigned char)vt->cmd) != 1) return 0; - } - - if (apc_read(temp, sizeof(temp), SER_AA) < 1) { - dstate_datastale(); + if (apc_read(temp, sizeof(temp), SER_AA) < 1) return 0; - } /* automagically no longer supported by the hardware somehow */ - if (!strcmp(temp, "NA")) - remove_var("poll_data", vt); - - dstate_setinfo(vt->name, "%s", convert_data(vt, temp)); - dstate_dataok(); + if (!strcmp(temp, "NA")) { + upslogx(LOG_WARNING, "%s: verified variable %s [%s] returned NA, removing", __func__, vt->name, prtchr(vt->cmd)); + vt->flags &= ~(unsigned int)APC_PRESENT; + apc_dstate_delinfo(vt, 0); + } else + apc_dstate_setinfo(vt, temp); return 1; } -static int dfa_fwnew(const char *val) +static int update_status(void) { - int ret; - regex_t mbuf; - /* must be xx.yy.zz */ - const char rex[] = "^[[:alnum:]]+\\.[[:alnum:]]+\\.[[:alnum:]]+$"; + ssize_t ret; + char buf[APC_LBUF]; - regcomp(&mbuf, rex, REG_EXTENDED|REG_NOSUB); - ret = regexec(&mbuf, val, 0,0,0); - regfree(&mbuf); - return ret; -} + upsdebugx(1, "%s: [%s]", __func__, prtchr(APC_STATUS)); -static int dfa_cmdset(const char *val) -{ - int ret; - regex_t mbuf; - /* - * must be #.alerts.commands ; we'll be a bit lax here - */ - const char rex[] = "^[0-9]\\.[^.]*\\.[^.]+$"; + apc_flush(SER_AA); + if (apc_write(APC_STATUS) != 1) + return 0; + ret = apc_read(buf, sizeof(buf), SER_AA); - regcomp(&mbuf, rex, REG_EXTENDED|REG_NOSUB); - ret = regexec(&mbuf, val, 0,0,0); - regfree(&mbuf); - return ret; -} - -static int valid_cmd(char cmd, const char *val) -{ - char info[256], *fmt; - int ret; - - switch (cmd) { - case APC_FW_NEW: - ret = dfa_fwnew(val); - break; - case APC_CMDSET: - ret = dfa_cmdset(val); - break; - default: - return 1; + if ((ret < 1) || (!strcmp(buf, "NA"))) { + if (ret >= 0) + upslogx(LOG_WARNING, "%s: %s", __func__, "failed"); + return 0; } - if (ret) { - if (isprint(cmd)) - fmt = "[%c]"; - else - fmt = "[0x%02x]"; + ups_status = strtol(buf, 0, 16) & 0xff; + ups_status_set(); - snprintf(info, sizeof(info), "%s%s%s", - "valid_cmd: cmd ", - fmt, - " failed regex match" - ); - upslogx(LOG_WARNING, info, cmd); - } - - return !ret; + return 1; } /* - * query_ups() is called before any APC_PRESENT flags are determined; - * only for the variable provided + * two informative functions, to not redo the same thing in few places */ -static int query_ups(const char *var) + +static inline void confirm_cv(unsigned char cmd, const char *tag, const char *name) { - int i, j; - const char *temp; - apc_vartab_t *vt, *vtn; + upsdebugx(1, "%s [%s] - %s supported", name, prtchr((char)cmd), tag); +} +static inline void warn_cv(unsigned char cmd, const char *tag, const char *name) +{ + if (tag && name) + upslogx(LOG_WARNING, "%s [%s] - %s invalid", name, prtchr((char)cmd), tag); + else + upslogx(LOG_WARNING, "[%s] unrecognized", prtchr((char)cmd)); +} + +static void var_string_setup(apc_vartab_t *vt) +{ /* - * at first run we know nothing about variable; we have to handle - * APC_MULTI gracefully as well + * handle special data for our two strings; note - STRING variables + * cannot be PACK at the same time */ - for (i = 0; apc_vartab[i].name != NULL; i++) { - vt = &apc_vartab[i]; - if (strcmp(vt->name, var) || vt->flags & APC_DEPR) - continue; + if (vt->flags & APC_STRING) { + dstate_setflags(vt->name, ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux(vt->name, APC_STRLEN); + vt->flags |= APC_RW; + } +} - /* found, [try to] get it */ - - temp = preread_data(vt); - if (!temp || !valid_cmd(vt->cmd, temp)) { - if (vt->flags & APC_MULTI) { - vt->flags |= APC_DEPR; - continue; - } - upsdebugx(1, "query_ups: unknown variable %s", var); - break; - } +static int var_verify(apc_vartab_t *vt) +{ + const char *temp; + if (vt->flags & APC_MULTI) { + /* APC_MULTI are handled by deprecate_vars() */ vt->flags |= APC_PRESENT; - dstate_setinfo(vt->name, "%s", temp); - dstate_dataok(); - - /* supported, deprecate all the remaining ones */ - if (vt->flags & APC_MULTI) - for (j = i + 1; apc_vartab[j].name != NULL; j++) { - vtn = &apc_vartab[j]; - if (strcmp(vtn->name, vt->name)) - continue; - vtn->flags |= APC_DEPR; - vtn->flags &= ~APC_PRESENT; - } - - return 1; /* success */ + return -1; } - return 0; + temp = preread_data(vt); + /* no conversion here, validator should operate on raw values */ + if (!temp || !rexhlp(vt->regex, temp)) { + warn_cv((const unsigned char)vt->cmd, "variable", vt->name); + return 0; + } + + vt->flags |= APC_PRESENT; + apc_dstate_setinfo(vt, temp); + var_string_setup(vt); + + confirm_cv((const unsigned char)vt->cmd, "variable", vt->name); + + return 1; } /* - * This function iterates over vartab, deprecating nut:apc 1:n variables. We - * prefer earliest present variable. All the other ones must be marked as - * deprecated and as not present. - * This is intended to call after verifying the presence of variables. - * Otherwise it would take a while to execute due to preread_data() + * This function iterates over vartab, deprecating nut<->apc 1:n and n:1 + * variables. We prefer earliest present variable. All the other ones must be + * marked as not present (which implies deprecation). + * This pass is requried after completion of all protocol_verify() and/or + * legacy_verify() calls. */ static void deprecate_vars(void) { @@ -727,50 +909,52 @@ static void deprecate_vars(void) for (i = 0; apc_vartab[i].name != NULL; i++) { vt = &apc_vartab[i]; - if (vt->flags & APC_DEPR) - /* already handled */ - continue; - - if (!(vt->flags & APC_MULTI)) - continue; - if (!(vt->flags & APC_PRESENT)) { - vt->flags |= APC_DEPR; + if (!(vt->flags & APC_MULTI) || !(vt->flags & APC_PRESENT)) { + /* + * a) not interesting, or + * b) not marked as present earlier, or already handled + */ continue; } /* pre-read data, we have to verify it */ temp = preread_data(vt); - if (!temp || !valid_cmd(vt->cmd, temp)) { - upslogx(LOG_ERR, "deprecate_vars: [%s] is unreadable or invalid, deprecating", vt->name); - vt->flags |= APC_DEPR; - vt->flags &= ~APC_PRESENT; + /* no conversion here, validator should operate on raw values */ + if (!temp || !rexhlp(vt->regex, temp)) { + vt->flags &= ~(unsigned int)APC_PRESENT; + + warn_cv((const unsigned char)vt->cmd, "variable combination", vt->name); continue; } /* multi & present, deprecate all the remaining ones */ for (j = i + 1; apc_vartab[j].name != NULL; j++) { vtn = &apc_vartab[j]; - if (strcmp(vtn->name, vt->name)) + if (strcmp(vtn->name, vt->name) && vtn->cmd != vt->cmd) continue; - vtn->flags |= APC_DEPR; - vtn->flags &= ~APC_PRESENT; + vtn->flags &= ~(unsigned int)APC_PRESENT; } - dstate_setinfo(vt->name, "%s", temp); - dstate_dataok(); + apc_dstate_setinfo(vt, temp); + var_string_setup(vt); + + confirm_cv((const unsigned char)vt->cmd, "variable combination", vt->name); } } -static void do_capabilities(int qco) +static void apc_getcaps(int qco) { const char *ptr, *entptr; char upsloc, temp[APC_LBUF], cmd, loc, etmp[APC_SBUF], *endtemp; - int nument, entlen, i, matrix, ret, valid; + int matrix, valid; + size_t nument, entlen, i; + ssize_t ret; apc_vartab_t *vt; - upsdebugx(1, "APC - About to get capabilities string"); - /* If we can do caps, then we need the Firmware revision which has - the locale descriptor as the last character (ugh) - */ + /* + * If we can do caps, then we need the Firmware revision which has the + * locale descriptor as the last character (ugh); this is valid for + * both 'V' and 'b' commands. + */ ptr = dstate_getinfo("ups.firmware"); if (ptr) upsloc = ptr[strlen(ptr) - 1]; @@ -779,25 +963,25 @@ static void do_capabilities(int qco) /* get capability string */ apc_flush(0); - ret = apc_write(APC_CAPS); - - if (ret != 1) { - upslog_with_errno(LOG_ERR, "do_capabilities: apc_write failed"); + if (apc_write(APC_CAPS) != 1) return; - } + /* - * note - apc_read() needs larger timeout grace and different + * note - apc_read() needs larger timeout grace (not a problem w.r.t. + * to nut's timing, as it's done only during setup) and different * ignore set due to certain characters like '#' being received */ ret = apc_read(temp, sizeof(temp), SER_CC|SER_TO); if ((ret < 1) || (!strcmp(temp, "NA"))) { - /* Early Smart-UPS, not as smart as later ones */ - /* This should never happen since we only call - this if the REQ_CAPABILITIES command is supported - */ - upslogx(LOG_ERR, "ERROR: APC cannot do capabilities but said it could !"); + /* + * Early Smart-UPS not as smart as the later ones ... + * this should never happen on properly functioning hardware - + * as capability support was reported earlier + */ + if (ret >= 0) + upslogx(LOG_WARNING, "%s", "APC cannot do capabilities but said it could !"); return; } @@ -806,19 +990,15 @@ static void do_capabilities(int qco) endtemp = &temp[0] + strlen(temp); if (temp[0] != '#') { - upsdebugx(1, "unrecognized capability start char %c", temp[0]); - upsdebugx(1, "please report this error [%s]", temp); - upslogx(LOG_ERR, "ERROR: unknown capability start char %c!", - temp[0]); - + upslogx(LOG_WARNING, "unknown capability start char [%c] !", temp[0]); + upsdebugx(1, "please report this caps string: %s", temp); return; } if (temp[1] == '#') { /* Matrix-UPS */ - matrix = 1; ptr = &temp[0]; - } - else { + matrix = 1; + } else { ptr = &temp[1]; matrix = 0; } @@ -831,37 +1011,52 @@ static void do_capabilities(int qco) /* check for idiocy */ if (ptr >= endtemp) { - /* if we expected this, just ignore it */ if (qco) return; - - fatalx(EXIT_FAILURE, - "capability string has overflowed\n" - "please report this error\n" - "ERROR: capability overflow!" - ); + fatalx(EXIT_FAILURE, "capability string has overflowed, please report this error !"); } cmd = ptr[0]; loc = ptr[1]; - nument = ptr[2] - 48; - entlen = ptr[3] - 48; + + if (ptr[2] < 48 || ptr[3] < 48) { + upsdebugx(0, + "%s: nument (%d) or entlen (%d) out of range", + __func__, (ptr[2] - 48), (ptr[3] - 48)); + fatalx(EXIT_FAILURE, + "nument or entlen out of range\n" + "Please report this error\n" + "ERROR: capability overflow!"); + } + + nument = (size_t)ptr[2] - 48; + entlen = (size_t)ptr[3] - 48; entptr = &ptr[4]; - vt = vartab_lookup_char(cmd); - valid = vt && ((loc == upsloc) || (loc == '4')); + vt = vt_lookup_char(cmd); + valid = vt && ((loc == upsloc) || (loc == '4')) && !(vt->flags & APC_PACK); /* mark this as writable */ if (valid) { - upsdebugx(1, "supported capability: %02x (%c) - %s", - cmd, loc, vt->name); + upsdebugx(1, "%s [%s(%c)] - capability supported", vt->name, prtchr(cmd), loc); dstate_setflags(vt->name, ST_FLAG_RW); /* make sure setvar knows what this is */ vt->flags |= APC_RW | APC_ENUM; - } + } else if (vt && (vt->flags & APC_PACK)) + /* + * Currently we assume - basing on the following + * feedback: + * http://www.mail-archive.com/nut-upsdev@lists.alioth.debian.org/msg03398.html + * - that "packed" variables are not enumerable; if at + * some point in the future it turns out to be false, + * the handling will have to be a bit more complex + */ + upslogx(LOG_WARNING, + "WARN: packed APC variable %s [%s] reported as enumerable,\n" + "please report it on the mailing list", vt->name, prtchr(cmd)); for (i = 0; i < nument; i++) { if (valid) { @@ -876,60 +1071,102 @@ static void do_capabilities(int qco) } } -static int update_status(void) +static void legacy_verify(const char *var) { - int ret; - char buf[APC_LBUF]; + int i; + /* + * note: some NUT variables map onto multiple APC ones, e.g. firmware: + * V,b -> ups.firmware; that's why we keep the loop, as it's over NUT + * names + */ + for (i = 0; apc_vartab[i].name != NULL; i++) { + if (strcmp(apc_vartab[i].name, var)) + continue; + var_verify(&apc_vartab[i]); + } +} - upsdebugx(4, "update_status"); +static void protocol_verify(unsigned char cmd) +{ + int i, found; + apc_vartab_t *vt; + apc_cmdtab_t *ct; - apc_flush(SER_AA); - ret = apc_write(APC_STATUS); + /* don't bother with cmd/var we don't care about */ + if (strchr(APC_UNR_CMDS, cmd)) + return; - if (ret != 1) { - upslog_with_errno(LOG_ERR, "update_status: apc_write failed"); - dstate_datastale(); - return 0; + /* + * loop necessary for apc:nut 1:n cases (e.g. T -> device.uptime, + * ambient.0.temperature) + */ + found = 0; + for (i = 0; apc_vartab[i].name != NULL; i++) { + vt = &apc_vartab[i]; + if (vt->cmd != cmd) + continue; + var_verify(vt); + found = 1; + } + if (found) + return; + + /* + * see if it's a command + * loop necessary for apc:nut 1:n cases (e.g. D -> calibrate.start, + * calibrate.stop) + */ + found = 0; + for (i = 0; apc_cmdtab[i].name != NULL; i++) { + ct = &apc_cmdtab[i]; + if (ct->cmd != cmd) + continue; + ct->flags |= APC_PRESENT; + dstate_addcmd(ct->name); + confirm_cv(cmd, "command", ct->name); + found = 1; } - ret = apc_read(buf, sizeof(buf), SER_AA); + if (found) + return; - if ((ret < 1) || (!strcmp(buf, "NA"))) { - dstate_datastale(); - return 0; - } - - ups_status = strtol(buf, 0, 16) & 0xff; - ups_status_set(); - - dstate_dataok(); - - return 1; + /* + * epilogue - unrecognized command / variable not included + * in APC_UNR_CMDS + */ + warn_cv(cmd, NULL, NULL); } static void oldapcsetup(void) { - /* really old models ignore REQ_MODEL, so find them first */ - if (!query_ups("ups.model")) { - /* force the model name */ - dstate_setinfo("ups.model", "Smart-UPS"); - } + /* + * note: battery.date and ups.id make little sense here, as + * that would imply writability and this is an *old* apc psu + */ + legacy_verify("ups.temperature"); + legacy_verify("ups.load"); + legacy_verify("input.voltage"); + legacy_verify("output.voltage"); + legacy_verify("battery.charge"); + legacy_verify("battery.voltage"); + + /* these will usually timeout */ + legacy_verify("ups.model"); + legacy_verify("ups.serial"); + legacy_verify("ups.firmware"); + legacy_verify("output.current"); + + deprecate_vars(); /* see if this might be an old Matrix-UPS instead */ - if (query_ups("output.current")) + if (vt_lookup_name("output.current")) dstate_setinfo("ups.model", "Matrix-UPS"); - - query_ups("ups.firmware"); - query_ups("ups.serial"); - query_ups("input.voltage"); - query_ups("battery.charge"); - query_ups("battery.voltage"); - query_ups("input.voltage"); - query_ups("output.voltage"); - query_ups("ups.temperature"); - query_ups("ups.load"); - - update_status(); + else { + /* really old models don't support ups.model (apc: 0x01) */ + if (!vt_lookup_name("ups.model")) + /* force the model name */ + dstate_setinfo("ups.model", "Smart-UPS"); + } /* * If we have come down this path then we dont do capabilities and @@ -937,152 +1174,58 @@ static void oldapcsetup(void) */ } -static void protocol_verify(unsigned char cmd) -{ - int i, found; - const char *fmt, *temp; - char info[256]; - - /* don't bother with cmd/var we don't care about */ - if (strchr(APC_UNR_CMDS, cmd)) - return; - - if (isprint(cmd)) - fmt = "[%c]"; - else - fmt = "[0x%02x]"; - - /* - * see if it's a variable - * note: some nut variables map onto multiple APC ones (firmware) - */ - for (i = 0; apc_vartab[i].name != NULL; i++) { - if (apc_vartab[i].cmd == cmd) { - if (apc_vartab[i].flags & APC_MULTI) { - /* APC_MULTI are handled by deprecate_vars() */ - apc_vartab[i].flags |= APC_PRESENT; - return; - } - - temp = preread_data(&apc_vartab[i]); - if (!temp || !valid_cmd(cmd, temp)) { - snprintf(info, sizeof(info), "%s%s%s", - "UPS variable [%s] - APC: ", - fmt, - " invalid or unreadable" - ); - upsdebugx(3, info, apc_vartab[i].name, cmd); - return; - } - - apc_vartab[i].flags |= APC_PRESENT; - - snprintf(info, sizeof(info), "%s%s", - "UPS supports variable [%s] - APC: ", - fmt - ); - upsdebugx(3, info, apc_vartab[i].name, cmd); - - dstate_setinfo(apc_vartab[i].name, "%s", temp); - dstate_dataok(); - - /* handle special data for our two strings */ - if (apc_vartab[i].flags & APC_STRING) { - dstate_setflags(apc_vartab[i].name, - ST_FLAG_RW | ST_FLAG_STRING); - dstate_setaux(apc_vartab[i].name, APC_STRLEN); - - apc_vartab[i].flags |= APC_RW; - } - return; - } - } - - /* - * check the command list - * some APC commands map onto multiple nut ones (start and stop) - */ - found = 0; - for (i = 0; apc_cmdtab[i].name != NULL; i++) { - if (apc_cmdtab[i].cmd == cmd) { - - snprintf(info, sizeof(info), "%s%s", - "UPS supports command [%s] - APC: ", - fmt - ); - upsdebugx(3, info, apc_cmdtab[i].name, cmd); - - dstate_addcmd(apc_cmdtab[i].name); - - apc_cmdtab[i].flags |= APC_PRESENT; - found = 1; - } - } - - if (found) - return; - - snprintf(info, sizeof(info), "%s%s%s", - "protocol_verify - APC: ", - fmt, - " unrecognized" - ); - upsdebugx(1, info, cmd); -} - /* some hardware is a special case - hotwire the list of cmdchars */ static int firmware_table_lookup(void) { - int ret; - unsigned int i, j; - char buf[APC_LBUF]; + ssize_t ret; + unsigned int i, j; + char buf[APC_LBUF]; - upsdebugx(1, "attempting firmware lookup using command 'V'"); + upsdebugx(1, "attempting firmware lookup using [%s]", prtchr(APC_FW_OLD)); apc_flush(0); - ret = apc_write(APC_FW_OLD); - - if (ret != 1) { - upslog_with_errno(LOG_ERR, "firmware_table_lookup: apc_write failed"); + if (apc_write(APC_FW_OLD) != 1) + return 0; + if ((ret = apc_read(buf, sizeof(buf), SER_TO)) < 0) return 0; - } - ret = apc_read(buf, sizeof(buf), SER_TO); - - /* + /* * Some UPSes support both 'V' and 'b'. As 'b' doesn't always return * firmware version, we attempt that only if 'V' doesn't work. */ - if ((ret < 1) || (!strcmp(buf, "NA"))) { - upsdebugx(1, "attempting firmware lookup using command 'b'"); - ret = apc_write(APC_FW_NEW); + if (!ret || !strcmp(buf, "NA")) { + upsdebugx(1, "attempting firmware lookup using [%s]", prtchr(APC_FW_NEW)); - if (ret != 1) { - upslog_with_errno(LOG_ERR, "firmware_table_lookup: apc_write failed"); + if (apc_write(APC_FW_NEW) != 1) return 0; - } - - ret = apc_read(buf, sizeof(buf), SER_TO); - - if (ret < 1) { - upslog_with_errno(LOG_ERR, "firmware_table_lookup: apc_read failed"); + if (apc_read(buf, sizeof(buf), SER_TO) < 1) return 0; - } } - upsdebugx(2, "firmware: [%s]", buf); + upsdebugx(1, "detected firmware version: %s", buf); /* this will be reworked if we get a lot of these things */ - if (!strcmp(buf, "451.2.I")) + if (!strcmp(buf, "451.2.I")) { /* quirk_capability_overflow */ + upsdebugx(1, "WARN: quirky firmware !"); return 2; + } + + if (rexhlp("^[a-fA-F0-9]{2}$", buf)) { + /* + * certain old set of UPSes that return voltage above 255V + * through 'b'; see: + * http://article.gmane.org/gmane.comp.monitoring.nut.user/7762 + */ + strcpy(buf, "set\1"); + } for (i = 0; apc_compattab[i].firmware != NULL; i++) { if (!strcmp(apc_compattab[i].firmware, buf)) { - upsdebugx(2, "matched - cmdchars: %s", - apc_compattab[i].cmdchars); + upsdebugx(1, "matched firmware: %s", apc_compattab[i].firmware); + /* magic ? */ if (strspn(apc_compattab[i].firmware, "05")) { dstate_setinfo("ups.model", "Matrix-UPS"); } else { @@ -1090,8 +1233,9 @@ static int firmware_table_lookup(void) } /* matched - run the cmdchars from the table */ + upsdebugx(1, "parsing out supported cmds and vars"); for (j = 0; j < strlen(apc_compattab[i].cmdchars); j++) - protocol_verify(apc_compattab[i].cmdchars[j]); + protocol_verify((const unsigned char)(apc_compattab[i].cmdchars[j])); deprecate_vars(); return 1; /* matched */ @@ -1101,11 +1245,12 @@ static int firmware_table_lookup(void) return 0; } -static void getbaseinfo(void) +static int getbaseinfo(void) { unsigned int i; - int ret, qco; - char *cmds, temp[APC_LBUF]; + ssize_t ret; + int qco; + char *cmds, *tail, temp[APC_LBUF]; /* * try firmware lookup first; we could start with 'a', but older models @@ -1114,95 +1259,98 @@ static void getbaseinfo(void) qco = firmware_table_lookup(); if (qco == 1) /* found compat */ - return; + return 1; - upsdebugx(2, "firmware not found in compatibility table - trying normal method"); - upsdebugx(1, "APC - attempting to find command set"); + upsdebugx(1, "attempting var/cmdset lookup using [%s]", prtchr(APC_CMDSET)); /* - * Initially we ask the UPS what commands it takes If this fails we are + * Initially we ask the UPS what commands it takes. If this fails we are * going to need an alternate strategy - we can deal with that if it * happens */ apc_flush(0); - ret = apc_write(APC_CMDSET); + if (apc_write(APC_CMDSET) != 1) + return 0; + if ((ret = apc_read(temp, sizeof(temp), SER_CS|SER_TO)) < 0) + return 0; - if (ret != 1) { - upslog_with_errno(LOG_ERR, "getbaseinfo: apc_write failed"); - return; - } - - ret = apc_read(temp, sizeof(temp), SER_CS|SER_TO); - - if ((ret < 1) || (!strcmp(temp, "NA")) || !valid_cmd(APC_CMDSET, temp)) { + if (!ret || !strcmp(temp, "NA") || !rexhlp(APC_CMDSET_FMT, temp)) { /* We have an old dumb UPS - go to specific code for old stuff */ - upsdebugx(1, "APC - trying to handle unknown model"); + upslogx(LOG_NOTICE, "very old or unknown APC model, support will be limited"); oldapcsetup(); - return; + return 1; } - upsdebugx(1, "APC - Parsing out supported cmds and vars"); + upsdebugx(1, "parsing out supported cmds/vars"); /* * returned set is verified for validity above, so just extract * what's interesting for us + * + * the known format is: + * ver.alerts.commands[.stuff] */ - cmds = strrchr(temp, '.'); - for (i = 1; i < strlen(cmds); i++) - protocol_verify(cmds[i]); + cmds = strchr(temp, '.'); + cmds = strchr(cmds + 1, '.'); + tail = strchr(++cmds, '.'); + if (tail) + *tail = 0; + for (i = 0; i < strlen(cmds); i++) + protocol_verify((const unsigned char)cmds[i]); deprecate_vars(); /* if capabilities are supported, add them here */ if (strchr(cmds, APC_CAPS)) { - do_capabilities(qco); - upsdebugx(1, "APC - UPS capabilities determined"); + upsdebugx(1, "parsing out caps"); + apc_getcaps(qco); } + return 1; } /* check for calibration status and either start or stop */ static int do_cal(int start) { char temp[APC_LBUF]; - int tval, ret; + long tval; + ssize_t ret; apc_flush(SER_AA); ret = apc_write(APC_STATUS); if (ret != 1) { - upslog_with_errno(LOG_ERR, "do_cal: apc_write failed"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } ret = apc_read(temp, sizeof(temp), SER_AA); /* if we can't check the current calibration status, bail out */ - if ((ret < 1) || (!strcmp(temp, "NA"))) + if ((ret < 1) || (!strcmp(temp, "NA"))) { + upslogx(LOG_WARNING, "%s", "runtime calibration state undeterminable"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ + } tval = strtol(temp, 0, 16); if (tval & APC_STAT_CAL) { /* calibration currently happening */ if (start == 1) { /* requested start while calibration still running */ - upslogx(LOG_INFO, "runtime calibration already in progress"); + upslogx(LOG_NOTICE, "%s", "runtime calibration already in progress"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } /* stop requested */ - upslogx(LOG_INFO, "stopping runtime calibration"); + upslogx(LOG_NOTICE, "%s", "stopping runtime calibration"); ret = apc_write(APC_CMD_CALTOGGLE); if (ret != 1) { - upslog_with_errno(LOG_ERR, "do_cal: apc_write failed"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } ret = apc_read(temp, sizeof(temp), SER_AA); if ((ret < 1) || (!strcmp(temp, "NA")) || (!strcmp(temp, "NO"))) { - upslogx(LOG_WARNING, "stop calibration failed: %s", - temp); + upslogx(LOG_WARNING, "stop calibration failed, cmd returned: %s", temp); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } @@ -1212,23 +1360,22 @@ static int do_cal(int start) /* calibration not happening */ if (start == 0) { /* stop requested */ - upslogx(LOG_INFO, "runtime calibration not occurring"); + upslogx(LOG_NOTICE, "%s", "runtime calibration not occurring"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } - upslogx(LOG_INFO, "starting runtime calibration"); + upslogx(LOG_NOTICE, "%s", "starting runtime calibration"); ret = apc_write(APC_CMD_CALTOGGLE); if (ret != 1) { - upslog_with_errno(LOG_ERR, "do_cal: apc_write failed"); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } ret = apc_read(temp, sizeof(temp), SER_AA); if ((ret < 1) || (!strcmp(temp, "NA")) || (!strcmp(temp, "NO"))) { - upslogx(LOG_WARNING, "start calibration failed: %s", temp); + upslogx(LOG_WARNING, "start calibration failed, cmd returned: %s", temp); return STAT_INSTCMD_HANDLED; /* FUTURE: failure */ } @@ -1245,13 +1392,12 @@ static int smartmode(void) apc_flush(0); ret = apc_write(APC_GOSMART); if (ret != 1) { - upslog_with_errno(LOG_ERR, "smartmode: apc_write failed"); return 0; } ret = apc_read(temp, sizeof(temp), 0); if ((ret < 1) || (!strcmp(temp, "NA")) || (!strcmp(temp, "NO"))) { - upslogx(LOG_CRIT, "enabling smartmode failed !"); + upslogx(LOG_CRIT, "%s", "enabling smartmode failed !"); return 0; } @@ -1267,33 +1413,26 @@ static int smartmode(void) */ static int smartmode(int cnt) { - int ret, tries; + ssize_t ret; + int tries; char temp[APC_LBUF]; for (tries = 0; tries < cnt; tries++) { apc_flush(0); - ret = apc_write(APC_GOSMART); - - if (ret != 1) { - upslog_with_errno(LOG_ERR, "smartmode: issuing 'Y' failed"); + if (apc_write(APC_GOSMART) != 1) return 0; - } - ret = apc_read(temp, sizeof(temp), SER_D1); + + /* timeout here is intented */ + ret = apc_read(temp, sizeof(temp), SER_TO|SER_D1); if (ret > 0 && !strcmp(temp, "SM")) return 1; /* success */ - if (ret < 0) { + if (ret < 0) /* error, so we didn't timeout - wait a bit before retry */ sleep(1); - } - apc_flush(0); - ret = apc_write(27); /* ESC */ - - if (ret != 1) { - upslog_with_errno(LOG_ERR, "smartmode: issuing ESC failed"); + if (apc_write(27) != 1) /* ESC */ return 0; - } /* eat the response (might be NA, might be something else) */ apc_read(temp, sizeof(temp), SER_TO|SER_D1); @@ -1309,7 +1448,7 @@ static int smartmode(int cnt) */ static int sdok(int ign) { - int ret; + ssize_t ret; char temp[APC_SBUF]; /* @@ -1318,61 +1457,64 @@ static int sdok(int ign) * furthermore, command 'Z' will not reply with anything */ ret = apc_read(temp, sizeof(temp), SER_HA|SER_D1|SER_TO); - if (ret < 0) { - upslog_with_errno(LOG_ERR, "sdok: apc_read failed"); + if (ret < 0) return STAT_INSTCMD_FAILED; - } - upsdebugx(4, "sdok: got \"%s\"", temp); + upsdebugx(1, "%s: got \"%s\"", __func__, temp); if ((!ret && ign) || !strcmp(temp, "OK")) { - upsdebugx(4, "sdok: last issued shutdown cmd succeeded"); + upsdebugx(1, "%s: %s", __func__, "last shutdown cmd succeeded"); return STAT_INSTCMD_HANDLED; } - upsdebugx(1, "sdok: last issued shutdown cmd failed"); + upsdebugx(1, "%s: %s", __func__, "last shutdown cmd failed"); return STAT_INSTCMD_FAILED; } /* soft hibernate: S - working only when OB, otherwise ignored */ static int sdcmd_S(const void *foo) { - int ret; - apc_flush(0); - upsdebugx(1, "issuing soft hibernate"); - ret = apc_write(APC_CMD_SOFTDOWN); - if (ret < 0) { - upslog_with_errno(LOG_ERR, "sdcmd_S: issuing 'S' failed"); + if (!foo) + upsdebugx(1, "%s: issuing [%s]", __func__, prtchr(APC_CMD_SOFTDOWN)); + if (apc_write(APC_CMD_SOFTDOWN) != 1) return STAT_INSTCMD_FAILED; - } - return sdok(0); } /* soft hibernate, hack version for CS 350 & co. */ static int sdcmd_CS(const void *foo) { - int ret; + ssize_t ret; + useconds_t cshd = 3500000; char temp[APC_SBUF]; + const char *val; + NUT_UNUSED_VARIABLE(foo); - upsdebugx(1, "using CS 350 'force OB' shutdown method"); + /* TODO: Catch overflows? + * Let compilers complain about (non-)casting on systems + * where useconds_t is not a good target for strtod() output + */ + if ((val = getval("cshdelay"))) + cshd = (strtod(val, NULL) * 1000000); + + upsdebugx(1, "%s: issuing CS 'hack' [%s+%s] with %2.1f sec delay", __func__, prtchr(APC_CMD_SIMPWF), prtchr(APC_CMD_SOFTDOWN), (double)cshd / 1000000); if (ups_status & APC_STAT_OL) { apc_flush(0); - upsdebugx(1, "status OL - forcing OB temporarily"); + upsdebugx(1, "%s: issuing [%s]", __func__, prtchr(APC_CMD_SIMPWF)); ret = apc_write(APC_CMD_SIMPWF); - if (ret < 0) { - upslog_with_errno(LOG_ERR, "sdcmd_CS: issuing 'U' failed"); + if (ret != 1) { return STAT_INSTCMD_FAILED; } - /* eat response */ - ret = apc_read(temp, sizeof(temp), SER_D1); + /* eat response, allow timeout */ + ret = apc_read(temp, sizeof(temp), SER_D1|SER_TO); if (ret < 0) { - upslog_with_errno(LOG_ERR, "sdcmd_CS: 'U' returned nothing ?"); return STAT_INSTCMD_FAILED; } + usleep(cshd); } - return sdcmd_S(0); + /* continue with regular soft hibernate */ + return sdcmd_S((void *)1); } /* @@ -1382,7 +1524,8 @@ static int sdcmd_CS(const void *foo) */ static int sdcmd_AT(const void *str) { - int ret, cnt, padto, i; + ssize_t ret; + size_t cnt, padto, i; const char *awd = str; char temp[APC_SBUF], *ptr; @@ -1399,27 +1542,33 @@ static int sdcmd_AT(const void *str) } strcpy(ptr, awd); - upsdebugx(1, "issuing '@' with %d minutes of additional wakeup delay", (int)strtol(awd, NULL, 10)*6); + upsdebugx(1, "%s: issuing [%s] with %ld minutes of additional wakeup delay", + __func__, prtchr(APC_CMD_GRACEDOWN), strtol(awd, NULL, 10)*6); apc_flush(0); ret = apc_write_long(temp); - if (ret < 0) { - upslog_with_errno(LOG_ERR, "sdcmd_AT: issuing '@' with %d digits failed", padto); + /* Range-check: padto is 2 or 3 per above */ + if (ret != (ssize_t)padto + 1) { + upslogx(LOG_ERR, + "issuing [%s] with %zu digits failed", + prtchr(APC_CMD_GRACEDOWN), padto); return STAT_INSTCMD_FAILED; } ret = sdok(0); if (ret == STAT_INSTCMD_HANDLED || padto == 3) - return ret; + return (int)ret; - upslog_with_errno(LOG_ERR, "sdcmd_AT: command '@' with 2 digits doesn't work - try 3 digits"); + upslogx(LOG_ERR, + "command [%s] with 2 digits doesn't work - try 3 digits", + prtchr(APC_CMD_GRACEDOWN)); /* * "tricky" part - we tried @nn variation and it (unsurprisingly) * failed; we have to abort the sequence with something bogus to have * the clean state; newer upses will respond with 'NO', older will be * silent (YMMV); */ - apc_write(APC_CMD_GRACEDOWN); + apc_write(APC_GOSMART); /* eat response, allow it to timeout */ apc_read(temp, sizeof(temp), SER_D1|SER_TO); @@ -1429,16 +1578,15 @@ static int sdcmd_AT(const void *str) /* shutdown: K - delayed poweroff */ static int sdcmd_K(const void *foo) { - int ret; + ssize_t ret; + NUT_UNUSED_VARIABLE(foo); - upsdebugx(1, "issuing 'K'"); + upsdebugx(1, "%s: issuing [%s]", __func__, prtchr(APC_CMD_SHUTDOWN)); apc_flush(0); ret = apc_write_rep(APC_CMD_SHUTDOWN); - if (ret < 0) { - upslog_with_errno(LOG_ERR, "sdcmd_K: issuing 'K' failed"); + if (ret != 2) return STAT_INSTCMD_FAILED; - } return sdok(0); } @@ -1446,14 +1594,14 @@ static int sdcmd_K(const void *foo) /* shutdown: Z - immediate poweroff */ static int sdcmd_Z(const void *foo) { - int ret; + ssize_t ret; + NUT_UNUSED_VARIABLE(foo); - upsdebugx(1, "issuing 'Z'"); + upsdebugx(1, "%s: issuing [%s]", __func__, prtchr(APC_CMD_OFF)); apc_flush(0); ret = apc_write_rep(APC_CMD_OFF); - if (ret < 0) { - upslog_with_errno(LOG_ERR, "sdcmd_Z: issuing 'Z' failed"); + if (ret != 2) { return STAT_INSTCMD_FAILED; } @@ -1463,12 +1611,15 @@ static int sdcmd_Z(const void *foo) static void upsdrv_shutdown_simple(void) { - unsigned int sdtype = 0; + long sdtype = 0; const char *val; if ((val = getval("sdtype"))) sdtype = strtol(val, NULL, 10); + upsdebugx(1, "%s: currently: %s, sdtype: %ld", __func__, + (ups_status & APC_STAT_OL) ? "on-line" : "on battery", sdtype); + switch (sdtype) { case 5: /* hard hibernate */ @@ -1492,13 +1643,10 @@ static void upsdrv_shutdown_simple(void) * Specifically it sends both the soft shutdown 'S' and the * hard hibernate '@nnn' commands */ - upsdebugx(1, "UPS - currently %s - sending soft/hard hibernate commands", - (ups_status & APC_STAT_OL) ? "on-line" : "on battery"); /* S works only when OB */ - if ((ups_status & APC_STAT_OB) && sdcmd_S(0) == STAT_INSTCMD_HANDLED) - break; - sdcmd_AT(getval("awd")); + if (!(ups_status & APC_STAT_OB) || sdcmd_S(0) != STAT_INSTCMD_HANDLED) + sdcmd_AT(getval("awd")); break; default: @@ -1521,6 +1669,9 @@ static void upsdrv_shutdown_advanced(void) val = getval("advorder"); len = strlen(val); + upsdebugx(1, "%s: currently: %s, advorder: %s", __func__, + (ups_status & APC_STAT_OL) ? "on-line" : "on battery", val); + /* * try each method in the list with a little bit of handling in certain * cases @@ -1542,87 +1693,61 @@ static void upsdrv_shutdown_advanced(void) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - char temp[APC_LBUF]; - int ret; + char temp[APC_LBUF]; if (!smartmode(1)) - upsdebugx(1, "SM detection failed. Trying a shutdown command anyway"); + upslogx(LOG_WARNING, "%s: %s", __func__, "setting SmartMode failed !"); /* check the line status */ - ret = apc_write(APC_STATUS); - - if (ret == 1) { - ret = apc_read(temp, sizeof(temp), SER_D1); - - if (ret < 1) { - upsdebugx(1, "status read failed ! assuming on battery state"); - ups_status = APC_STAT_LB | APC_STAT_OB; - } else { + if (apc_write(APC_STATUS) == 1) { + if (apc_read(temp, sizeof(temp), SER_D1) == 1) { ups_status = strtol(temp, 0, 16); + } else { + upslogx(LOG_WARNING, "%s: %s", __func__, "status read failed, assuming LB+OB"); + ups_status = APC_STAT_LB | APC_STAT_OB; } - } else { - upsdebugx(1, "status request failed; assuming on battery state"); + upslogx(LOG_WARNING, "%s: %s", __func__, "status write failed, assuming LB+OB"); ups_status = APC_STAT_LB | APC_STAT_OB; } - if (testvar("advorder") && strcasecmp(getval("advorder"), "no")) + if (testvar("advorder") && toupper((size_t)*getval("advorder")) != 'N') upsdrv_shutdown_advanced(); else upsdrv_shutdown_simple(); } -static void update_info_normal(void) +static int update_info(int all) { - int i; + int i; - upsdebugx(3, "update_info_normal: starting"); + upsdebugx(1, "%s: starting scan%s", __func__, all ? " (all vars)" : ""); for (i = 0; apc_vartab[i].name != NULL; i++) { - if ((apc_vartab[i].flags & APC_POLL) == 0) + if (!all && (apc_vartab[i].flags & APC_POLL) == 0) continue; if (!poll_data(&apc_vartab[i])) { - upsdebugx(3, "update_info_normal: poll_data (%s) failed - " - "aborting scan", apc_vartab[i].name); - return; + upsdebugx(1, "%s: %s", __func__, "aborting scan"); + return 0; } } - upsdebugx(3, "update_info_normal: done"); -} - -static void update_info_all(void) -{ - int i; - - upsdebugx(3, "update_info_all: starting"); - - for (i = 0; apc_vartab[i].name != NULL; i++) { - if (!poll_data(&apc_vartab[i])) { - upsdebugx(3, "update_info_all: poll_data (%s) failed - " - "aborting scan", apc_vartab[i].name); - return; - } - } - - upsdebugx(3, "update_info_all: done"); + upsdebugx(1, "%s: %s", __func__, "scan completed"); + return 1; } static int setvar_enum(apc_vartab_t *vt, const char *val) { - int i, ret; + int i; + ssize_t ret; char orig[APC_LBUF], temp[APC_LBUF]; const char *ptr; apc_flush(SER_AA); - ret = apc_write(vt->cmd); - - if (ret != 1) { - upslog_with_errno(LOG_ERR, "setvar_enum: apc_write failed"); + if (apc_write((const unsigned char)vt->cmd) != 1) return STAT_SET_FAILED; - } ret = apc_read(orig, sizeof(orig), SER_AA); @@ -1633,19 +1758,15 @@ static int setvar_enum(apc_vartab_t *vt, const char *val) /* suppress redundant changes - easier on the eeprom */ if (!strcmp(ptr, val)) { - upslogx(LOG_INFO, "ignoring enum SET %s='%s' (unchanged value)", - vt->name, val); + upslogx(LOG_INFO, "%s: ignoring SET %s='%s' (unchanged value)", + __func__, vt->name, val); return STAT_SET_HANDLED; /* FUTURE: no change */ } - for (i = 0; i < 6; i++) { - ret = apc_write(APC_NEXTVAL); - - if (ret != 1) { - upslog_with_errno(LOG_ERR, "setvar_enum: apc_write failed"); + for (i = 0; i < 32; i++) { + if (apc_write(APC_NEXTVAL) != 1) return STAT_SET_FAILED; - } /* this should return either OK (if rotated) or NO (if not) */ ret = apc_read(temp, sizeof(temp), SER_AA); @@ -1660,12 +1781,8 @@ static int setvar_enum(apc_vartab_t *vt, const char *val) return STAT_SET_FAILED; /* see what it rotated onto */ - ret = apc_write(vt->cmd); - - if (ret != 1) { - upslog_with_errno(LOG_ERR, "setvar_enum: apc_write failed"); + if (apc_write((const unsigned char)vt->cmd) != 1) return STAT_SET_FAILED; - } ret = apc_read(temp, sizeof(temp), SER_AA); @@ -1674,57 +1791,48 @@ static int setvar_enum(apc_vartab_t *vt, const char *val) ptr = convert_data(vt, temp); - upsdebugx(1, "rotate value: got [%s], want [%s]", - ptr, val); + upsdebugx(1, "%s: rotate - got [%s], want [%s]", __func__, ptr, val); if (!strcmp(ptr, val)) { /* got it */ - upslogx(LOG_INFO, "SET %s='%s'", vt->name, val); + upslogx(LOG_INFO, "%s: SET %s='%s'", __func__, vt->name, val); /* refresh data from the hardware */ poll_data(vt); - /* query_ups(vt->name, 0); */ return STAT_SET_HANDLED; /* FUTURE: success */ } /* check for wraparound */ if (!strcmp(ptr, orig)) { - upslogx(LOG_ERR, "setvar: variable %s wrapped", - vt->name); + upslogx(LOG_ERR, "%s: variable %s wrapped", __func__, vt->name); return STAT_SET_FAILED; } } - upslogx(LOG_ERR, "setvar: gave up after 6 tries for %s", - vt->name); + upslogx(LOG_ERR, "%s: gave up after 6 tries for %s", __func__, vt->name); /* refresh data from the hardware */ poll_data(vt); - /* query_ups(vt->name, 0); */ - return STAT_SET_HANDLED; + return STAT_SET_FAILED; } static int setvar_string(apc_vartab_t *vt, const char *val) { - unsigned int i; - int ret; + size_t i; + ssize_t ret; char temp[APC_LBUF], *ptr; /* sanitize length */ if (strlen(val) > APC_STRLEN) { - upslogx(LOG_ERR, "setvar_string: value (%s) too long", val); + upslogx(LOG_ERR, "%s: value (%s) too long", __func__, val); return STAT_SET_FAILED; } apc_flush(SER_AA); - ret = apc_write(vt->cmd); - - if (ret != 1) { - upslog_with_errno(LOG_ERR, "setvar_string: apc_write failed"); + if (apc_write((const unsigned char)vt->cmd) != 1) return STAT_SET_FAILED; - } ret = apc_read(temp, sizeof(temp), SER_AA); @@ -1733,8 +1841,8 @@ static int setvar_string(apc_vartab_t *vt, const char *val) /* suppress redundant changes - easier on the eeprom */ if (!strcmp(temp, val)) { - upslogx(LOG_INFO, "ignoring string SET %s='%s' (unchanged value)", - vt->name, val); + upslogx(LOG_INFO, "%s: ignoring SET %s='%s' (unchanged value)", + __func__, vt->name, val); return STAT_SET_HANDLED; /* FUTURE: no change */ } @@ -1747,30 +1855,25 @@ static int setvar_string(apc_vartab_t *vt, const char *val) *ptr++ = '\015'; /* pad with CRs */ *ptr = 0; - ret = apc_write_long(ptr); - - if ((size_t)ret != strlen(ptr)) { - upslog_with_errno(LOG_ERR, "setvar_string: apc_write_long failed"); + if (apc_write_long(temp) != APC_STRLEN + 1) return STAT_SET_FAILED; - } ret = apc_read(temp, sizeof(temp), SER_AA); if (ret < 1) { - upslogx(LOG_ERR, "setvar_string: short final read"); + upslogx(LOG_ERR, "%s: %s", __func__, "short final read"); return STAT_SET_FAILED; } if (!strcmp(temp, "NO")) { - upslogx(LOG_ERR, "setvar_string: got NO at final read"); + upslogx(LOG_ERR, "%s: %s", __func__, "got NO at final read"); return STAT_SET_FAILED; } /* refresh data from the hardware */ poll_data(vt); - /* query_ups(vt->name, 0); */ - upslogx(LOG_INFO, "SET %s='%s'", vt->name, val); + upslogx(LOG_INFO, "%s: SET %s='%s'", __func__, vt->name, val); return STAT_SET_HANDLED; /* FUTURE: success */ } @@ -1779,13 +1882,13 @@ static int setvar(const char *varname, const char *val) { apc_vartab_t *vt; - vt = vartab_lookup_name(varname); + vt = vt_lookup_name(varname); if (!vt) return STAT_SET_UNKNOWN; if ((vt->flags & APC_RW) == 0) { - upslogx(LOG_WARNING, "setvar: [%s] is not writable", varname); + upslogx(LOG_WARNING, "%s: [%s] is not writable", __func__, varname); return STAT_SET_UNKNOWN; } @@ -1795,22 +1898,18 @@ static int setvar(const char *varname, const char *val) if (vt->flags & APC_STRING) return setvar_string(vt, val); - upslogx(LOG_WARNING, "setvar: Unknown type for [%s]", varname); + upslogx(LOG_WARNING, "%s: unknown type for [%s]", __func__, varname); return STAT_SET_UNKNOWN; } /* load on */ static int do_loadon(void) { - int ret; apc_flush(0); - upsdebugx(1, "issuing load-on command"); + upsdebugx(1, "%s: issuing [%s]", __func__, prtchr(APC_CMD_ON)); - ret = apc_write_rep(APC_CMD_ON); - if (ret < 0) { - upslog_with_errno(LOG_ERR, "do_loadon: apc_write_rep failed"); + if (apc_write_rep(APC_CMD_ON) != 2) return STAT_INSTCMD_FAILED; - } /* * ups will not reply anything after this command, but might @@ -1818,29 +1917,28 @@ static int do_loadon(void) * the next status update) */ - upsdebugx(1, "load-on command (apc:^N) executed"); + upsdebugx(1, "%s: [%s] completed", __func__, prtchr(APC_CMD_ON)); return STAT_INSTCMD_HANDLED; } /* actually send the instcmd's char to the ups */ static int do_cmd(const apc_cmdtab_t *ct) { - int ret; + ssize_t ret; + int c; char temp[APC_LBUF]; - const char *strerr; apc_flush(SER_AA); if (ct->flags & APC_REPEAT) { - ret = apc_write_rep(ct->cmd); - strerr = "apc_write_rep"; + ret = apc_write_rep((const unsigned char)ct->cmd); + c = 2; } else { - ret = apc_write(ct->cmd); - strerr = "apc_write"; + ret = apc_write((const unsigned char)ct->cmd); + c = 1; } - if (ret < 1) { - upslog_with_errno(LOG_ERR, "do_cmd: %s failed", strerr); + if (ret != c) { return STAT_INSTCMD_FAILED; } @@ -1850,13 +1948,13 @@ static int do_cmd(const apc_cmdtab_t *ct) return STAT_INSTCMD_FAILED; if (strcmp(temp, "OK")) { - upslogx(LOG_WARNING, "got [%s] after command [%s]", - temp, ct->name); + upslogx(LOG_WARNING, "%s: got [%s] after command [%s]", + __func__, temp, ct->name); return STAT_INSTCMD_FAILED; } - upslogx(LOG_INFO, "command: %s", ct->name); + upslogx(LOG_INFO, "%s: %s completed", __func__, ct->name); return STAT_INSTCMD_HANDLED; } @@ -1874,8 +1972,8 @@ static int instcmd_chktime(apc_cmdtab_t *ct, const char *ext) /* you have to hit this in a small window or it fails */ if ((elapsed < MINCMDTIME) || (elapsed > MAXCMDTIME)) { - upsdebugx(1, "instcmd_chktime: outside window for [%s %s] (%2.0f)", - ct->name, ext ? ext : "\b", elapsed); + upsdebugx(1, "%s: outside window for [%s %s] (%2.0f)", + __func__, ct->name, ext ? ext : "\b", elapsed); return 0; } @@ -1888,35 +1986,31 @@ static int instcmd(const char *cmd, const char *ext) apc_cmdtab_t *ct = NULL; for (i = 0; apc_cmdtab[i].name != NULL; i++) { - /* main command must match */ + /* cmd must match */ if (strcasecmp(apc_cmdtab[i].name, cmd)) continue; - /* extra was provided - check it */ - if (ext && *ext) { - if (!apc_cmdtab[i].ext) + /* if cmd specifies regex, ext must match */ + if (apc_cmdtab[i].ext) { + if (!rexhlp(apc_cmdtab[i].ext, ext)) continue; - if (strlen(apc_cmdtab[i].ext) > 2) { - if (rexhlp(apc_cmdtab[i].ext, ext)) - continue; - } else { - if (strcasecmp(apc_cmdtab[i].ext, ext)) - continue; - } - } else if (apc_cmdtab[i].ext) - continue; + /* if cmd doesn't specify regex, ext must be NULL */ + } else { + if (ext) + continue; + } ct = &apc_cmdtab[i]; break; } if (!ct) { - upslogx(LOG_WARNING, "instcmd: unknown command [%s %s]", cmd, + upslogx(LOG_WARNING, "%s: unknown command [%s %s]", __func__, cmd, ext ? ext : "\b"); return STAT_INSTCMD_INVALID; } if (!(ct->flags & APC_PRESENT)) { - upslogx(LOG_WARNING, "instcmd: command [%s %s] recognized, but" - " not supported by your UPS model", cmd, + upslogx(LOG_WARNING, "%s: command [%s %s] recognized, but" + " not supported by your UPS model", __func__, cmd, ext ? ext : "\b"); return STAT_INSTCMD_INVALID; } @@ -1946,11 +2040,10 @@ static int instcmd(const char *cmd, const char *ext) if (!ext || !*ext) return sdcmd_S(0); - /* ext length is guaranteed by regex match above */ - if (!strncasecmp(ext, "at", 2)) + if (toupper((size_t)*ext) == 'A') return sdcmd_AT(ext + 3); - if (!strncasecmp(ext, "cs", 2)) + if (toupper((size_t)*ext) == 'C') return sdcmd_CS(0); } @@ -1965,52 +2058,67 @@ static void setuphandlers(void) upsh.instcmd = instcmd; } -/* functions that interface with main.c */ +/* ---- functions that interface with main.c ------------------------------- */ void upsdrv_makevartable(void) { - addvar(VAR_VALUE, "cable", "specify alternate cable (940-0095B)"); + addvar(VAR_VALUE, "ttymode", "tty discipline selection"); + addvar(VAR_VALUE, "cable", "alternate cable (940-0095B) selection"); addvar(VAR_VALUE, "awd", "hard hibernate's additional wakeup delay"); - addvar(VAR_VALUE, "sdtype", "specify simple shutdown method (0 - " APC_SDMAX ")"); - addvar(VAR_VALUE, "advorder", "enable advanced shutdown control"); + addvar(VAR_VALUE, "sdtype", "simple shutdown method"); + addvar(VAR_VALUE, "advorder", "advanced shutdown control"); + addvar(VAR_VALUE, "cshdelay", "CS hack delay"); +} + +void upsdrv_help(void) +{ + printf( + "\nFor detailed information, please refer to:\n" + " - apcsmart(8)\n" + " - http://www.networkupstools.org/docs/man/apcsmart.html\n" + ); } void upsdrv_initups(void) { - size_t i, len; char *val; - - upsfd = extrafd = ser_open(device_path); - apc_ser_set(); + apc_vartab_t *ptr; /* sanitize awd (additional waekup delay of '@' command) */ - if ((val = getval("awd")) && rexhlp(APC_AWDFMT, val)) { + if ((val = getval("awd")) && !rexhlp(APC_AWDFMT, val)) { fatalx(EXIT_FAILURE, "invalid value (%s) for option 'awd'", val); } /* sanitize sdtype */ - if ((val = getval("sdtype")) && rexhlp(APC_SDFMT, val)) { + if ((val = getval("sdtype")) && !rexhlp(APC_SDFMT, val)) { fatalx(EXIT_FAILURE, "invalid value (%s) for option 'sdtype'", val); } /* sanitize advorder */ - if ((val = getval("advorder")) && strcasecmp(val, "no")) { - len = strlen(val); - - if (!len || len > SDCNT) - fatalx(EXIT_FAILURE, "invalid length of 'advorder' option (%s)", val); - for (i = 0; i < len; i++) { - if (val[i] < '0' || val[i] >= '0' + SDCNT) { - fatalx(EXIT_FAILURE, "invalid characters in 'advorder' option (%s)", val); - } - } + if ((val = getval("advorder")) && !rexhlp(APC_ADVFMT, val)) { + fatalx(EXIT_FAILURE, "invalid value (%s) for option 'advorder'", val); } + + /* sanitize cshdelay */ + if ((val = getval("cshdelay")) && !rexhlp(APC_CSHDFMT, val)) { + fatalx(EXIT_FAILURE, "invalid value (%s) for option 'cshdelay'", val); + } + + upsfd = extrafd = ser_open(device_path); + apc_ser_set(); + + /* fill length values */ + for (ptr = apc_vartab; ptr->name; ptr++) + ptr->nlen0 = strlen(ptr->name) + 1; } void upsdrv_cleanup(void) { char temp[APC_LBUF]; + if (upsfd == -1) + return; + apc_flush(0); /* try to bring the UPS out of smart mode */ apc_write(APC_GODUMB); @@ -2018,10 +2126,6 @@ void upsdrv_cleanup(void) ser_close(upsfd, device_path); } -void upsdrv_help(void) -{ -} - void upsdrv_initinfo(void) { const char *pmod, *pser; @@ -2033,11 +2137,15 @@ void upsdrv_initinfo(void) ); } + if (!getbaseinfo()) { + fatalx(EXIT_FAILURE, + "Problems with communicating APC UPS on port %s\n", device_path + ); + } + /* manufacturer ID - hardcoded in this particular module */ dstate_setinfo("ups.mfr", "APC"); - getbaseinfo(); - if (!(pmod = dstate_getinfo("ups.model"))) pmod = "\"unknown model\""; if (!(pser = dstate_getinfo("ups.serial"))) @@ -2046,18 +2154,24 @@ void upsdrv_initinfo(void) upsdebugx(1, "detected %s [%s] on %s", pmod, pser, device_path); setuphandlers(); + /* + * seems to be ok so far, it must be set so initial call of + * upsdrv_updateinfo() doesn't begin with stale condition + */ + dstate_dataok(); } void upsdrv_updateinfo(void) { static int last_worked = 0; static time_t last_full = 0; + int all; time_t now; /* try to wake up a dead ups once in awhile */ if (dstate_is_stale()) { if (!last_worked) - upsdebugx(LOG_DEBUG, "upsdrv_updateinfo: comm lost"); + upsdebugx(1, "%s: %s", __func__, "comm lost"); /* reset this so a full update runs when the UPS returns */ last_full = 0; @@ -2066,15 +2180,17 @@ void upsdrv_updateinfo(void) return; /* become aggressive after a few tries */ - upsdebugx(LOG_DEBUG, "upsdrv_updateinfo: nudging ups with 'Y', iteration #%d ...", last_worked); + upsdebugx(1, "%s: nudging ups with 'Y', iteration #%d ...", __func__, last_worked); if (!smartmode(1)) return; last_worked = 0; } - if (!update_status()) + if (!update_status()) { + dstate_datastale(); return; + } time(&now); @@ -2082,9 +2198,13 @@ void upsdrv_updateinfo(void) /* does not catch measure-ups II insertion/removal */ if (difftime(now, last_full) > 3600) { last_full = now; - update_info_all(); - return; - } + all = 1; + } else + all = 0; - update_info_normal(); + if (update_info(all)) { + dstate_dataok(); + } else { + dstate_datastale(); + } } diff --git a/drivers/apcsmart.h b/drivers/apcsmart.h index e10c5fe..fe7bd1a 100644 --- a/drivers/apcsmart.h +++ b/drivers/apcsmart.h @@ -3,7 +3,7 @@ * * Copyright (C) 1999 Russell Kroll * (C) 2000 Nigel Metheringham - * (C) 2011 Michal Soltys + * (C) 2011+ Michal Soltys * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,11 +20,11 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef __apcsmart_h__ -#define __apcsmart_h__ +#ifndef NUT_APCSMART_H_SEEN +#define NUT_APCSMART_H_SEEN 1 #define DRIVER_NAME "APC Smart protocol driver" -#define DRIVER_VERSION "3.0" +#define DRIVER_VERSION "3.2" #define ALT_CABLE_1 "940-0095B" @@ -59,11 +59,22 @@ * as it's handled by IGNCR at read() level */ +/* + * about CR: + * apparently windows is unable to ignore CRs by means of IGNCR flag (or + * perhaps there is something else involved); so despite IGNCR we re-aad \015 + * to ignore sets for now; see: + * http://article.gmane.org/gmane.comp.monitoring.nut.user/7762 + * + * furthermore, since the canonical/non-canonical mode is user selectable now, + * we have to ignore this character explicitly + */ + /* Basic UPS reply line structure */ #define ENDCHAR 10 /* APC ends responses with LF (and CR, but it's IGNCRed) */ /* what to ignore during alert aware serial reads */ -#define IGN_AACHARS "|&" +#define IGN_AACHARS "\015|&" /* what alert_handler() should care about */ #define ALERT_CHARS "$!%+#?=" @@ -72,15 +83,15 @@ #define IGN_CHARS IGN_AACHARS ALERT_CHARS /* - * these ones are used only during capability read, due to ^Z sending certain - * characters such as #; it seems it could be equal to just IGN_CHARS w/o # - * old: #define IGN_CCCHARS "|$!+" + * these ones are used only during Capability Check read, due to ^Z sending + * certain characters such as #; it seems it could be equal to just IGN_CHARS + * w/o # old: #define IGN_CCCHARS "|$!+" */ -#define IGN_CCCHARS "|&$!%+?=" /* capability check ignore set */ +#define IGN_CCCHARS "\015|&$!%+?=" /* capability check ignore set */ /* - * command set 'a' command reports everything - protocol number, alerts and - * supported commands + * Command Set 'a' reports everything - protocol number, alerts and supported + * commands */ #define IGN_CSCHARS "" /* command set ignore set */ @@ -95,9 +106,9 @@ #define SER_DX 0x002 /* 200 ms for long/repeated cmds, in case of unexpected NAs */ #define SER_D1 0x004 /* 1.5 sec. */ #define SER_D3 0x008 /* 3 sec. (default) */ -#define SER_AA 0x010 /* alert aware set */ -#define SER_CC 0x020 /* capability check ign set */ -#define SER_CS 0x040 /* command set ign set */ +#define SER_AA 0x010 /* Alert Aware set */ +#define SER_CC 0x020 /* Capability Check ign set */ +#define SER_CS 0x040 /* Command Set ign set */ #define SER_TO 0x080 /* timeout allowed */ #define SER_HA 0x100 /* handle asterisk */ @@ -115,14 +126,14 @@ /* status bits */ -#define APC_STAT_CAL 0x01 /* calibration */ -#define APC_STAT_TRIM 0x02 /* SmartTrim */ -#define APC_STAT_BOOST 0x04 /* SmartBoost */ -#define APC_STAT_OL 0x08 /* on line */ -#define APC_STAT_OB 0x10 /* on battery */ -#define APC_STAT_OVER 0x20 /* overload */ -#define APC_STAT_LB 0x40 /* low battery */ -#define APC_STAT_RB 0x80 /* replace battery */ +#define APC_STAT_CAL (1L << 0) /* calibration */ +#define APC_STAT_TRIM (1L << 1) /* SmartTrim */ +#define APC_STAT_BOOST (1L << 2) /* SmartBoost */ +#define APC_STAT_OL (1L << 3) /* on line */ +#define APC_STAT_OB (1L << 4) /* on battery */ +#define APC_STAT_OVER (1L << 5) /* overload */ +#define APC_STAT_LB (1L << 6) /* low battery */ +#define APC_STAT_RB (1L << 7) /* replace battery */ /* * serial protocol: special commands - initialization and such @@ -132,6 +143,7 @@ #define APC_GOSMART 'Y' #define APC_GODUMB 'R' #define APC_CMDSET 'a' +#define APC_CMDSET_FMT "^[0-9]\\.[^.]*\\.[^.]+(\\.[^.]+)?$" #define APC_CAPS '\032' /* ^Z */ #define APC_NEXTVAL '-' #define APC_FW_OLD 'V' @@ -141,11 +153,15 @@ #define APC_SBUF 32 /* default a.w.d. value / regex format for command '@' */ -#define APC_AWDDEF "000" #define APC_AWDFMT "^[0-9]{1,3}$" -/* maximum number of supported sdtype methods + regex format*/ -#define APC_SDMAX "5" +/* sdtype method regex format*/ #define APC_SDFMT "^[0-5]$" -#endif +/* advorder method regex format*/ +#define APC_ADVFMT "^([0-4]{1,5}|[Nn][Oo])$" + +/* cshdelay format */ +#define APC_CSHDFMT "^([0-9]\\.?|[0-9]?\\.[0-9])$" + +#endif /* NUT_APCSMART_H_SEEN */ diff --git a/drivers/apcsmart_tabs.c b/drivers/apcsmart_tabs.c index 7b5c3cd..0955e01 100644 --- a/drivers/apcsmart_tabs.c +++ b/drivers/apcsmart_tabs.c @@ -2,7 +2,7 @@ * * Copyright (C) 1999 Russell Kroll * (C) 2000 Nigel Metheringham - * (C) 2011 Michal Soltys + * (C) 2011+ Michal Soltys * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,52 +23,55 @@ /* APC_MULTI variables *must* be listed in order of preference */ apc_vartab_t apc_vartab[] = { +/* name cmd flags regex nlen0 cnt */ + { "ups.temperature", 'C', APC_POLL|APC_F_CELSIUS, NULL, 0, 0 }, + { "ups.load", 'P', APC_POLL|APC_F_PERCENT, NULL, 0, 0 }, + { "ups.test.interval", 'E', APC_F_HOURS, NULL, 0, 0 }, + { "ups.test.result", 'X', APC_POLL, NULL, 0, 0 }, + { "ups.delay.start", 'r', APC_F_SECONDS, NULL, 0, 0 }, + { "ups.delay.shutdown", 'p', APC_F_SECONDS, NULL, 0, 0 }, + { "ups.id", 'c', APC_STRING, NULL, 0, 0 }, + { "ups.contacts", 'i', APC_POLL|APC_F_HEX, NULL, 0, 0 }, + { "ups.display.language", '\014', 0, NULL, 0, 0 }, + { "input.voltage", 'L', APC_POLL|APC_F_VOLT, NULL, 0, 0 }, + { "input.frequency", 'F', APC_POLL|APC_F_DEC, NULL, 0, 0 }, + { "input.sensitivity", 's', 0, NULL, 0, 0 }, + { "input.quality", '9', APC_POLL|APC_F_HEX, NULL, 0, 0 }, + { "input.transfer.low", 'l', APC_F_VOLT, NULL, 0, 0 }, + { "input.transfer.high", 'u', APC_F_VOLT, NULL, 0, 0 }, + { "input.transfer.reason", 'G', APC_POLL|APC_F_REASON, NULL, 0, 0 }, + { "input.voltage.maximum", 'M', APC_POLL|APC_F_VOLT, NULL, 0, 0 }, + { "input.voltage.minimum", 'N', APC_POLL|APC_F_VOLT, NULL, 0, 0 }, + { "output.current", '/', APC_POLL|APC_F_AMP, NULL, 0, 0 }, + { "output.voltage", 'O', APC_POLL|APC_F_VOLT, NULL, 0, 0 }, + { "output.voltage.nominal", 'o', APC_F_VOLT, NULL, 0, 0 }, + { "ambient.humidity", 'h', APC_POLL|APC_F_PERCENT, NULL, 0, 0 }, + { "ambient.0.humidity", 'H', APC_POLL|APC_PACK|APC_F_PERCENT, NULL, 0, 0 }, + { "ambient.0.humidity.high", '{', APC_POLL|APC_PACK|APC_F_PERCENT, NULL, 0, 0 }, + { "ambient.0.humidity.low", '}', APC_POLL|APC_PACK|APC_F_PERCENT, NULL, 0, 0 }, + { "ambient.temperature", 't', APC_POLL|APC_F_CELSIUS, NULL, 0, 0 }, + { "ambient.0.temperature", 'T', APC_MULTI|APC_POLL|APC_PACK|APC_F_CELSIUS, "^[0-9]{2}\\.[0-9]{2}$", 0, 0 }, + { "ambient.0.temperature.high", '[', APC_POLL|APC_PACK|APC_F_CELSIUS, NULL, 0, 0 }, + { "ambient.0.temperature.low", ']', APC_POLL|APC_PACK|APC_F_CELSIUS, NULL, 0, 0 }, + { "battery.date", 'x', APC_STRING, NULL, 0, 0 }, + { "battery.charge", 'f', APC_POLL|APC_F_PERCENT, NULL, 0, 0 }, + { "battery.charge.restart", 'e', APC_F_PERCENT, NULL, 0, 0 }, + { "battery.voltage", 'B', APC_POLL|APC_F_VOLT, NULL, 0, 0 }, + { "battery.voltage.nominal", 'g', 0, NULL, 0, 0 }, + { "battery.runtime", 'j', APC_POLL|APC_F_MINUTES, NULL, 0, 0 }, + { "battery.runtime.low", 'q', APC_F_MINUTES, NULL, 0, 0 }, + { "battery.packs", '>', APC_F_DEC, NULL, 0, 0 }, + { "battery.packs.bad", '<', APC_F_DEC, NULL, 0, 0 }, + { "battery.alarm.threshold", 'k', 0, NULL, 0, 0 }, + { "device.uptime", 'T', APC_MULTI|APC_POLL|APC_F_HOURS, "^[0-9]{3}\\.[0-9]{1}$", 0, 0 }, + { "ups.serial", 'n', 0, NULL, 0, 0 }, + { "ups.mfr.date", 'm', 0, NULL, 0, 0 }, + { "ups.model", '\001', 0, NULL, 0, 0 }, + { "ups.firmware.aux", 'v', 0, NULL, 0, 0 }, + { "ups.firmware", 'b', APC_MULTI, "^[[:alnum:]]+\\.[[:alnum:]]+\\.[[:alnum:]]+$", 0, 0 }, + { "ups.firmware", 'V', APC_MULTI, NULL, 0, 0 }, - { "ups.temperature", 'C', APC_POLL|APC_F_CELSIUS }, - { "ups.load", 'P', APC_POLL|APC_F_PERCENT }, - { "ups.test.interval", 'E', APC_F_HOURS }, - { "ups.test.result", 'X', APC_POLL }, - { "ups.delay.start", 'r', APC_F_SECONDS }, - { "ups.delay.shutdown", 'p', APC_F_SECONDS }, - { "ups.id", 'c', APC_STRING }, - { "ups.contacts", 'i', APC_POLL|APC_F_HEX }, - { "ups.display.language", '\014', 0 }, - { "input.voltage", 'L', APC_POLL|APC_F_VOLT }, - { "input.frequency", 'F', APC_POLL|APC_F_DEC }, - { "input.sensitivity", 's', 0 }, - { "input.quality", '9', APC_POLL|APC_F_HEX }, - { "input.transfer.low", 'l', APC_F_VOLT }, - { "input.transfer.high", 'u', APC_F_VOLT }, - { "input.transfer.reason", 'G', APC_POLL|APC_F_REASON }, - { "input.voltage.maximum", 'M', APC_POLL|APC_F_VOLT }, - { "input.voltage.minimum", 'N', APC_POLL|APC_F_VOLT }, - { "output.current", '/', APC_POLL|APC_F_AMP }, - { "output.voltage", 'O', APC_POLL|APC_F_VOLT }, - { "output.voltage.nominal", 'o', APC_F_VOLT }, - { "ambient.humidity", 'h', APC_POLL|APC_F_PERCENT }, - { "ambient.humidity.high", '{', APC_F_PERCENT }, - { "ambient.humidity.low", '}', APC_F_PERCENT }, - { "ambient.temperature", 't', APC_POLL|APC_F_CELSIUS }, - { "ambient.temperature.high", '[', APC_F_CELSIUS }, - { "ambient.temperature.low", ']', APC_F_CELSIUS }, - { "battery.date", 'x', APC_STRING }, - { "battery.charge", 'f', APC_POLL|APC_F_PERCENT }, - { "battery.charge.restart", 'e', APC_F_PERCENT }, - { "battery.voltage", 'B', APC_POLL|APC_F_VOLT }, - { "battery.voltage.nominal", 'g', 0 }, - { "battery.runtime", 'j', APC_POLL|APC_F_MINUTES }, - { "battery.runtime.low", 'q', APC_F_MINUTES }, - { "battery.packs", '>', APC_F_DEC }, - { "battery.packs.bad", '<', APC_F_DEC }, - { "battery.alarm.threshold", 'k', 0 }, - { "ups.serial", 'n', 0 }, - { "ups.mfr.date", 'm', 0 }, - { "ups.model", '\001', 0 }, - { "ups.firmware.aux", 'v', 0 }, - { "ups.firmware", 'b', APC_MULTI }, - { "ups.firmware", 'V', APC_MULTI }, - - { 0, 0, 0 } + { NULL, 0, 0, NULL, 0, 0 } /* todo: I = alarm enable (hex field) - split into alarm.n.enable @@ -81,27 +84,29 @@ apc_vartab_t apc_vartab[] = { }; /* - * apc commands mapped to nut's instant commands extra values are either - * exactly 2-char prefix, or longer than 2-char extended regex + * APC commands mapped to NUT's instant commands + * the format of extra values is matched by extended posix regex + * APC_CMD_CUSTOM means that the instant command is handled by separate + * function, thus the actual APC cmd in the table is ignored */ apc_cmdtab_t apc_cmdtab[] = { - { "test.panel.start", 0, APC_CMD_FPTEST, 0 }, - { "test.failure.start", 0, APC_CMD_SIMPWF, 0 }, - { "test.battery.start", 0, APC_CMD_BTESTTOGGLE, 0 }, - { "test.battery.stop", 0, APC_CMD_BTESTTOGGLE, 0 }, - { "shutdown.return", "^at:[0-9]{1,3}$", - APC_CMD_GRACEDOWN, APC_NASTY }, - { "shutdown.return", "cs", APC_CMD_SOFTDOWN, APC_NASTY }, - { "shutdown.return", 0, APC_CMD_SOFTDOWN, APC_NASTY }, - { "shutdown.stayoff", 0, APC_CMD_SHUTDOWN, APC_NASTY|APC_REPEAT }, - { "load.off", 0, APC_CMD_OFF, APC_NASTY|APC_REPEAT }, - { "load.on", 0, APC_CMD_ON, APC_REPEAT }, - { "bypass.start", 0, APC_CMD_BYPTOGGLE, 0 }, - { "bypass.stop", 0, APC_CMD_BYPTOGGLE, 0 }, - { "calibrate.start", 0, APC_CMD_CALTOGGLE, 0 }, - { "calibrate.stop", 0, APC_CMD_CALTOGGLE, 0 }, + { "shutdown.return", "^[Aa][Tt]:[0-9]{1,3}$", + APC_CMD_GRACEDOWN, APC_NASTY }, + { "shutdown.return", "^([Cc][Ss]|)$", + APC_CMD_SOFTDOWN, APC_NASTY }, + { "shutdown.stayoff", NULL, APC_CMD_SHUTDOWN, APC_NASTY|APC_REPEAT }, + { "load.off", NULL, APC_CMD_OFF, APC_NASTY|APC_REPEAT }, + { "load.on", NULL, APC_CMD_ON, APC_REPEAT }, + { "calibrate.start", NULL, APC_CMD_CALTOGGLE, 0 }, + { "calibrate.stop", NULL, APC_CMD_CALTOGGLE, 0 }, + { "test.panel.start", NULL, APC_CMD_FPTEST, 0 }, + { "test.failure.start", NULL, APC_CMD_SIMPWF, 0 }, + { "test.battery.start", NULL, APC_CMD_BTESTTOGGLE, 0 }, + { "test.battery.stop", NULL, APC_CMD_BTESTTOGGLE, 0 }, + { "bypass.start", NULL, APC_CMD_BYPTOGGLE, 0 }, + { "bypass.stop", NULL, APC_CMD_BYPTOGGLE, 0 }, - { 0, 0, 0, 0 } + { NULL, NULL, 0, 0 } }; /* compatibility with hardware that doesn't do APC_CMDSET ('a') */ @@ -122,11 +127,16 @@ apc_compattab_t apc_compattab[] = { { "7QI", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "7TD", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "7TI", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, + /* SmartUPS 600I */ + { "6JI", "@789ABCFGKLMNOPQSTUVWXYZfg", 0 }, /* SmartUPS 900I */ - { "7II", "@79ABCEFGKLMNOPQSUVWXYZcfg", 0 }, + { "7II", "@79ABCEFGKLMNOPQRSUVWXYZcfg", 0 }, /* SmartUPS 2000I */ - { "9II", "@79ABCEFGKLMNOPQSUVWXYZcfg", 0 }, - { "9GI", "@79ABCEFGKLMNOPQSUVWXYZcfg", 0 }, + { "9II", "@79ABCEFGKLMNOPQRSUVWXYZcfg", 0 }, + { "9GI", "@79ABCEFGKLMNOPQRSUVWXYZcfg", 0 }, + /* SmartUPS 1250I */ + { "8II", "@79ABCEFGKLMNOPQRSUVWXYZcfg", 0 }, + { "8GI", "@79ABCEFGKLMNOPQRSUVWXYZfg", 0 }, /* SmartUPS 1250 */ { "8QD", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "8QI", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, @@ -134,22 +144,23 @@ apc_compattab_t apc_compattab[] = { { "8TI", "@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, /* CS 350 */ { "5.4.D", "@\1ABPQRSUYbdfgjmnx9", 0 }, - /* Smart-UPS 600 */ - { "D9", "@789ABCEFGKLMNOPQRSUVWXYZ", 0 }, - { "D8", "@789ABCEFGKLMNOPQRSUVWXYZ", 0 }, - { "D7", "@789ABCEFGKLMNOPQRSUVWXYZ", 0 }, - { "D6", "@789ABCEFGKLMNOPQRSUVWXYZ", 0 }, - { "D5", "@789ABCEFGKLMNOPQRSUVWXYZ", 0 }, - { "D4", "@789ABCEFGKLMNOPQRSUVWXYZ", 0 }, + /* + * certain set of UPSes returning voltage > 255V through 'b'; "set\1" + * is matched explicitly (fake key); among the UPS models - some old + * APC 600 ones + */ + { "set\1", "@789ABCFGKLMNOPQRSUVWXYZ", 0 }, - { 0, 0, 0 } + { NULL, NULL, 0 } }; upsdrv_info_t apc_tab_info = { "APC command table", APC_TABLE_VERSION, - 0, - 0, - { 0 } + "Russell Kroll \n" \ + "Nigel Metheringham \n" \ + "Michal Soltys ", + DRV_STABLE, + { NULL } }; diff --git a/drivers/apcsmart_tabs.h b/drivers/apcsmart_tabs.h index c49ce9e..2e2e44c 100644 --- a/drivers/apcsmart_tabs.h +++ b/drivers/apcsmart_tabs.h @@ -3,7 +3,7 @@ * * Copyright (C) 1999 Russell Kroll * (C) 2000 Nigel Metheringham - * (C) 2011 Michal Soltys + * (C) 2011+ Michal Soltys * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,12 +20,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef __apcsmart_tabs_h__ -#define __apcsmart_tabs_h__ +#ifndef NUT_APCSMART_TABS_H_SEEN +#define NUT_APCSMART_TABS_H_SEEN 1 #include "main.h" -#define APC_TABLE_VERSION "version 3.0" +#define APC_TABLE_VERSION "version 3.1" /* common flags */ @@ -43,11 +43,14 @@ #define APC_ENUM 0x00000400 /* enumerated type variable */ #define APC_STRING 0x00000800 /* string variable */ #define APC_MULTI 0x00001000 /* there're other vars like that */ -#define APC_DEPR 0x00002000 /* deprecated variable */ +#define APC_PACK 0x00002000 /* packed variable */ + +#define APC_PACK_MAX 4 /* max count of subfields in packed var */ /* variables' format */ #define APC_F_MASK 0xFF000000 /* Mask for apc data formats */ +#define APC_F_LEAVE 0x00000000 /* Just pass this through */ #define APC_F_PERCENT 0x01000000 /* Data in a percent format */ #define APC_F_VOLT 0x02000000 /* Data in a voltage format */ #define APC_F_AMP 0x03000000 /* Data in a current/amp format */ @@ -57,13 +60,13 @@ #define APC_F_SECONDS 0x07000000 /* Time in seconds */ #define APC_F_MINUTES 0x08000000 /* Time in minutes */ #define APC_F_HOURS 0x09000000 /* Time in hours */ -#define APC_F_REASON 0x10000000 /* Reason of transfer */ -#define APC_F_LEAVE 0x00000000 /* Just pass this through */ +#define APC_F_REASON 0x0A000000 /* Reason of transfer */ /* instant commands */ +#define APC_CMD_CUSTOM 0 /* command uses separate function */ #define APC_CMD_OFF 'Z' -#define APC_CMD_ON '\016' /* ^N */ +#define APC_CMD_ON '\016' /* ^N */ #define APC_CMD_FPTEST 'A' #define APC_CMD_SIMPWF 'U' #define APC_CMD_BTESTTOGGLE 'W' @@ -78,6 +81,9 @@ typedef struct { const char *name; /* the variable name */ char cmd; /* variable character */ unsigned int flags; /* various flags */ + const char *regex; /* variable must match this regex */ + size_t nlen0; /* var name + null len */ + int cnt; /* curr. count of subs */ } apc_vartab_t; typedef struct { @@ -97,4 +103,4 @@ extern apc_cmdtab_t apc_cmdtab[]; extern apc_compattab_t apc_compattab[]; extern upsdrv_info_t apc_tab_info; -#endif +#endif /* NUT_APCSMART_TABS_H_SEEN */ diff --git a/drivers/apcupsd-ups.c b/drivers/apcupsd-ups.c new file mode 100644 index 0000000..29d20dd --- /dev/null +++ b/drivers/apcupsd-ups.c @@ -0,0 +1,325 @@ +/* apcupsd-ups.c - client for apcupsd + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +*/ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "main.h" +#include "apcupsd-ups.h" +#include "attribute.h" + +#define DRIVER_NAME "apcupsd network client UPS driver" +#define DRIVER_VERSION "0.6" + +#define POLL_INTERVAL_MIN 10 + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Andreas Steinmetz ", + DRV_STABLE, + { NULL } +}; + +static uint16_t port=3551; +static struct sockaddr_in host; + +static void process(char *item,char *data) +{ + int i; + char *p1; + char *p2; + + for(i=0;nut_data[i].info_type;i++)if(!(nut_data[i].apcupsd_item)) + dstate_setinfo(nut_data[i].info_type,"%s", + nut_data[i].default_value); + else if(!strcmp(nut_data[i].apcupsd_item,item)) + switch(nut_data[i].drv_flags&~DU_FLAG_INIT) + { + case DU_FLAG_STATUS: + status_init(); + if(!strcmp(data,"COMMLOST")||!strcmp(data,"NETWORK ERROR")|| + !strcmp(data,"ERROR"))status_set("OFF"); + else if(!strcmp(data,"SELFTEST"))status_set("OB"); + else for(;(data=strtok(data," "));data=NULL) + { + if(!strcmp(data,"CAL"))status_set("CAL"); + else if(!strcmp(data,"TRIM"))status_set("TRIM"); + else if(!strcmp(data,"BOOST"))status_set("BOOST"); + else if(!strcmp(data,"ONLINE"))status_set("OL"); + else if(!strcmp(data,"ONBATT"))status_set("OB"); + else if(!strcmp(data,"OVERLOAD"))status_set("OVER"); + else if(!strcmp(data,"SHUTTING DOWN")|| + !strcmp(data,"LOWBATT"))status_set("LB"); + else if(!strcmp(data,"REPLACEBATT"))status_set("RB"); + else if(!strcmp(data,"NOBATT"))status_set("BYPASS"); + } + status_commit(); + break; + + case DU_FLAG_DATE: + if((p1=strchr(data,' '))) + { + *p1=0; + dstate_setinfo(nut_data[i].info_type,"%s",data); + *p1=' '; + } + else dstate_setinfo(nut_data[i].info_type,"%s",data); + break; + + case DU_FLAG_TIME: + if((p1=strchr(data,' '))) + { + *p1=0; + if((p2=strchr(p1+1,' '))) + { + *p2=0; + dstate_setinfo(nut_data[i].info_type,"%s",p1+1); + *p2=' '; + } + else dstate_setinfo(nut_data[i].info_type,"%s",p1+1); + *p1=' '; + } + break; + + case DU_FLAG_FW1: + if((p1=strchr(data,'/'))) + { + for(;p1!=data;p1--)if(p1[-1]!=' ')break; + if(*p1==' ') + { + *p1=0; + dstate_setinfo(nut_data[i].info_type,"%s",data); + *p1=' '; + } + else dstate_setinfo(nut_data[i].info_type,"%s",data); + } + else dstate_setinfo(nut_data[i].info_type,"%s",data); + break; + + case DU_FLAG_FW2: + if((p1=strchr(data,'/'))) + { + for(;*p1;p1++)if(p1[1]!=' ')break; + if(*p1&&p1[1])dstate_setinfo(nut_data[i].info_type,"%s", + p1+1); + } + break; + + default:if(nut_data[i].info_flags&ST_FLAG_STRING) + { + if((int)strlen(data)>(int)nut_data[i].info_len) + data[(int)nut_data[i].info_len]=0; + dstate_setinfo(nut_data[i].info_type,"%s",data); + } + else + { +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + /* default_value acts as a format string in this case */ + dstate_setinfo(nut_data[i].info_type, + nut_data[i].default_value, + atof(data)*nut_data[i].info_len); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + } + break; + } +} + +static int getdata(void) +{ + ssize_t x; + int fd_flags; + uint16_t n; + char *item; + char *data; + struct pollfd p; + char bfr[1024]; + + for(x=0;nut_data[x].info_type;x++) + if(!(nut_data[x].drv_flags & DU_FLAG_INIT) && !(nut_data[x].drv_flags & DU_FLAG_PRESERVE)) + dstate_delinfo(nut_data[x].info_type); + + if((p.fd=socket(AF_INET,SOCK_STREAM,0))==-1) + { + upsdebugx(1,"socket error"); + return -1; + } + + if(connect(p.fd,(struct sockaddr *)&host,sizeof(host))) + { + upsdebugx(1,"can't connect to apcupsd"); + close(p.fd); + return -1; + } + + fd_flags = fcntl(p.fd, F_GETFL); + fd_flags |= O_NONBLOCK; + if(fcntl(p.fd, F_SETFL, fd_flags)) + { + upsdebugx(1,"unexpected fcntl(fd, F_SETFL, fd_flags|O_NONBLOCK) failure"); + close(p.fd); + return -1; + } + + p.events=POLLIN; + + n=htons(6); + x=write(p.fd,&n,2); + x=write(p.fd,"status",6); + + /* TODO: double-check for poll() in configure script */ + while(poll(&p,1,15000)==1) + { + if(read(p.fd,&n,2)!=2) + { + upsdebugx(1,"apcupsd communication error"); + close(p.fd); + return -1; + } + + if(!(x=ntohs(n))) + { + close(p.fd); + return 0; + } + else if(x<0||x>=(int)sizeof(bfr)) + /* Note: LGTM.com suggests "Comparison is always false because x >= 0" + * for the line above, probably because ntohs() returns an uint type. + * I am reluctant to fix this one, because googling for headers from + * random OSes showed various types used as the return value (uint16_t, + * unsigned_short, u_short, in_port_t...) + */ + { + upsdebugx(1,"apcupsd communication error"); + close(p.fd); + return -1; + } + + if(poll(&p,1,15000)!=1)break; + + if(read(p.fd,bfr,(size_t)x)!=x) + { + upsdebugx(1,"apcupsd communication error"); + close(p.fd); + return -1; + } + + bfr[x]=0; + + if(!(item=strtok(bfr," \t:\r\n"))) + { + upsdebugx(1,"apcupsd communication error"); + close(p.fd); + return -1; + } + + if(!(data=strtok(NULL,"\r\n"))) + { + upsdebugx(1,"apcupsd communication error"); + close(p.fd); + return -1; + } + while(*data==' '||*data=='\t'||*data==':')data++; + + process(item,data); + } + + upsdebugx(1,"unexpected connection close by apcupsd"); + close(p.fd); + return -1; +} + +void upsdrv_initinfo(void) +{ + if(!port)fatalx(EXIT_FAILURE,"invalid host or port specified!"); + if(getdata())fatalx(EXIT_FAILURE,"can't communicate with apcupsd!"); + else dstate_dataok(); + + poll_interval = (poll_interval > POLL_INTERVAL_MIN) ? POLL_INTERVAL_MIN : poll_interval; +} + +void upsdrv_updateinfo(void) +{ + if(getdata())upslogx(LOG_ERR,"can't communicate with apcupsd!"); + else dstate_dataok(); + + poll_interval = (poll_interval > POLL_INTERVAL_MIN) ? POLL_INTERVAL_MIN : poll_interval; +} + +void upsdrv_shutdown(void) + __attribute__((noreturn)); + +void upsdrv_shutdown(void) +{ + fatalx(EXIT_FAILURE, "shutdown not supported"); +} + +void upsdrv_help(void) +{ +} + +void upsdrv_makevartable(void) +{ +} + +void upsdrv_initups(void) +{ + char *p; + struct hostent *h; + + if(device_path&&*device_path) + { + /* TODO: fix parsing since bare IPv6 addresses contain colons */ + if((p=strchr(device_path,':'))) + { + int i; + *p++=0; + i=atoi(p); + if(i<1||i>65535)i=0; + port = (uint16_t)i; + } + } + else device_path="localhost"; + + if(!(h=gethostbyname(device_path)))port=0; + else memcpy(&host.sin_addr,h->h_addr,4); + + /* TODO: add IPv6 support */ + host.sin_family=AF_INET; + host.sin_port=htons(port); +} + +void upsdrv_cleanup(void) +{ +} diff --git a/drivers/apcupsd-ups.h b/drivers/apcupsd-ups.h new file mode 100644 index 0000000..8abdbf4 --- /dev/null +++ b/drivers/apcupsd-ups.h @@ -0,0 +1,150 @@ +/* apcupsd-ups.h - NUT client driver to apcupsd + + Copyright (C) + 2005 - 2010 Arnaud Quette + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_APCUPSD_UPS_H_SEEN +#define NUT_APCUPSD_UPS_H_SEEN 1 + +/* from usbhid-ups.h */ +/* --------------------------------------------------------------- */ +/* Struct & data for ups.status processing */ +/* --------------------------------------------------------------- */ + +typedef struct { + const char *status_str; /* ups.status string */ + int status_value; /* ups.status value */ +} status_lkp_t; + +#define STATUS_CAL 1 /* calibration */ +#define STATUS_TRIM 2 /* SmartTrim */ +#define STATUS_BOOST 4 /* SmartBoost */ +#define STATUS_OL 8 /* on line */ +#define STATUS_OB 16 /* on battery */ +#define STATUS_OVER 32 /* overload */ +#define STATUS_LB 64 /* low battery */ +#define STATUS_RB 128 /* replace battery */ +#define STATUS_BYPASS 256 /* on bypass */ +#define STATUS_OFF 512 /* ups is off */ +#define STATUS_CHRG 1024 /* charging */ +#define STATUS_DISCHRG 2048 /* discharging */ + +/* +static status_lkp_t status_info[] = { + { "CAL", STATUS_CAL }, + { "TRIM", STATUS_TRIM }, + { "BOOST", STATUS_BOOST }, + { "OL", STATUS_OL }, + { "OB", STATUS_OB }, + { "OVER", STATUS_OVER }, + { "LB", STATUS_LB }, + { "RB", STATUS_RB }, + { "BYPASS", STATUS_BYPASS }, + { "OFF", STATUS_OFF }, + { "CHRG", STATUS_CHRG }, + { "DISCHRG", STATUS_DISCHRG }, + { "NULL", 0 }, +}; +*/ +/* from usbhid-ups.h */ + +typedef struct { + char hid_value; /* HID value */ + char *nut_value; /* NUT value */ +} info_enum_t; + +/* --------------------------------------------------------------- */ +/* Structure containing information about how to get/set data */ +/* from/to the UPS and convert these to/from NUT standard */ +/* --------------------------------------------------------------- */ + +typedef struct { + const char *apcupsd_item; + const char *info_type; /* NUT variable name */ + int info_flags; /* NUT flags (to set in addinfo) */ + float info_len; /* if ST_FLAG_STRING: length of the string */ + /* if HU_TYPE_CMD: command value ; multiplier (or max len) otherwise */ + const char *default_value; /* if HU_FLAG_ABSENT: default value ; format otherwise */ + int drv_flags; /* */ + char **var_values; /* all possible values for this variable (allows to check data...) */ + /* FIXME: "void *" so we can have bound or enum */ +/* interpreter interpret; */ /* FFE: interpreter fct, NULL if not needed */ +} apcuspd_info_t; + +/* data flags */ +#define DU_FLAG_NONE 0 +#define DU_FLAG_INIT 1 /* intialy show element to upsd */ +#define DU_FLAG_STATUS 2 +#define DU_FLAG_DATE 4 +#define DU_FLAG_TIME 8 +#define DU_FLAG_FW1 16 +#define DU_FLAG_FW2 32 +#define DU_FLAG_PRESERVE 64 + +/* ------------ */ +/* Data table */ +/* ------------ */ + +static apcuspd_info_t nut_data[] = +{ + { NULL, "ups.mfr", ST_FLAG_STRING | ST_FLAG_RW, 32, "APC", DU_FLAG_INIT, NULL }, + { "MODEL", "ups.model", ST_FLAG_STRING | ST_FLAG_RW, 32, "Unknown UPS", DU_FLAG_INIT, NULL }, + { "STATUS", "ups.status", ST_FLAG_STRING | ST_FLAG_RW, 32, "OFF", DU_FLAG_INIT|DU_FLAG_STATUS, NULL }, + { "SERIALNO", "ups.serial", ST_FLAG_STRING | ST_FLAG_RW, 32, NULL, DU_FLAG_NONE, NULL }, + { "LOADPCT", "ups.load", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "DATE", "ups.time", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_TIME, NULL }, + { "DATE", "ups.date", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_DATE, NULL }, + { "MANDATE", "ups.mfr.date", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, + { "FIRMWARE", "ups.firmware", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_FW1, NULL }, + { "FIRMWARE", "ups.firmware.aux", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_FW2, NULL }, + { "ITEMP", "ups.temperature", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "UPSNAME", "ups.id", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, + { "DWAKE", "ups.delay.start", ST_FLAG_RW, 1, "%.0f", DU_FLAG_NONE, NULL }, + { "DSHUTD", "ups.delay.shutdown", ST_FLAG_RW, 1, "%.0f", DU_FLAG_NONE, NULL }, + { "STESTI", "ups.test.interval", ST_FLAG_RW, 1, "%.0f", DU_FLAG_NONE, NULL }, + { "SELFTEST", "ups.test.result", ST_FLAG_STRING | ST_FLAG_RW, 16, NULL, DU_FLAG_NONE, NULL }, + { "LINEV", "input.voltage", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "MAXLINEV", "input.voltage.maximum", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "MINLINEV", "input.voltage.minimum", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "NOMINV", "input.voltage.nominal", 0, 1, "%.0f", DU_FLAG_NONE, NULL }, + { "NOMOUTV", "output.voltage.nominal", 0, 1, "%.0f", DU_FLAG_NONE, NULL }, + { "LASTXFER", "input.transfer.reason", ST_FLAG_STRING | ST_FLAG_RW, 32, NULL, DU_FLAG_NONE, NULL }, + { "LOTRANS", "input.transfer.low", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "HITRANS", "input.transfer.high", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "SENSE", "input.sensitivity", ST_FLAG_STRING | ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, + { "LINEFREQ", "input.frequency", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "OUTPUTV", "output.voltage", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "LINEFREQ", "output.frequency", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "BCHARGE", "battery.charge", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_PRESERVE, NULL }, + { "MBATTCHG", "battery.charge.low", ST_FLAG_RW, 1, "%.0f", DU_FLAG_NONE, NULL }, + { "BATTDATE", "battery.date", ST_FLAG_STRING /* | ST_FLAG_RW */, 16, NULL, DU_FLAG_DATE, NULL }, + { "BATTV", "battery.voltage", 0, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "NOMBATTV", "battery.voltage.nominal", 0, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "TIMELEFT", "battery.runtime", ST_FLAG_RW, 60, "%1.1f", DU_FLAG_PRESERVE, NULL }, + { "MINTIMEL", "battery.runtime.low", ST_FLAG_RW, 60, "%.0f", DU_FLAG_NONE, NULL }, + { "RETPCT", "battery.charge.restart", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "NOMPOWER", "ups.realpower.nominal", 0, 1, "%1.1f", DU_FLAG_INIT, NULL }, + { "LOAD_W", "ups.realpower", 0, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "LOADAPNT", "power.percent", ST_FLAG_RW, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "OUTCURNT", "output.current", 0, 1, "%1.2f", DU_FLAG_NONE, NULL }, + { "LOAD_VA", "ups.power", 0, 1, "%1.1f", DU_FLAG_NONE, NULL }, + { "NOMAPNT", "ups.power.nominal", 0, 1, "%.0f", DU_FLAG_INIT, NULL }, + { NULL, NULL, 0, 0, NULL, DU_FLAG_NONE, NULL } +}; + +#endif /* NUT_APCUPSD_UPS_H_SEEN */ diff --git a/drivers/arduino-hid.c b/drivers/arduino-hid.c new file mode 100644 index 0000000..5b74c12 --- /dev/null +++ b/drivers/arduino-hid.c @@ -0,0 +1,148 @@ +/* arduino-hid.c - subdriver to monitor Arduino USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2012 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2013 Charles Lepple + * 2021 Alex Bratchik + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-usbhid-subdriver script. It must be customized. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" /* must be first */ + +#include "usbhid-ups.h" +#include "arduino-hid.h" +#include "main.h" /* for getval() */ +#include "usb-common.h" + +#define ARDUINO_HID_VERSION "Arduino HID 0.2" +/* FIXME: experimental flag to be put in upsdrv_info */ + +/* Arduino */ +#define ARDUINO_VENDORID 0x2341 +#define ARDUINO_VENDORID2 0x2A03 + +/* USB IDs device table */ +static usb_device_id_t arduino_usb_device_table[] = { + /* Arduino Leonardo, Leonardo ETH and Pro Micro*/ + { USB_DEVICE(ARDUINO_VENDORID, 0x0036), NULL }, + { USB_DEVICE(ARDUINO_VENDORID, 0x8036), NULL }, + { USB_DEVICE(ARDUINO_VENDORID2, 0x0036), NULL }, + { USB_DEVICE(ARDUINO_VENDORID2, 0x8036), NULL }, + { USB_DEVICE(ARDUINO_VENDORID2, 0x0040), NULL }, + { USB_DEVICE(ARDUINO_VENDORID2, 0x8040), NULL }, + + /* Terminating entry */ + { 0, 0, NULL } +}; + +static usb_communication_subdriver_t *usb = &usb_subdriver; + + +/* --------------------------------------------------------------- */ +/* Vendor-specific usage table */ +/* --------------------------------------------------------------- */ + +/* ARDUINO usage table */ +static usage_lkp_t arduino_usage_lkp[] = { + { NULL, 0 } +}; + +static usage_tables_t arduino_utab[] = { + arduino_usage_lkp, + hid_usage_lkp, + NULL, +}; + +/* --------------------------------------------------------------- */ +/* HID2NUT lookup table */ +/* --------------------------------------------------------------- */ + +static hid_info_t arduino_hid2nut[] = { + + /* USB HID PDC defaults */ + { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, + { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL}, + { "ups.timer.start", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, + { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, + { "ups.timer.reboot", 0, 0, "UPS.PowerSummary.DelayBeforeReboot", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, + + /* USB HID PDC defaults */ + { "load.off.delay", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, + { "load.on.delay", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, + { "shutdown.stop", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, + { "shutdown.reboot", 0, 0, "UPS.PowerSummary.DelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } +}; + +static const char *arduino_format_model(HIDDevice_t *hd) { + return hd->Product; +} + +static const char *arduino_format_mfr(HIDDevice_t *hd) { + return hd->Vendor ? hd->Vendor : "Arduino"; +} + +static const char *arduino_format_serial(HIDDevice_t *hd) { + return hd->Serial; +} + +/* this function allows the subdriver to "claim" a device: return 1 if + * the device is supported by this subdriver, else 0. */ +static int arduino_claim(HIDDevice_t *hd) +{ + int status = is_usb_device_supported(arduino_usb_device_table, hd); + + switch (status) { + case POSSIBLY_SUPPORTED: + /* by default, reject, unless the productid option is given */ + if (getval("productid")) { + usb->hid_ep_in=4; + usb->hid_ep_out=5; + usb->hid_rep_index = 2; + return 1; + } + possibly_supported("Arduino", hd); + return 0; + + case SUPPORTED: + usb->hid_ep_in=4; + usb->hid_ep_out=5; + usb->hid_rep_index = 2; + return 1; + + case NOT_SUPPORTED: + default: + return 0; + } +} + +subdriver_t arduino_subdriver = { + ARDUINO_HID_VERSION, + arduino_claim, + arduino_utab, + arduino_hid2nut, + arduino_format_model, + arduino_format_mfr, + arduino_format_serial, + fix_report_desc, +}; diff --git a/drivers/arduino-hid.h b/drivers/arduino-hid.h new file mode 100644 index 0000000..025c211 --- /dev/null +++ b/drivers/arduino-hid.h @@ -0,0 +1,31 @@ +/* arduino-hid.h - subdriver to monitor Arduino USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2009 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2021 Alex Bratchik + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef ARDUINO_HID_H +#define ARDUINO_HID_H + +#include "usbhid-ups.h" + +extern subdriver_t arduino_subdriver; + +#endif /* ARDUINO_HID_H */ diff --git a/drivers/asem.c b/drivers/asem.c new file mode 100644 index 0000000..7b0af43 --- /dev/null +++ b/drivers/asem.c @@ -0,0 +1,441 @@ +/* asem.c - driver for ASEM PB 1300 hardware, accessible through i2c. + + Copyright (C) 2014 Giuseppe Corbelli + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + ASEM SPA contributed with support and documentation. + Copan Italia SPA funded the development. + + There are 2 versions of the charger. Older one is based on Max1667, + newer one is a custom solution. Both are on address 0x09. + To be compatible with both versions just read bit 15 of address 0x13 + to have online/on battery status. + Battery monitor is a BQ2060 at address 0x0B. + + Beware that the SystemIO memory used by the i2c controller is reserved by ACPI. + On Linux, as of 3.5.x kernel only a native driver (i2c_i801) is available, + so you need to boot with acpi_enforce_resources=lax option. +*/ + +#include "main.h" + +#include +#include +#include + +/* Depends on i2c-dev.h, Linux only + * Linux I2C userland is a bit of a mess until distros refresh to + * the i2c-tools 4.x release that profides i2c/smbus.h for userspace + * instead of (re)using linux/i2c-dev.h, which conflicts with a + * kernel header of the same name. + * + * See: + * https://i2c.wiki.kernel.org/index.php/Plans_for_I2C_Tools_4 + */ +#if HAVE_LINUX_SMBUS_H +# include +#endif +#if HAVE_LINUX_I2C_DEV_H +# include /* for I2C_SLAVE */ +# if !HAVE_LINUX_SMBUS_H +# ifndef I2C_FUNC_I2C +# include +# endif +# endif +#endif + +#include + +#ifndef __STR__ +# define __STR__(x) #x +#endif +#ifndef __XSTR__ +# define __XSTR__(x) __STR__(x) +#endif + +#define DRIVER_NAME "ASEM" +#define DRIVER_VERSION "0.11" + +/* Valid on ASEM PB1300 UPS */ +#define BQ2060_ADDRESS 0x0B +#define CHARGER_ADDRESS 0x09 + +#define CMD_DEVICENAME 0x21 + +#define LOW_BATTERY_THRESHOLD 25 +#define HIGH_BATTERY_THRESHOLD 75 + +#define ACCESS_DEVICE(fd, address) \ + if (ioctl(fd, I2C_SLAVE, address) < 0) { \ + fatal_with_errno(EXIT_FAILURE, "Failed to acquire bus access and/or talk to i2c slave 0x%02X", address); \ + } + +static unsigned long lb_threshold = LOW_BATTERY_THRESHOLD; +static unsigned long hb_threshold = HIGH_BATTERY_THRESHOLD; + +static char *valid_devicename_data[] = { + "ASEM SPA", + NULL +}; + +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Giuseppe Corbelli ", + DRV_EXPERIMENTAL, + {NULL} +}; + +void upsdrv_initinfo(void) +{ + __s32 i2c_status; + __u8 buffer[10]; + unsigned short year, month, day; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT +# pragma GCC diagnostic ignored "-Wextra-semi-stmt" +#endif + /* Current definition of this macro ends with a brace; + * we keep the useless trailing ";" for readability */ + ACCESS_DEVICE(upsfd, BQ2060_ADDRESS); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT) +# pragma GCC diagnostic pop +#endif + + /* Set capacity mode in mA(h) */ + i2c_status = i2c_smbus_read_word_data(upsfd, 0x03); + if (i2c_status == -1) { + fatal_with_errno(EXIT_FAILURE, "Could not read BatteryMode word data"); + } + /* Clear 15th bit */ + i2c_status = i2c_smbus_write_word_data(upsfd, 0x03, i2c_status & ~0x8000); + if (i2c_status == -1) { + fatal_with_errno(EXIT_FAILURE, "Could not set BatteryMode word data"); + } + + /* Device name */ + memset(buffer, 0, 10); + i2c_status = i2c_smbus_read_block_data(upsfd, 0x21, buffer); + if (i2c_status == -1) { + fatal_with_errno(EXIT_FAILURE, "Could not read DeviceName block data"); + } + upsdebugx(1, "UPS model %s", (char *) buffer); + dstate_setinfo("ups.model", "%s", (char *) buffer); + + /* Manufacturing date */ + i2c_status = i2c_smbus_read_word_data(upsfd, 0x1B); + if (i2c_status == -1) { + fatal_with_errno(EXIT_FAILURE, "Could not read ManufactureDate word data"); + } + /* (Year - 1980) * 512 */ + year = (i2c_status >> 9) & 0x000000FF; + /* Month * 32 */ + month = (i2c_status >> 4) & 0x0000001F; + day = i2c_status & 0x0000001F; + upsdebugx(1, "UPS manufacturing date %d-%02d-%02d (%d)", year + 1980, month, day, i2c_status); + dstate_setinfo("ups.mfr.date", "%d-%02d-%02d", year + 1980, month, day); + + /* Device chemistry */ + memset(buffer, 0, 10); + i2c_status = i2c_smbus_read_block_data(upsfd, 0x22, buffer); + if (i2c_status == -1) { + fatal_with_errno(EXIT_FAILURE, "Could not read DeviceChemistry block data"); + } + upsdebugx(1, "Battery chemistry %s", (char *) buffer); + dstate_setinfo("battery.type", "%s", (char *) buffer); + + /* Serial number */ + i2c_status = i2c_smbus_read_word_data(upsfd, 0x1C); + if (i2c_status == -1) { + fatal_with_errno(EXIT_FAILURE, "Could not read SerialNumber block data"); + } + upsdebugx(1, "Serial Number %d", i2c_status); + dstate_setinfo("ups.serial", "%d", i2c_status); +} + +void upsdrv_updateinfo(void) +{ + static char online; + static char discharging; + static char fully_charged; + static unsigned short charge_percentage; + static unsigned short voltage; + static unsigned short capacity; + static signed short current; + static __s32 i2c_status; + static __s32 temperature; + static __s32 runtime_to_empty; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT +# pragma GCC diagnostic ignored "-Wextra-semi-stmt" +#endif + /* Current definition of this macro ends with a brace; + * we keep the useless trailing ";" for readability */ + ACCESS_DEVICE(upsfd, CHARGER_ADDRESS); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT) +# pragma GCC diagnostic pop +#endif + /* Charger only supplies online/offline status */ + i2c_status = i2c_smbus_read_word_data(upsfd, 0x13); + if (i2c_status == -1) { + dstate_datastale(); + upslogx(LOG_ERR, "Could not read charger status word at address 0x13"); + return; + } + online = (i2c_status & 0x8000) != 0; + upsdebugx(3, "Charger status 0x%02X, online %d", i2c_status, online); + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT +# pragma GCC diagnostic ignored "-Wextra-semi-stmt" +#endif + /* Current definition of this macro ends with a brace; + * we keep the useless trailing ";" for readability */ + ACCESS_DEVICE(upsfd, BQ2060_ADDRESS); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT) +# pragma GCC diagnostic pop +#endif + i2c_status = i2c_smbus_read_word_data(upsfd, 0x16); + if (i2c_status == -1) { + dstate_datastale(); + upslogx(LOG_ERR, "Could not read bq2060 status word at address 0x16"); + return; + } + upsdebugx(3, "bq2060 status 0x04%X", i2c_status); + /* Busy, leave data as stale, try next time */ + if (i2c_status & 0x0001) { + dstate_datastale(); + upslogx(LOG_NOTICE, "bq2060 is busy"); + return; + } + /* Error, leave data as stale, try next time */ + if (i2c_status & 0x000F) { + dstate_datastale(); + upslogx(LOG_WARNING, "bq2060 returned error code 0x%02X", i2c_status & 0x000F); + return; + } + + discharging = (i2c_status & 0x0040); + fully_charged = (i2c_status & 0x0020); + + /* Charge percentage */ + i2c_status = i2c_smbus_read_word_data(upsfd, 0x0D); + if (i2c_status == -1) { + dstate_datastale(); + upslogx(LOG_ERR, "Could not read charge percentage from bq2060 at address 0x0D"); + return; + } + charge_percentage = i2c_status & 0xFFFF; + upsdebugx(3, "Charge percentage %03d", charge_percentage); + + /* Battery voltage in mV */ + i2c_status = i2c_smbus_read_word_data(upsfd, 0x09); + if (i2c_status == -1) { + dstate_datastale(); + upslogx(LOG_ERR, "Could not read voltage from bq2060 at address 0x09"); + return; + } + voltage = i2c_status & 0x0000FFFF; + upsdebugx(3, "Battery voltage %d mV", voltage); + + /* Temperature in °K */ + temperature = i2c_smbus_read_word_data(upsfd, 0x08); + if (temperature == -1) { + dstate_datastale(); + upslogx(LOG_ERR, "Could not read temperature from bq2060 at address 0x08"); + return; + } + upsdebugx(3, "Temperature %4.1f K", temperature / 10.0); + + /* Current load in mA, positive for charge, negative for discharge */ + i2c_status = i2c_smbus_read_word_data(upsfd, 0x0A); + if (i2c_status == -1) { + dstate_datastale(); + upslogx(LOG_ERR, "Could not read current from bq2060 at address 0x0A"); + return; + } + current = i2c_status & 0x0000FFFF; + upsdebugx(3, "Current %d mA", current); + + /* Current capacity */ + i2c_status = i2c_smbus_read_word_data(upsfd, 0x0F); + if (i2c_status == -1) { + dstate_datastale(); + upslogx(LOG_ERR, "Could not read RemainingCapacity word data"); + return; + } + capacity = i2c_status & 0x0000FFFF; + upsdebugx(3, "Current capacity %d mAh", capacity); + + /* Expected runtime capacity, averaged by gauge */ + runtime_to_empty = i2c_smbus_read_word_data(upsfd, 0x12); + if (runtime_to_empty == -1) { + dstate_datastale(); + upslogx(LOG_ERR, "Could not read AverageTimeToEmpty word data"); + return; + } + upsdebugx(3, "Expected run-time to empty %d m", runtime_to_empty); + + status_init(); + status_set(online ? "OL" : "OB"); + if (!discharging && !fully_charged) + status_set("CHRG"); + else if (discharging && current < 0) + status_set("DISCHRG"); + + if (charge_percentage >= hb_threshold) + status_set("HB"); + else if (charge_percentage <= lb_threshold) + status_set("LB"); + + /* In V */ + dstate_setinfo("battery.voltage", "%2.3f", voltage / 1000.0); + /* In mAh */ + dstate_setinfo("battery.current", "%2.3f", current / 1000.0); + dstate_setinfo("battery.charge", "%d", charge_percentage); + /* In mAh */ + dstate_setinfo("battery.capacity", "%2.3f", capacity / 1000.0); + /* In °C */ + dstate_setinfo("ups.temperature", "%4.1f", (temperature / 10.0) - 273.15); + /* In seconds */ + dstate_setinfo("battery.runtime", "%d", runtime_to_empty * 60); + status_commit(); + dstate_dataok(); +} + +void upsdrv_shutdown(void) + __attribute__((noreturn)); + +void upsdrv_shutdown(void) +{ + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ + + /* maybe try to detect the UPS here, but try a shutdown even if + it doesn't respond at first if possible */ + + /* replace with a proper shutdown function */ + fatalx(EXIT_FAILURE, "shutdown not supported"); + + /* you may have to check the line status since the commands + for toggling power are frequently different for OL vs. OB */ + + /* OL: this must power cycle the load if possible */ + + /* OB: the load must remain off until the power returns */ +} + +void upsdrv_help(void) +{ + /* Redundant */ + printf("\nASEM options\n"); + printf(" HIGH/low battery thresholds\n"); + printf(" lb = " __XSTR__(LOW_BATTERY_THRESHOLD) " (battery is low under this level)\n"); + printf(" hb = " __XSTR__(HIGH_BATTERY_THRESHOLD) " (battery is high above this level)\n"); +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "lb", "Low battery threshold, default " __XSTR__(LOW_BATTERY_THRESHOLD)); + addvar(VAR_VALUE, "hb", "High battery threshold, default " __XSTR__(HIGH_BATTERY_THRESHOLD)); +} + +void upsdrv_initups(void) +{ + __s32 i2c_status; + __u8 DeviceName_buffer[10]; + unsigned int i; + unsigned long x; + char *DeviceName; + char *option; + + upsfd = open(device_path, O_RDWR); + if (upsfd < 0) { + fatal_with_errno(EXIT_FAILURE, "Could not open device port '%s'", device_path); + } + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT +# pragma GCC diagnostic ignored "-Wextra-semi-stmt" +#endif + /* Current definition of this macro ends with a brace; + * we keep the useless trailing ";" for readability */ + ACCESS_DEVICE(upsfd, BQ2060_ADDRESS); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT) +# pragma GCC diagnostic pop +#endif + + /* Get ManufacturerName */ + memset(DeviceName_buffer, 0, 10); + i2c_status = i2c_smbus_read_block_data(upsfd, 0x20, DeviceName_buffer); + if (i2c_status == -1) { + fatal_with_errno(EXIT_FAILURE, "Could not read DeviceName block data"); + } + i = 0; + while ( (DeviceName = valid_devicename_data[i++]) ) { + if (0 == memcmp(DeviceName, DeviceName_buffer, i2c_status)) + break; + } + if (!DeviceName) { + fatal_with_errno(EXIT_FAILURE, "Device '%s' unknown", (char *) DeviceName_buffer); + } + upsdebugx(1, "Found device '%s' on port '%s'", (char *) DeviceName, device_path); + dstate_setinfo("ups.mfr", "%s", (char *) DeviceName); + + option = getval("lb"); + if (option) { + x = strtoul(option, NULL, 0); + if ((x == 0) && (errno != 0)) { + upslogx(LOG_WARNING, "Invalid value specified for low battery threshold: '%s'", option); + } else { + lb_threshold = x; + } + } + option = getval("hb"); + if (option) { + x = strtoul(option, NULL, 0); + if ((x == 0) && (errno != 0)) { + upslogx(LOG_WARNING, "Invalid value specified for high battery threshold: '%s'", option); + } else if ((x < 1) || (x > 100)) { + upslogx(LOG_WARNING, "Invalid value specified for high battery threshold: '%s' (must be 1 < hb <= 100)", option); + } else { + hb_threshold = x; + } + } + /* Invalid values specified */ + if (lb_threshold > hb_threshold) { + upslogx(LOG_WARNING, "lb > hb specified in options. Returning to defaults."); + lb_threshold = LOW_BATTERY_THRESHOLD; + hb_threshold = HIGH_BATTERY_THRESHOLD; + } + + upslogx(LOG_NOTICE, "High battery threshold is %lu, low battery threshold is %lu", lb_threshold, hb_threshold); +} + +void upsdrv_cleanup(void) +{ + close(upsfd); +} diff --git a/drivers/baytech-mib.c b/drivers/baytech-mib.c index d17c0a5..43098cd 100644 --- a/drivers/baytech-mib.c +++ b/drivers/baytech-mib.c @@ -2,7 +2,7 @@ * * Copyright (C) 2009 * Opengear - * Arnaud Quette + * Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,69 +23,76 @@ #include "baytech-mib.h" -#define BAYTECH_MIB_VERSION "4031" +/* NOTE: last badly versioned release was "4032" but should be "X.Y[Z]"! */ +#define BAYTECH_MIB_VERSION "0.4034" /* Baytech MIB */ #define BAYTECH_OID_MIB ".1.3.6.1.4.1.4779" #define BAYTECH_OID_MODEL_NAME ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1" -static info_lkp_t outlet_status_info[] = { - { -1, "error" }, - { 0, "off" }, - { 1, "on" }, - { 2, "cycling" }, /* transitional status */ - { 0, NULL } +static info_lkp_t baytech_outlet_status_info[] = { + { -1, "error", NULL, NULL }, + { 0, "off", NULL, NULL }, + { 1, "on", NULL, NULL }, + { 2, "cycling", NULL, NULL }, /* transitional status */ + { 0, NULL, NULL, NULL } }; /* Snmp2NUT lookup table for BayTech MIBs */ static snmp_info_t baytech_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device page */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "BayTech", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1", - "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, + "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.2.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, /* UPS page */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Baytech", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1", - "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, + "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.3.0", - "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.2.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.1.1.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - { "ups.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "ups.temperature", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.10.2.1", NULL, 0, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.temperature", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.10.2.1", NULL, 0, NULL }, /* Outlet page */ { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.count", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.2.1.15.1", "0", 0, NULL }, - { "outlet.current", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.6.2.1", NULL, 0, NULL, NULL }, - { "outlet.voltage", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.8.2.1", NULL, 0, NULL, NULL }, + { "outlet.current", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.6.2.1", NULL, 0, NULL }, + { "outlet.voltage", 0, 0.1, ".1.3.6.1.4.1.4779.1.3.5.5.1.8.2.1", NULL, 0, NULL }, /* outlet template definition */ - { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", NULL, SU_OUTLET, &outlet_status_info[0], NULL }, - { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.3.1.4.1.%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.6.1.3.2.1.%i", "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_OUTLET | SU_FLAG_OK, NULL, NULL }, - { "outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, NULL, NULL }, + { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", NULL, SU_OUTLET, &baytech_outlet_status_info[0] }, + { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4779.1.3.5.3.1.4.1.%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.6.1.3.2.1.%i", "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_OUTLET | SU_FLAG_OK, NULL }, + { "outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, NULL }, /* instant commands. */ - { "outlet.%i.load.off", 0, 0, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, - { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, + { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.4779.1.3.5.3.1.3.1.%i", "1", SU_TYPE_CMD | SU_OUTLET, NULL }, /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } + { NULL, 0, 0, NULL, NULL, 0, NULL } }; -mib2nut_info_t baytech = { "baytech", BAYTECH_MIB_VERSION, "", BAYTECH_OID_MODEL_NAME, baytech_mib }; +mib2nut_info_t baytech = { "baytech", BAYTECH_MIB_VERSION, NULL, BAYTECH_OID_MODEL_NAME, baytech_mib, BAYTECH_OID_MIB, NULL }; diff --git a/drivers/bcmxcp.c b/drivers/bcmxcp.c index 1bc8b78..e0ea904 100644 --- a/drivers/bcmxcp.c +++ b/drivers/bcmxcp.c @@ -6,8 +6,10 @@ * emes -at- geomer.de * * All rights reserved.* - Copyright (C) 2004 Kjell Claesson - and Tore Ørpetveit + Copyright (C) + 2004 Kjell Claesson + 2004 Tore Ørpetveit + 2011 - 2015 Arnaud Quette Thanks to Tore Ørpetveit that sent me the manuals for bcm/xcp. @@ -20,7 +22,7 @@ ojw0000 2007Apr5 Oliver Wilcock - modified to control individual load segments (outlet.2.shutdown.return) on Powerware PW5125. Modified to support setvar for outlet.n.delay.start by Rich Wrenn (RFW) 9-3-11. - Modified to support setvar for outlet.n.delay.shutdown by Arnaud Quette, 9-12-11 + Modified to support setvar for outlet.n.delay.shutdown by Arnaud Quette, 9-12-11 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -38,96 +40,88 @@ This program is free software; you can redistribute it and/or modify TODO List: - Extend the parsing of the Standard ID Block, to read: + Extend the parsing of the Standard ID Block, to read: - Config Block Length: (High priority) - Give information if config block is - present, and how long it is, if it exist. - If config block exist, read the config block and setup - the possible config commands, and parse the - 'Length of the Extended Limits Configuration Block' for - extended configuration commands + Config Block Length: (High priority) + Give information if config block is + present, and how long it is, if it exist. + If config block exist, read the config block and parse the + 'Length of the Extended Limits Configuration Block' for + extended configuration commands - Statistic map Size: (Low priority) - May be used to se if there is a Statistic Map. - It holds data on the utility power quality for - the past month and since last reset. Number of - times on battery and how long. Up time and utility - frequency deviation. (Only larger ups'es) + Statistic map Size: (Low priority) + May be used to se if there is a Statistic Map. + It holds data on the utility power quality for + the past month and since last reset. Number of + times on battery and how long. Up time and utility + frequency deviation. (Only larger ups'es) - Size of Alarm History Log: (Low priority) - See if it have any alarm history block and enable - command to dump it. + Size of Alarm History Log: (Low priority) + See if it have any alarm history block and enable + command to dump it. - Size of Topology Block: (Medium priority) - Check if the topology block exist. Parse it for - some additional info. Type of ups input phases etc. + Maximum Supported Command Length: ( Med. to High priority) + Give info about the ups receive buffer size. - Maximum Supported Command Length: ( Med. to High priority) - Give info about the ups receive buffer size. + Size of Alarm Block: ( Med. to High priority) + Make a smarter handling of the Active alarm's if we know the length + of the Active Alarm Block. Don't need the long loop to parse the + alarm's. Maybe use another way to set up the alarm struct in the + 'init_alarm_map'. - Size of Command List Block: ( Med. to High priority) - Tell me if the command block exist. Can use this to ask - for command list and set up the commands accepted by the ups. + Parse 'Communication Capabilities Block' ( Low priority) + Get info of the connected ports ID, number of baud rates, + command and respnse length. - Size of Alarm Block: ( Med. to High priority) - Make a smarter handling of the Active alarm's if we know the length - of the Active Alarm Block. Don't need the long loop to parse the - alarm's. Maybe use another way to set up the alarm struct in the - 'init_alarm_map'. + Parse 'Communication Port List Block': ( Low priority) + This block gives info about the communication ports. Some ups'es + have multiple comport's, and use one port for eatch load segment. + In this block it is possible to get: + Number of ports. (In this List) + This Comport id (Which Comm Port is reporting this block.) + Comport id (Id for eatch port listed. The first comport ID=1) + Baudrate of the listed port. + Serial config. + Port usage: + What this Comm Port is being used for: + 0 = Unknown usage, No communication occurring. + 1 = Undefined / Unknown communication occurring + 2 = Waiting to communicate with a UPS + 3 = Communication established with a UPS + 4 = Waiting to communicate with software or adapter + 5 = Communication established software (e.g., LanSafe) + or adapter (e.g., ConnectUPS) + 6 = Communicating with a Display Device + 7 = Multi-drop Serial channel + 8 = Communicating with an Outlet Controller + Number of outlets. (Number of Outlets "assigned to" (controlled by) this Comm Port) + Outlet number. (Each assigned Outlet is listed (1-64)) - Parse 'Communication Capabilities Block' ( Low priority) - Get info of the connected ports ID, number of baud rates, - command and respnse length. - - Parse 'Communication Port List Block': ( Low priority) - This block gives info about the communication ports. Some ups'es - have multiple comport's, and use one port for eatch load segment. - In this block it is possible to get: - Number of ports. (In this List) - This Comport id (Which Comm Port is reporting this block.) - Comport id (Id for eatch port listed. The first comport ID=1) - Baudrate of the listed port. - Serial config. - Port usage: - What this Comm Port is being used for: - 0 = Unknown usage, No communication occurring. - 1 = Undefined / Unknown communication occurring - 2 = Waiting to communicate with a UPS - 3 = Communication established with a UPS - 4 = Waiting to communicate with software or adapter - 5 = Communication established software (e.g., LanSafe) - or adapter (e.g., ConnectUPS) - 6 = Communicating with a Display Device - 7 = Multi-drop Serial channel - 8 = Communicating with an Outlet Controller - Number of outlets. (Number of Outlets "assigned to" (controlled by) this Comm Port) - Outlet number. (Each assigned Outlet is listed (1-64)) - - 'Set outlet parameter command (0x97)' to alter the delay - settings or turn the outlet on or off with a delay (0 - 32767 seconds) + 'Set outlet parameter command (0x97)' to alter the delay + settings or turn the outlet on or off with a delay (0 - 32767 seconds) - Rewrite some parts of the driver, to minimise code duplication. (Like the instant commands) + Rewrite some parts of the driver, to minimise code duplication. (Like the instant commands) - Implement support for Password Authorization (XCP spec, §4.3.2) + Implement support for Password Authorization (XCP spec, §4.3.2) - Complete support for settable variables (upsh.setvar) + Complete support for settable variables (upsh.setvar) */ #include "main.h" -#include /* For ldexp() */ -#include /*for FLT_MAX */ +#include /* For ldexp() */ +#include /*for FLT_MAX */ + #include "nut_stdint.h" /* for uint8_t, uint16_t, uint32_t, ... */ #include "bcmxcp_io.h" #include "bcmxcp.h" -#define DRIVER_NAME "BCMXCP UPS driver" -#define DRIVER_VERSION "0.25" +#define DRIVER_NAME "BCMXCP UPS driver" +#define DRIVER_VERSION "0.32" -#define MAX_NUT_NAME_LENGTH 128 -#define NUT_OUTLET_POSITION 7 +#define MAX_NUT_NAME_LENGTH 128 +#define NUT_OUTLET_POSITION 7 /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -136,41 +130,151 @@ upsdrv_info_t upsdrv_info = { "Martin Schroeder \n" \ "Kjell Claesson \n" \ "Tore Ørpetveit \n" \ + "Arnaud Quette \n" \ "Wolfgang Ocker \n" \ - "Oliver Wilcock", + "Oliver Wilcock\n" \ + "Prachi Gandhi \n" \ + "Alf Høgemark \n" \ + "Gavrilov Igor", DRV_STABLE, { &comm_upsdrv_info, NULL } }; -static int get_word(const unsigned char*); -static long int get_long(const unsigned char*); +static uint16_t get_word(const unsigned char*); +static uint32_t get_long(const unsigned char*); static float get_float(const unsigned char *data); +static void init_command_map(void); static void init_meter_map(void); static void init_alarm_map(void); -static void init_command_map(void); +static bool_t init_command(int size); static void init_config(void); static void init_limit(void); +static void init_ext_vars(void); +static void init_topology(void); static void init_ups_meter_map(const unsigned char *map, unsigned char len); static void init_ups_alarm_map(const unsigned char *map, unsigned char len); +static bool_t set_alarm_support_in_alarm_map(const unsigned char *map, const unsigned int mapIndex, const unsigned int bitmask, const unsigned int alarmMapIndex, const unsigned int alarmBlockIndex); static void decode_meter_map_entry(const unsigned char *entry, const unsigned char format, char* value); -static int init_outlet(unsigned char len); +static unsigned char init_outlet(unsigned char len); +static void init_system_test_capabilities(void); static int instcmd(const char *cmdname, const char *extra); -static int setvar (const char *varname, const char *val); +static int setvar(const char *varname, const char *val); +static int decode_instcmd_exec(const ssize_t res, const unsigned char exec_status, const char *cmdname, const char *success_msg); +static int decode_setvar_exec(const ssize_t res, const unsigned char exec_status, const char *cmdname, const char *success_msg); +static float calculate_ups_load(const unsigned char *data); +static const char *nut_find_infoval(info_lkp_t *xcp2info, const double value, const bool_t debug_output_nonexisting); + +/* static const char *FreqTol[3] = {"+/-2%", "+/-5%", "+/-7"}; */ +static const char *ABMStatus[4] = { + "charging", + "discharging", + "floating", + "resting" + }; +static const char *OutletStatus[9] = { + "unknown", + "on/closed", + "off/open", + "on with pending", + "off with pending", + "unknown", + "unknown", + "failed and closed", + "failed and open" + }; + +/* Standard Authorization Block */ +static unsigned char AUTHOR[4] = {0xCF, 0x69, 0xE8, 0xD5}; +static int nphases = 0; +static uint16_t outlet_block_len = 0; +static const char *cpu_name[5] = { + "Cont:", + "Inve:", + "Rect:", + "Netw:", + "Disp:" + }; +static const char *horn_stat[3] = { + "disabled", + "enabled", + "muted" + }; + +/* Battery test results */ +static info_lkp_t batt_test_info[] = { + { 0, "No test initiated", NULL, NULL }, + { 1, "In progress", NULL, NULL }, + { 2, "Done and passed", NULL, NULL }, + { 3, "Aborted", NULL, NULL }, + { 4, "Done and error", NULL, NULL }, + { 5, "Test scheduled", NULL, NULL }, + /* Not sure about the meaning of the below ones! */ + { 6, NULL, NULL, NULL }, /* The string was present but it has now been removed */ + { 7, NULL, NULL, NULL }, /* The string was not installed at the last power up */ + { 0, NULL, NULL, NULL } +}; + +/* Topology map results */ +static info_lkp_t topology_info[] = { + { BCMXCP_TOPOLOGY_OFFLINE_SWITCHER_1P, "Off-line switcher, Single Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_LINEINT_UPS_1P, "Line-Interactive UPS, Single Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_LINEINT_UPS_2P, "Line-Interactive UPS, Two Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_LINEINT_UPS_3P, "Line-Interactive UPS, Three Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_1P, "Dual AC Input, On-Line UPS, Single Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_2P, "Dual AC Input, On-Line UPS, Two Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_3P, "Dual AC Input, On-Line UPS, Three Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_ONLINE_UPS_1P, "On-Line UPS, Single Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_ONLINE_UPS_2P, "On-Line UPS, Two Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_ONLINE_UPS_3P, "On-Line UPS, Three Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_1P, "Parallel Redundant On-Line UPS, Single Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_2P, "Parallel Redundant On-Line UPS, Two Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_3P, "Parallel Redundant On-Line UPS, Three Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_1P, "Parallel for Capacity On-Line UPS, Single Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_2P, "Parallel for Capacity On-Line UPS, Two Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_3P, "Parallel for Capacity On-Line UPS, Three Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_SYSTEM_BYPASS_MODULE_3P, "System Bypass Module, Three Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_HOT_TIE_CABINET_3P, "Hot-Tie Cabinet, Three Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_OUTLET_CONTROLLER_1P, "Outlet Controller, Single Phase", NULL, NULL }, + { BCMXCP_TOPOLOGY_DUAL_AC_STATIC_SWITCH_3P, "Dual AC Input Static Switch Module, 3 Phase", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* Command map results */ +static info_lkp_t command_map_info[] = { + { PW_INIT_BAT_TEST, "test.battery.start", NULL, NULL }, + { PW_LOAD_OFF_RESTART, "shutdown.return", NULL, NULL }, + { PW_UPS_OFF, "shutdown.stayoff", NULL, NULL }, + { PW_UPS_ON, "load.on", NULL, NULL }, + { PW_GO_TO_BYPASS, "bypass.start", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* System test capabilities results */ +static info_lkp_t system_test_info[] = { + { PW_SYS_TEST_GENERAL, "test.system.start", NULL, NULL }, +/* { PW_SYS_TEST_SCHEDULE_BATTERY_COMMISSION, "test.battery.start.delayed", NULL, NULL }, */ +/* { PW_SYS_TEST_ALTERNATE_AC_INPUT, "test.alternate_acinput.start", NULL, NULL }, */ + { PW_SYS_TEST_FLASH_LIGHTS, "test.panel.start", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* allocate storage for shared variables (extern in bcmxcp.h) */ +BCMXCP_COMMAND_MAP_ENTRY_t + bcmxcp_command_map[BCMXCP_COMMAND_MAP_MAX]; +BCMXCP_METER_MAP_ENTRY_t + bcmxcp_meter_map[BCMXCP_METER_MAP_MAX]; +BCMXCP_ALARM_MAP_ENTRY_t + bcmxcp_alarm_map[BCMXCP_ALARM_MAP_MAX]; +BCMXCP_STATUS_t + bcmxcp_status; -const char *FreqTol[3] = {"+/-2%", "+/-5%", "+/-7"}; -const char *ABMStatus[4] = {"Charging", "Discharging", "Floating", "Resting"}; -/* Standard Authorization Block */ -unsigned char AUTHOR[4] = {0xCF, 0x69, 0xE8, 0xD5}; -int nphases = 0; -int outlet_block_len = 0; -const char *cpu_name[5] = {"Cont:", "Inve:", "Rect:", "Netw:", "Disp:"}; /* get_word function from nut driver metasys.c */ -int get_word(const unsigned char *buffer) /* return an integer reading a word in the supplied buffer */ +uint16_t get_word(const unsigned char *buffer) /* return a short integer reading a word in the supplied buffer */ { unsigned char a, b; - int result; + uint16_t result; a = buffer[0]; b = buffer[1]; @@ -179,11 +283,11 @@ int get_word(const unsigned char *buffer) /* return an integer reading a word in return result; } -/* get_long funktion from nut driver metasys.c for meter readings*/ -long int get_long(const unsigned char *buffer) /* return a long integer reading 4 bytes in the supplied buffer.*/ +/* get_long function from nut driver metasys.c for meter readings*/ +uint32_t get_long(const unsigned char *buffer) /* return a long integer reading 4 bytes in the supplied buffer.*/ { unsigned char a, b, c, d; - long int result; + uint32_t result; a = buffer[0]; b = buffer[1]; @@ -279,64 +383,140 @@ unsigned char calc_checksum(const unsigned char *buf) int i; c = 0; - for(i = 0; i < 2 + buf[1]; i++) + for (i = 0; i < 2 + buf[1]; i++) c -= buf[i]; return c; } +void init_command_map() +{ + int i = 0; + + /* Clean entire map */ + memset(&bcmxcp_command_map, 0, sizeof(BCMXCP_COMMAND_MAP_ENTRY_t) * BCMXCP_COMMAND_MAP_MAX); + + /* Set all command descriptions */ + bcmxcp_command_map[PW_ID_BLOCK_REQ].command_desc = "PW_ID_BLOCK_REQ"; + bcmxcp_command_map[PW_EVENT_HISTORY_LOG_REQ].command_desc = "PW_EVENT_HISTORY_LOG_REQ"; + bcmxcp_command_map[PW_STATUS_REQ].command_desc = "PW_STATUS_REQ"; + bcmxcp_command_map[PW_METER_BLOCK_REQ].command_desc = "PW_METER_BLOCK_REQ"; + bcmxcp_command_map[PW_CUR_ALARM_REQ].command_desc = "PW_CUR_ALARM_REQ"; + bcmxcp_command_map[PW_CONFIG_BLOCK_REQ].command_desc = "PW_CONFIG_BLOCK_REQ"; + bcmxcp_command_map[PW_UTILITY_STATISTICS_BLOCK_REQ].command_desc = "PW_UTILITY_STATISTICS_BLOCK_REQ"; + bcmxcp_command_map[PW_WAVEFORM_BLOCK_REQ].command_desc = "PW_WAVEFORM_BLOCK_REQ"; + bcmxcp_command_map[PW_BATTERY_REQ].command_desc = "PW_BATTERY_REQ"; + bcmxcp_command_map[PW_LIMIT_BLOCK_REQ].command_desc = "PW_LIMIT_BLOCK_REQ"; + bcmxcp_command_map[PW_TEST_RESULT_REQ].command_desc = "PW_TEST_RESULT_REQ"; + bcmxcp_command_map[PW_COMMAND_LIST_REQ].command_desc = "PW_COMMAND_LIST_REQ"; + bcmxcp_command_map[PW_OUT_MON_BLOCK_REQ].command_desc = "PW_OUT_MON_BLOCK_REQ"; + bcmxcp_command_map[PW_COM_CAP_REQ].command_desc = "PW_COM_CAP_REQ"; + bcmxcp_command_map[PW_UPS_TOP_DATA_REQ].command_desc = "PW_UPS_TOP_DATA_REQ"; + bcmxcp_command_map[PW_COM_PORT_LIST_BLOCK_REQ].command_desc = "PW_COM_PORT_LIST_BLOCK_REQ"; + bcmxcp_command_map[PW_REQUEST_SCRATCHPAD_DATA_REQ].command_desc = "PW_REQUEST_SCRATCHPAD_DATA_REQ"; + bcmxcp_command_map[PW_GO_TO_BYPASS].command_desc = "PW_GO_TO_BYPASS"; + bcmxcp_command_map[PW_UPS_ON].command_desc = "PW_UPS_ON"; + bcmxcp_command_map[PW_LOAD_OFF_RESTART].command_desc = "PW_LOAD_OFF_RESTART"; + bcmxcp_command_map[PW_UPS_OFF].command_desc = "PW_UPS_OFF"; + bcmxcp_command_map[PW_DECREMENT_OUTPUT_VOLTAGE].command_desc = "PW_DECREMENT_OUTPUT_VOLTAGE"; + bcmxcp_command_map[PW_INCREMENT_OUTPUT_VOLTAGE].command_desc = "PW_INCREMENT_OUTPUT_VOLTAGE"; + bcmxcp_command_map[PW_SET_TIME_AND_DATE].command_desc = "PW_SET_TIME_AND_DATE"; + bcmxcp_command_map[PW_UPS_ON_TIME].command_desc = "PW_UPS_ON_TIME"; + bcmxcp_command_map[PW_UPS_ON_AT_TIME].command_desc = "PW_UPS_ON_AT_TIME"; + bcmxcp_command_map[PW_UPS_OFF_TIME].command_desc = "PW_UPS_OFF_TIME"; + bcmxcp_command_map[PW_UPS_OFF_AT_TIME].command_desc = "PW_UPS_OFF_AT_TIME"; + bcmxcp_command_map[PW_SET_CONF_COMMAND].command_desc = "PW_SET_CONF_COMMAND"; + bcmxcp_command_map[PW_SET_OUTLET_COMMAND].command_desc = "PW_SET_OUTLET_COMMAND"; + bcmxcp_command_map[PW_SET_COM_COMMAND].command_desc = "PW_SET_COM_COMMAND"; + bcmxcp_command_map[PW_SET_SCRATHPAD_SECTOR].command_desc = "PW_SET_SCRATHPAD_SECTOR"; + bcmxcp_command_map[PW_SET_POWER_STRATEGY].command_desc = "PW_SET_POWER_STRATEGY"; + bcmxcp_command_map[PW_SET_REQ_ONLY_MODE].command_desc = "PW_SET_REQ_ONLY_MODE"; + bcmxcp_command_map[PW_SET_UNREQUESTED_MODE].command_desc = "PW_SET_UNREQUESTED_MODE"; + bcmxcp_command_map[PW_INIT_BAT_TEST].command_desc = "PW_INIT_BAT_TEST"; + bcmxcp_command_map[PW_INIT_SYS_TEST].command_desc = "PW_INIT_SYS_TEST"; + bcmxcp_command_map[PW_SELECT_SUBMODULE].command_desc = "PW_SELECT_SUBMODULE"; + bcmxcp_command_map[PW_AUTHORIZATION_CODE].command_desc = "PW_AUTHORIZATION_CODE"; + + for (i = 0; i < BCMXCP_COMMAND_MAP_MAX; i++) { + bcmxcp_command_map[i].command_byte = 0; + } +} + void init_meter_map() { /* Clean entire map */ memset(&bcmxcp_meter_map, 0, sizeof(BCMXCP_METER_MAP_ENTRY_t) * BCMXCP_METER_MAP_MAX); /* Set all corresponding mappings NUT <-> BCM/XCP */ - bcmxcp_meter_map[0].nut_entity = "output.L1-L2.voltage"; - bcmxcp_meter_map[1].nut_entity = "output.L2-L3.voltage"; - bcmxcp_meter_map[2].nut_entity = "output.L3-L1.voltage"; - bcmxcp_meter_map[3].nut_entity = "input.L1-L2.voltage"; - bcmxcp_meter_map[4].nut_entity = "input.L2-L3.voltage"; - bcmxcp_meter_map[5].nut_entity = "input.L3-L1.voltage"; - bcmxcp_meter_map[19].nut_entity = "input.L2.current"; - bcmxcp_meter_map[20].nut_entity = "input.L3.current"; - bcmxcp_meter_map[23].nut_entity = "ups.power"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_AB].nut_entity = "output.L1-L2.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_BC].nut_entity = "output.L2-L3.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_CA].nut_entity = "output.L3-L1.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_AB].nut_entity = "input.L1-L2.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_BC].nut_entity = "input.L2-L3.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_CA].nut_entity = "input.L3-L1.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_B].nut_entity = "input.L2.current"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_C].nut_entity = "input.L3.current"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_WATTS].nut_entity = "input.realpower"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].nut_entity = "ups.power"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VA].nut_entity = "input.power"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_POWER_FACTOR].nut_entity = "output.powerfactor"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_POWER_FACTOR].nut_entity = "input.powerfactor"; -if (nphases == 1) { - bcmxcp_meter_map[18].nut_entity = "input.current"; - bcmxcp_meter_map[56].nut_entity = "input.voltage"; - bcmxcp_meter_map[65].nut_entity = "output.current"; - bcmxcp_meter_map[68].nut_entity = "output.current.nominal"; - bcmxcp_meter_map[78].nut_entity = "output.voltage"; - bcmxcp_meter_map[82].nut_entity = "ups.realpower"; -}else{ - bcmxcp_meter_map[18].nut_entity = "input.L1.current"; - bcmxcp_meter_map[56].nut_entity = "input.L1-N.voltage"; - bcmxcp_meter_map[65].nut_entity = "output.L1.current"; - bcmxcp_meter_map[68].nut_entity = "output.L1.current.nominal"; - bcmxcp_meter_map[78].nut_entity = "output.L1-N.voltage"; - bcmxcp_meter_map[82].nut_entity = "ups.L1-N.realpower"; -} - bcmxcp_meter_map[27].nut_entity = "output.frequency"; - bcmxcp_meter_map[28].nut_entity = "input.frequency"; - bcmxcp_meter_map[32].nut_entity = "battery.current"; - bcmxcp_meter_map[33].nut_entity = "battery.voltage"; - bcmxcp_meter_map[34].nut_entity = "battery.charge"; - bcmxcp_meter_map[35].nut_entity = "battery.runtime"; - bcmxcp_meter_map[43].nut_entity = "battery.charge.low"; - bcmxcp_meter_map[57].nut_entity = "input.L2-N.voltage"; - bcmxcp_meter_map[58].nut_entity = "input.L3-N.voltage"; - bcmxcp_meter_map[62].nut_entity = "ambient.temperature"; - bcmxcp_meter_map[63].nut_entity = "ups.temperature"; - bcmxcp_meter_map[66].nut_entity = "output.L2.current"; - bcmxcp_meter_map[67].nut_entity = "output.L3.current"; - bcmxcp_meter_map[69].nut_entity = "output.L2.current.nominal"; - bcmxcp_meter_map[70].nut_entity = "output.L3.current.nominal"; - bcmxcp_meter_map[77].nut_entity = "battery.temperature"; - bcmxcp_meter_map[79].nut_entity = "output.L2-N.voltage"; - bcmxcp_meter_map[80].nut_entity = "output.L3-N.voltage"; - bcmxcp_meter_map[83].nut_entity = "ups.L2-N.realpower"; - bcmxcp_meter_map[84].nut_entity = "ups.L3-N.realpower"; - bcmxcp_meter_map[85].nut_entity = "ups.realpower.nominal"; + if (nphases == 1) { + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_A].nut_entity = "input.current"; + bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_A].nut_entity = "ups.load"; /* TODO: Decide on corresponding three-phase variable mapping. */ + bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_A].nut_entity = "input.bypass.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_A].nut_entity = "input.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].nut_entity = "output.current"; + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].nut_entity = "output.current.nominal"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_A].nut_entity = "output.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS].nut_entity = "ups.realpower"; + } else { + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_A].nut_entity = "input.L1.current"; + bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_A].nut_entity = "output.L1.power.percent"; + bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_B].nut_entity = "output.L2.power.percent"; + bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_C].nut_entity = "output.L3.power.percent"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_PHASE_A].nut_entity = "output.L1.power"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_PHASE_B].nut_entity = "output.L2.power"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_PHASE_C].nut_entity = "output.L3.power"; + bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_A].nut_entity = "input.bypass.L1-N.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_B].nut_entity = "input.bypass.L2-N.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_C].nut_entity = "input.bypass.L3-N.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_A].nut_entity = "input.L1-N.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].nut_entity = "output.L1.current"; + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].nut_entity = "output.L1.current.nominal"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_A].nut_entity = "output.L1-N.voltage"; + } + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_FREQUENCY].nut_entity = "output.frequency"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_FREQUENCY].nut_entity = "input.frequency"; + bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_FREQUENCY].nut_entity = "input.bypass.frequency"; + bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_CURRENT].nut_entity = "battery.current"; + bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_VOLTAGE].nut_entity = "battery.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_BATTERY_LEFT].nut_entity = "battery.charge"; + bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_TIME_REMAINING].nut_entity = "battery.runtime"; + bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_DCUV_BAR_CHART].nut_entity = "battery.voltage.low"; + bcmxcp_meter_map[BCMXCP_METER_MAP_LOW_BATTERY_WARNING_V_BAR_CHART].nut_entity = "battery.charge.low"; + bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_DISCHARGING_CURRENT_BAR_CHART].nut_entity = "battery.current.total"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_B].nut_entity = "input.L2-N.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_C].nut_entity = "input.L3-N.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_AMBIENT_TEMPERATURE].nut_entity = "ambient.temperature"; + bcmxcp_meter_map[BCMXCP_METER_MAP_HEATSINK_TEMPERATURE].nut_entity = "ups.temperature"; + bcmxcp_meter_map[BCMXCP_METER_MAP_POWER_SUPPLY_TEMPERATURE].nut_entity = "ambient.1.temperature"; + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_B].nut_entity = "output.L2.current"; + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_C].nut_entity = "output.L3.current"; + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_B_BAR_CHART].nut_entity = "output.L2.current.nominal"; + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_C_BAR_CHART].nut_entity = "output.L3.current.nominal"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].nut_entity = "ups.power.nominal"; + bcmxcp_meter_map[BCMXCP_METER_MAP_DATE].nut_entity = "ups.date"; + bcmxcp_meter_map[BCMXCP_METER_MAP_TIME].nut_entity = "ups.time"; + bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_TEMPERATURE].nut_entity = "battery.temperature"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_B].nut_entity = "output.L2-N.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_C].nut_entity = "output.L3-N.voltage"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_A].nut_entity = "ups.L1-N.realpower"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_B].nut_entity = "ups.L2-N.realpower"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_C].nut_entity = "ups.L3-N.realpower"; + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_A_B_C_BAR_CHART].nut_entity = "ups.realpower.nominal"; + bcmxcp_meter_map[BCMXCP_METER_MAP_LINE_EVENT_COUNTER].nut_entity = "input.quality"; } void init_alarm_map() @@ -344,7 +524,7 @@ void init_alarm_map() /* Clean entire map */ memset(&bcmxcp_alarm_map, 0, sizeof(BCMXCP_ALARM_MAP_ENTRY_t) * BCMXCP_ALARM_MAP_MAX); - /* Set all alarm descriptions */ + /* Set all alarm descriptions */ bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_AC_OVER_VOLTAGE].alarm_desc = "INVERTER_AC_OVER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_AC_UNDER_VOLTAGE].alarm_desc = "INVERTER_AC_UNDER_VOLTAGE"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_OR_UNDER_FREQ].alarm_desc = "INVERTER_OVER_OR_UNDER_FREQ"; @@ -422,7 +602,7 @@ void init_alarm_map() bcmxcp_alarm_map[BCMXCP_ALARM_HEATSINK_TEMP_SENSOR_FAIL].alarm_desc = "HEATSINK_TEMP_SENSOR_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_CURRENT_OVER_125].alarm_desc = "RECTIFIER_CURRENT_OVER_125"; bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_FAULT_INTERRUPT_FAIL].alarm_desc = "RECTIFIER_FAULT_INTERRUPT_FAIL"; - bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_POWER_CAPASITOR_FAIL].alarm_desc = "RECTIFIER_POWER_CAPASITOR_FAIL"; + bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_POWER_CAPACITOR_FAIL].alarm_desc = "RECTIFIER_POWER_CAPACITOR_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_PROGRAM_STACK_ERROR].alarm_desc = "INVERTER_PROGRAM_STACK_ERROR"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_BOARD_SELFTEST_FAIL].alarm_desc = "INVERTER_BOARD_SELFTEST_FAIL"; bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_AD_SELFTEST_FAIL].alarm_desc = "INVERTER_AD_SELFTEST_FAIL"; @@ -583,25 +763,82 @@ void init_alarm_map() bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_ON_COMMAND].alarm_desc = "CHARGER_ON_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_OFF_COMMAND].alarm_desc = "CHARGER_OFF_COMMAND"; bcmxcp_alarm_map[BCMXCP_ALARM_UPS_NORMAL].alarm_desc = "UPS_NORMAL"; + bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_PHASE_ROTATION].alarm_desc = "INVERTER_PHASE_ROTATION"; + bcmxcp_alarm_map[BCMXCP_ALARM_UPS_OFF].alarm_desc = "UPS_OFF"; bcmxcp_alarm_map[BCMXCP_ALARM_EXTERNAL_COMMUNICATION_FAILURE].alarm_desc = "EXTERNAL_COMMUNICATION_FAILURE"; - + bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TEST_INPROGRESS].alarm_desc = "BATTERY_TEST_INPROGRESS"; + bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_TEST_INPROGRESS].alarm_desc = "SYSTEM_TEST_INPROGRESS"; + bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TEST_ABORTED].alarm_desc = "BATTERY_TEST_ABORTED"; } /* Get information on UPS commands */ -void init_command_map() +bool_t init_command(int size) { unsigned char answer[PW_ANSWER_MAX_SIZE]; - int res; + unsigned char commandByte; + const char* nutvalue; + ssize_t res; + int iIndex = 0, ncounter, NumComms = 0, i; - upsdebugx(1, "entering init_command_map()"); + upsdebugx(1, "entering init_command(%i)", size); res = command_read_sequence(PW_COMMAND_LIST_REQ, answer); if (res <= 0) + { upsdebugx(2, "No command list block."); + return FALSE; + } else + { upsdebugx(2, "Command list block supported."); - /* FIXME: to be completed */ + res = answer[iIndex]; + NumComms = (int)res; /* Number of commands implemented in this UPS */ + upsdebugx(3, "Number of commands implemented in ups %zd", res); + iIndex++; + res = answer[iIndex]; /* Entry length - bytes reported for each command */ + iIndex++; + upsdebugx(5, "bytes per command %zd", res); + + /* In case of debug - make explanation of values */ + upsdebugx(2, "Index\tCmd byte\tDescription"); + + /* Get command bytes if size of command block matches with size from standard ID block */ + if (NumComms + 2 == size) + { + for (ncounter = 0; ncounter < NumComms; ncounter++) + { + commandByte = answer[iIndex]; + if (commandByte < BCMXCP_COMMAND_MAP_MAX) { + upsdebugx(2, "%03d\t%02x\t%s", ncounter, commandByte, bcmxcp_command_map[commandByte].command_desc); + bcmxcp_command_map[commandByte].command_byte = commandByte; + } + else { + upsdebugx(2, "%03d\t%02x\t%s", ncounter, commandByte, "Unknown command, the commandByte is not mapped"); + } + + iIndex++; + } + + /* Map supported commands to instcmd */ + for (i = 0; i < BCMXCP_COMMAND_MAP_MAX; i++) { + if (bcmxcp_command_map[i].command_desc != NULL) { + if (bcmxcp_command_map[i].command_byte > 0) { + if ((nutvalue = nut_find_infoval(command_map_info, bcmxcp_command_map[i].command_byte, FALSE)) != NULL) { + dstate_addcmd(nutvalue); + upsdebugx(2, "Added support for instcmd %s", nutvalue); + } + } + } + } + + return TRUE; + } + else { + upsdebugx(1, "Invalid response received from Command List block"); + return FALSE; + } + } } void init_ups_meter_map(const unsigned char *map, unsigned char len) @@ -609,7 +846,7 @@ void init_ups_meter_map(const unsigned char *map, unsigned char len) unsigned int iIndex, iOffset = 0; /* In case of debug - make explanation of values */ - upsdebugx(2, "Index\tOffset\tFormat\tNUT\n"); + upsdebugx(2, "Index\tOffset\tFormat\tNUT"); /* Loop thru map */ for (iIndex = 0; iIndex < len && iIndex < BCMXCP_METER_MAP_MAX; iIndex++) @@ -627,15 +864,15 @@ void init_ups_meter_map(const unsigned char *map, unsigned char len) iOffset += 4; } } - upsdebugx(2, "\n"); } void decode_meter_map_entry(const unsigned char *entry, const unsigned char format, char* value) { - long lValue = 0; + uint32_t lValue = 0; char sFormat[32]; float fValue; + unsigned char dd, mm, yy, cc, hh, ss; /* Paranoid input sanity checks */ if (value == NULL) @@ -658,8 +895,21 @@ void decode_meter_map_entry(const unsigned char *entry, const unsigned char form else if (format <= 0x97) { /* Floating point */ fValue = get_float(entry); + /* Format is packed BCD */ snprintf(sFormat, 31, "%%%d.%df", ((format & 0xf0) >> 4), (format & 0x0f)); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif snprintf(value, 127, sFormat, fValue); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif } else if (format == 0xe2) { /* Seconds */ @@ -668,31 +918,32 @@ void decode_meter_map_entry(const unsigned char *entry, const unsigned char form } else if (format == 0xe0) { /* Date */ - unsigned char - dd = entry[0], - mm = entry[1], - yy = entry[2], + /* Format is packed BCD for each byte, and cc uses most signifcant bit to signal date format */ + dd = entry[0]; + mm = entry[1]; + yy = entry[2]; cc = entry[3]; /* Check format type */ if (cc & 0x80) { - /* Julian format */ - snprintf(value, 127, "%2d%2d:%3d", (cc & 0x7f), yy, (mm * 0x100)+dd); + /* Month:Day format */ + snprintf(value, 127, "%d%d/%d%d/%d%d%d%d", ((dd & 0xf0) >> 4), (dd & 0x0f), ((mm & 0xf0) >> 4), (mm & 0x0f), (((cc & 0x7f) & 0xf0) >> 4), ((cc & 0x7f) & 0x0f), ((yy & 0xf0) >> 4), (yy & 0x0f)); } else { - /* Month:Day format */ - snprintf(value, 127, "%2d/%2d/%2d%2d", dd, mm, (cc & 0x7f), yy); + /* Julian format */ + /* TODO test this, unsure if the day part is correct, i.e. how we use the two bytes mm and dd to calculate the number of julian days */ + snprintf(value, 127, "%d%d%d%d:%d%d%d", (((cc & 0x7f) & 0xf0) >> 4), ((cc & 0x7f) & 0x0f), ((yy & 0xf0) >> 4), (yy & 0x0f), (mm & 0x0f), ((dd & 0xf0) >> 4), (dd & 0x0f)); } } else if (format == 0xe1) { /* Time */ - unsigned char - cc = entry[0], - ss = entry[1], - mm = entry[2], + /* Format is packed BCD for each byte */ + cc = entry[0]; + ss = entry[1]; + mm = entry[2]; hh = entry[3]; - snprintf(value, 127, "%2d:%2d:%2d.%2d", hh, mm, ss, cc); + snprintf(value, 127, "%d%d:%d%d:%d%d.%d%d", ((hh & 0xf0) >> 4), (hh & 0x0f), ((mm & 0xf0) >> 4), (mm & 0x0f), ((ss & 0xf0) >> 4), (ss & 0x0f), ((cc & 0xf0) >> 4), (cc & 0x0f)); } else { /* Unknown format */ @@ -705,238 +956,224 @@ void decode_meter_map_entry(const unsigned char *entry, const unsigned char form void init_ups_alarm_map(const unsigned char *map, unsigned char len) { - unsigned int iIndex = 0, iOffset = 0; - int alarm = 0; + unsigned int iIndex = 0; + unsigned int alarm = 0; /* In case of debug - make explanation of values */ - upsdebugx(2, "Index\tAlarm\tSupported\n"); + upsdebugx(2, "Index\tAlarm\tSupported"); /* Loop thru map */ for (iIndex = 0; iIndex < len && iIndex < BCMXCP_ALARM_MAP_MAX / 8; iIndex++) { /* Bit 0 */ - iOffset = iIndex * 8; - if (map[iIndex] & 0x01) - { - /* Set alarm active */ - bcmxcp_alarm_map[iOffset].alarm_block_index = alarm; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tYes", alarm, bcmxcp_alarm_map[iOffset].alarm_desc); + if (set_alarm_support_in_alarm_map(map, iIndex, 0x01, iIndex * 8, alarm) == TRUE) alarm++; - } - else - { - /* Set alarm inactive */ - bcmxcp_alarm_map[iOffset].alarm_block_index = -1; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tNo", -1, bcmxcp_alarm_map[iOffset].alarm_desc); - } - /* Bit 1 */ - iOffset = iIndex*8 + 1; - if (map[iIndex] & 0x02) - { - /* Set alarm active */ - bcmxcp_alarm_map[iOffset].alarm_block_index = alarm; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tYes", alarm, bcmxcp_alarm_map[iOffset].alarm_desc); + if (set_alarm_support_in_alarm_map(map, iIndex, 0x02, iIndex * 8 + 1, alarm) == TRUE) alarm++; - } - else - { - /* Set alarm inactive */ - bcmxcp_alarm_map[iOffset].alarm_block_index = -1; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tNo", -1, bcmxcp_alarm_map[iOffset].alarm_desc); - } - /* Bit 2 */ - iOffset = iIndex*8 + 2; - if (map[iIndex] & 0x04) - { - /* Set alarm active */ - bcmxcp_alarm_map[iOffset].alarm_block_index = alarm; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tYes", alarm, bcmxcp_alarm_map[iOffset].alarm_desc); + if (set_alarm_support_in_alarm_map(map, iIndex, 0x04, iIndex * 8 + 2, alarm) == TRUE) alarm++; - } - else - { - /* Set alarm inactive */ - bcmxcp_alarm_map[iOffset].alarm_block_index = -1; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tNo", -1, bcmxcp_alarm_map[iOffset].alarm_desc); - } - /* Bit 3 */ - iOffset = iIndex*8 + 3; - if (map[iIndex] & 0x08) - { - /* Set alarm active */ - bcmxcp_alarm_map[iOffset].alarm_block_index = alarm; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tYes", alarm, bcmxcp_alarm_map[iOffset].alarm_desc); + if (set_alarm_support_in_alarm_map(map, iIndex, 0x08, iIndex * 8 + 3, alarm) == TRUE) alarm++; - } - else - { - /* Set alarm inactive */ - bcmxcp_alarm_map[iOffset].alarm_block_index = -1; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tNo", -1, bcmxcp_alarm_map[iOffset].alarm_desc); - } - /* Bit 4 */ - iOffset = iIndex*8 + 4; - if (map[iIndex] & 0x10) - { - /* Set alarm active */ - bcmxcp_alarm_map[iOffset].alarm_block_index = alarm; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tYes", alarm, bcmxcp_alarm_map[iOffset].alarm_desc); + if (set_alarm_support_in_alarm_map(map, iIndex, 0x10, iIndex * 8 + 4, alarm) == TRUE) alarm++; - } - else - { - /* Set alarm inactive */ - bcmxcp_alarm_map[iOffset].alarm_block_index = -1; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tNo", -1, bcmxcp_alarm_map[iOffset].alarm_desc); - } - /* Bit 5 */ - iOffset = iIndex*8 + 5; - if (map[iIndex] & 0x20) - { - /* Set alarm active */ - bcmxcp_alarm_map[iOffset].alarm_block_index = alarm; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tYes", alarm, bcmxcp_alarm_map[iOffset].alarm_desc); + if (set_alarm_support_in_alarm_map(map, iIndex, 0x20, iIndex * 8 + 5, alarm) == TRUE) alarm++; - } - else - { - /* Set alarm inactive */ - bcmxcp_alarm_map[iOffset].alarm_block_index = -1; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tNo", -1, bcmxcp_alarm_map[iOffset].alarm_desc); - } - /* Bit 6 */ - iOffset = iIndex*8 + 6; - if (map[iIndex] & 0x40) - { - /* Set alarm active */ - bcmxcp_alarm_map[iOffset].alarm_block_index = alarm; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tYes", alarm, bcmxcp_alarm_map[iOffset].alarm_desc); + if (set_alarm_support_in_alarm_map(map, iIndex, 0x40, iIndex * 8 + 6, alarm) == TRUE) alarm++; - } - else - { - /* Set alarm inactive */ - bcmxcp_alarm_map[iOffset].alarm_block_index = -1; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tNo", -1, bcmxcp_alarm_map[iOffset].alarm_desc); - } - /* Bit 7 */ - iOffset = iIndex*8 + 7; - if (map[iIndex] & 0x80) - { - /* Set alarm active */ - bcmxcp_alarm_map[iOffset].alarm_block_index = alarm; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tYes", alarm, bcmxcp_alarm_map[iOffset].alarm_desc); + if (set_alarm_support_in_alarm_map(map, iIndex, 0x80, iIndex * 8 + 7, alarm) == TRUE) alarm++; - } - else - { - /* Set alarm inactive */ - bcmxcp_alarm_map[iOffset].alarm_block_index = -1; - - /* Debug info */ - upsdebugx(2, "%04d\t%s\tNo", -1, bcmxcp_alarm_map[iOffset].alarm_desc); - } } upsdebugx(2, "\n"); } -int init_outlet(unsigned char len) +bool_t set_alarm_support_in_alarm_map( + const unsigned char *map, + const unsigned int mapIndex, + const unsigned int bitmask, + const unsigned int alarmMapIndex, + const unsigned int alarmBlockIndex +) { + /* Check what the alarm block tells about the support for the alarm */ + if (map[mapIndex] & bitmask) + { + /* Set alarm active */ + assert (alarmBlockIndex < INT_MAX); + bcmxcp_alarm_map[alarmMapIndex].alarm_block_index = (int)alarmBlockIndex; + } + else + { + /* Set alarm inactive */ + bcmxcp_alarm_map[alarmMapIndex].alarm_block_index = -1; + } + + /* Return if the alarm was supported or not */ + if (bcmxcp_alarm_map[alarmMapIndex].alarm_block_index >= 0) { + /* Debug info */ + upsdebugx(2, "%04d\t%s\tYes", bcmxcp_alarm_map[alarmMapIndex].alarm_block_index, bcmxcp_alarm_map[alarmMapIndex].alarm_desc); + return TRUE; + } + else { + /* Debug info */ + upsdebugx(3, "%04d\t%s\tNo", bcmxcp_alarm_map[alarmMapIndex].alarm_block_index, bcmxcp_alarm_map[alarmMapIndex].alarm_desc); + return FALSE; + } +} + +unsigned char init_outlet(unsigned char len) { + /* Note: (bug?) the argument "len" is not practically used in code below + * Callers know it as "outlet_block_len" in their routines and it is greater than 8 + */ unsigned char answer[PW_ANSWER_MAX_SIZE]; - int iIndex = 0, res, num; - int num_outlet, size_outlet; - int outlet_num, outlet_state; - short auto_dly_off, auto_dly_on; - char outlet_name[25]; + int iIndex = 0; + ssize_t res; + unsigned char num_outlet, size_outlet, num; + unsigned char outlet_num, outlet_state; + uint16_t auto_dly_off, auto_dly_on; + char outlet_name[64]; res = command_read_sequence(PW_OUT_MON_BLOCK_REQ, answer); if (res <= 0) fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); else - upsdebugx(1, "init_outlet(%i), res=%i", len, res); + upsdebugx(1, "init_outlet(%i), res=%zi", len, res); num_outlet = answer[iIndex++]; - upsdebugx(2, "Number of outlets: %d\n", num_outlet); + upsdebugx(2, "Number of outlets: %u", num_outlet); size_outlet = answer[iIndex++]; - upsdebugx(2, "Number of bytes: %d\n", size_outlet); + upsdebugx(2, "Number of bytes: %u", size_outlet); - for(num = 1 ; num <= num_outlet ; num++) { + for (num = 1 ; num <= num_outlet ; num++) { outlet_num = answer[iIndex++]; - upsdebugx(2, "Outlet number: %d\n", outlet_num); - snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.id", num); - dstate_setinfo(outlet_name, "%d", outlet_num); + upsdebugx(2, "Outlet number: %u", outlet_num); + snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%u.id", num); + dstate_setinfo(outlet_name, "%u", outlet_num); outlet_state = answer[iIndex++]; - upsdebugx(2, "Outlet state: %d\n", outlet_state); - snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.status", num); - dstate_setinfo(outlet_name, "%s", (outlet_state & 0x01 ? "On" : "Off")); + upsdebugx(2, "Outlet state: %u", outlet_state); + snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%u.status", num); + if (outlet_state>0 && outlet_state <9) + dstate_setinfo(outlet_name, "%s", OutletStatus[outlet_state]); auto_dly_off = get_word(answer+iIndex); iIndex += 2; - upsdebugx(2, "Auto delay off: %d\n", auto_dly_off); - snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.delay.shutdown", num); - dstate_setinfo(outlet_name, "%d", auto_dly_off); - dstate_setflags(outlet_name, ST_FLAG_RW | ST_FLAG_STRING); - dstate_setaux(outlet_name, 5); + upsdebugx(2, "Auto delay off: %u", auto_dly_off); + snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%u.delay.shutdown", num); + dstate_setinfo(outlet_name, "%u", auto_dly_off); + dstate_setflags(outlet_name, ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux(outlet_name, 5); auto_dly_on = get_word(answer+iIndex); iIndex += 2; - upsdebugx(2, "Auto delay on: %d\n", auto_dly_on); - snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.delay.start", num); - dstate_setinfo(outlet_name, "%d", auto_dly_on); - dstate_setflags(outlet_name, ST_FLAG_RW | ST_FLAG_STRING); - dstate_setaux(outlet_name, 5); + upsdebugx(2, "Auto delay on: %u", auto_dly_on); + snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%u.delay.start", num); + dstate_setinfo(outlet_name, "%u", auto_dly_on); + dstate_setflags(outlet_name, ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux(outlet_name, 5); } return num_outlet; - } +void init_ext_vars(void) +{ + unsigned char answer[PW_ANSWER_MAX_SIZE], cbuf[5]; + ssize_t length = 0; + int index = 0; + + send_write_command(AUTHOR, 4); + + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + cbuf[0] = PW_SET_CONF_COMMAND; + cbuf[1] = PW_CONF_REQ; + cbuf[2] = 0x0; + cbuf[3] = 0x0; + + length = command_write_sequence(cbuf, 4, answer); + if (length <= 0) + fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); + if (length < 4) /* UPS doesn't have configurable vars */ + return; + for (index=3; index < length; index++) { + switch(answer[index]) { + case PW_CONF_LOW_DEV_LIMIT: dstate_setinfo("input.transfer.boost.high", "%d", 0); + dstate_setflags("input.transfer.boost.high", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("input.transfer.boost.high", 3); + break; + + case PW_CONF_HIGH_DEV_LIMIT: dstate_setinfo("input.transfer.trim.low", "%d", 0); + dstate_setflags("input.transfer.trim.low", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("input.transfer.trim.low", 3); + break; + + case PW_CONF_LOW_BATT: dstate_setinfo("battery.runtime.low", "%d", 0); + dstate_setflags("battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("battery.runtime.low", 2); + break; + + case PW_CONF_BEEPER: dstate_addcmd("beeper.disable"); + dstate_addcmd("beeper.enable"); + dstate_addcmd("beeper.mute"); + break; + + case PW_CONF_RETURN_DELAY: dstate_setinfo("input.transfer.delay", "%d", 0); + dstate_setflags("input.transfer.delay", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("input.transfer.delay", 5); + break; + + case PW_CONF_RETURN_CAP: dstate_setinfo("battery.charge.restart", "%d", 0); + dstate_setflags("battery.charge.restart", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("battery.charge.restart", 3); + break; + + case PW_CONF_MAX_TEMP: dstate_setinfo("ambient.temperature.high", "%d", 0); + dstate_setflags("ambient.temperature.high", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("ambient.temperature.high", 3); + break; + + case PW_CONF_NOMINAL_OUT_VOLTAGE: dstate_setinfo("output.voltage.nominal", "%d", 0); + dstate_setflags("output.voltage.nominal", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("output.voltage.nominal", 3); + break; + + case PW_CONF_SLEEP_TH_LOAD: dstate_setinfo("battery.energysave.load", "%d", 0); + dstate_setflags("battery.energysave.load", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("battery.energysave.load", 3); + break; + + case PW_CONF_SLEEP_DELAY: dstate_setinfo("battery.energysave.delay", "%d", 0); + dstate_setflags("battery.energysave.delay", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("battery.energysave.delay", 3); + break; + + case PW_CONF_BATT_STRINGS: dstate_setinfo("battery.packs", "%d", 0); + dstate_setflags("battery.packs", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("battery.packs", 1); + break; + + } + } +} + + void init_config(void) { unsigned char answer[PW_ANSWER_MAX_SIZE]; - int voltage = 0, res, len; + uint16_t voltage = 0, frequency = 0, tmp = 0; + ssize_t res; char sValue[17]; + char sPartNumber[17]; - res = command_read_sequence(PW_CONFIG_BLOC_REQ, answer); + res = command_read_sequence(PW_CONFIG_BLOCK_REQ, answer); if (res <= 0) fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); @@ -945,32 +1182,48 @@ void init_config(void) /* Nominal output voltage of ups */ voltage = get_word((answer + BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_VOLTAGE)); - if (voltage != 0) - dstate_setinfo("output.voltage.nominal", "%d", voltage); + dstate_setinfo("output.voltage.nominal", "%u", voltage); + + /* Nominal Output Frequency */ + frequency = get_word((answer + BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_FREQ)); + if (frequency != 0) + dstate_setinfo("output.frequency.nominal", "%u", frequency); + + /*Number of EBM*/ + tmp = (uint16_t) *(answer + BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD3); + if (tmp != 0) + dstate_setinfo("battery.packs", "%u", tmp); + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic ignored "-Wformat-truncation" +#endif + /* NOTE: We intentionally limit the amount of characters picked from + * "answer" into "sValue" and "sPartNumber" buffers (16 byte + NUL). + */ /* UPS serial number */ - sValue[16] = 0; - - snprintf(sValue, 16, "%s", answer + BCMXCP_CONFIG_BLOCK_SERIAL_NUMBER); - len = 0; - - for (len = 0; len < 16; len++) { - if (sValue[len] == 0x20) { - sValue[len] = 0; - break; - } - } - - dstate_setinfo("ups.serial", "%s", sValue); + snprintf(sValue, sizeof(sValue), "%s", answer + BCMXCP_CONFIG_BLOCK_SERIAL_NUMBER); + if (sValue[0] != '\0') + dstate_setinfo("ups.serial", "%s", sValue); + /* UPS Part Number*/ + snprintf(sPartNumber, sizeof(sPartNumber), "%s", answer + BCMXCP_CONFIG_BLOCK_PART_NUMBER); + if (sPartNumber[0] != '\0') + dstate_setinfo("device.part", "%s", sPartNumber); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic pop +#endif } void init_limit(void) { unsigned char answer[PW_ANSWER_MAX_SIZE]; - int value, res; - const char *horn_stat[3] = {"disabled", "enabled", "muted"}; + uint16_t value; + ssize_t res; res = command_read_sequence(PW_LIMIT_BLOCK_REQ, answer); if (res <= 0) { @@ -979,82 +1232,159 @@ void init_limit(void) /* Nominal input voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_VOLTAGE)); - if (value != 0) { - dstate_setinfo("input.voltage.nominal", "%d", value); + dstate_setinfo("input.voltage.nominal", "%u", value); } /* Nominal input frequency */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_FREQ)); - if (value != 0) { - int fnom = value; - dstate_setinfo("input.frequency.nominal", "%d", value); + uint16_t fnom = value; + dstate_setinfo("input.frequency.nominal", "%u", value); /* Input frequency deviation */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_FREQ_DEV_LIMIT)); if (value != 0) { value /= 100; - dstate_setinfo("input.frequency.low", "%d", fnom - value); - dstate_setinfo("input.frequency.high", "%d", fnom + value); + dstate_setinfo("input.frequency.low", "%u", fnom - value); + dstate_setinfo("input.frequency.high", "%u", fnom + value); } } /* Bypass Voltage Low Deviation Limit / Transfer to Boost Voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_LOW_DEV_LIMIT)); - if (value != 0) { - dstate_setinfo("input.transfer.boost.high", "%d", value); + dstate_setinfo("input.transfer.boost.high", "%u", value); } /* Bypass Voltage High Deviation Limit / Transfer to Buck Voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_HIGE_DEV_LIMIT)); - if (value != 0) { - dstate_setinfo("input.transfer.trim.low", "%d", value); + dstate_setinfo("input.transfer.trim.low", "%u", value); } /* Low battery warning */ bcmxcp_status.lowbatt = answer[BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING] * 60; - /* Check if we should warn the user that her shutdown delay is to long? */ + /* Check if we should warn the user that her shutdown delay is too long? */ if (bcmxcp_status.shutdowndelay > bcmxcp_status.lowbatt) - upslogx(LOG_WARNING, "Shutdown delay longer than battery capacity when Low Battery warning is given. (max %d seconds)", bcmxcp_status.lowbatt); + upslogx(LOG_WARNING, + "Shutdown delay longer than battery capacity when Low Battery " + "warning is given. (max %d seconds)", bcmxcp_status.lowbatt); /* Horn Status: */ value = answer[BCMXCP_EXT_LIMITS_BLOCK_HORN_STATUS]; - - if (value >= 0 && value <= 2) { + if (value <= 2) { dstate_setinfo("ups.beeper.status", "%s", horn_stat[value]); } /* Minimum Supported Input Voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MIN_INPUT_VOLTAGE)); - if (value != 0) { - dstate_setinfo("input.transfer.low", "%d", value); + dstate_setinfo("input.transfer.low", "%u", value); } /* Maximum Supported Input Voltage */ value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MAX_INPUT_VOLTAGE)); - if (value != 0) { - dstate_setinfo("input.transfer.high", "%d", value); + dstate_setinfo("input.transfer.high", "%u", value); } /* Ambient Temperature Lower Alarm Limit */ value = answer[BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_LOW]; - if (value != 0) { - dstate_setinfo("ambient.temperature.low", "%d", value); + dstate_setinfo("ambient.temperature.low", "%u", value); } - /* AAmbient Temperature Upper Alarm Limit */ + /* Ambient Temperature Upper Alarm Limit */ value = answer[BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_HIGE]; - if (value != 0) { - dstate_setinfo("ambient.temperature.high", "%d", value); + dstate_setinfo("ambient.temperature.high", "%u", value); + } + + /*Sleep minimum load*/ + value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_TH_LOAD]; + if (value != 0) { + dstate_setinfo("battery.energysave.load", "%u", value); + } + + /* Sleep delay*/ + value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_DELAY]; + if (value != 0) { + dstate_setinfo("battery.energysave.delay", "%u", value); + } + + /* Low batt minutes warning*/ + value = answer[BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING]; + if (value != 0) { + dstate_setinfo("battery.runtime.low", "%u", value); + } + + /* Return to mains delay */ + value = get_word(answer + BCMXCP_EXT_LIMITS_BLOCK_RETURN_STAB_DELAY); + if (value != 0) { + dstate_setinfo("input.transfer.delay", "%u", value); + } + + /* Minimum return capacity*/ + value = answer[BCMXCP_EXT_LIMITS_BLOCK_BATT_CAPACITY_RETURN]; + if (value != 0) { + dstate_setinfo("battery.charge.restart", "%u", value); + } + +} + +void init_topology(void) +{ + unsigned char answer[PW_ANSWER_MAX_SIZE]; + const char* nutvalue; + uint16_t value; + ssize_t res; + + res = command_read_sequence(PW_UPS_TOP_DATA_REQ, answer); + if (res <= 0) + fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); + + value = get_word(answer); + + if ((nutvalue = nut_find_infoval(topology_info, value, TRUE)) != NULL) { + dstate_setinfo("ups.description", "%s", nutvalue); + } +} + +void init_system_test_capabilities(void) +{ + unsigned char answer[PW_ANSWER_MAX_SIZE], cbuf[5]; + const char* nutvalue; + ssize_t res; + int value, i; + + /* Query what system test capabilities are supported */ + send_write_command(AUTHOR, 4); + + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + cbuf[0] = PW_INIT_SYS_TEST; + cbuf[1] = PW_SYS_TEST_REPORT_CAPABILITIES; + res = command_write_sequence(cbuf, 2, answer); + if (res <= 0) { + upslogx(LOG_ERR, "Short read from UPS"); + return; + } + + if ((unsigned char)answer[0] != BCMXCP_RETURN_ACCEPTED) { + upsdebugx(2, "System test capabilities list not supported"); + return; + } + + /* Add instcmd for system test capabilities */ + for (i = 3; i < res; i++) { + value = answer[i]; + if ((nutvalue = nut_find_infoval(system_test_info, value, TRUE)) != NULL) { + upsdebugx(2, "Added support for instcmd %s", nutvalue); + dstate_addcmd(nutvalue); + } } } @@ -1062,20 +1392,34 @@ void upsdrv_initinfo(void) { unsigned char answer[PW_ANSWER_MAX_SIZE]; char *pTmp; - char outlet_name[27]; + char outlet_name[64]; char power_rating[10]; - int iRating = 0, iIndex = 0, res, len; - int ncpu = 0, buf; - int conf_block_len = 0, alarm_block_len = 0, cmd_list_len = 0; + ssize_t res; + unsigned int ncpu = 0; + size_t buf; + uint16_t iRating = 0, iIndex = 0, len; + uint16_t conf_block_len = 0, alarm_block_len = 0, cmd_list_len = 0, topology_block_len = 0; + bool_t got_cmd_list = FALSE; + + /* Init BCM/XCP command descriptions */ + init_command_map(); /* Init BCM/XCP alarm descriptions */ init_alarm_map(); /* Get vars from ups.conf */ - if (getval("shutdown_delay") != NULL) - bcmxcp_status.shutdowndelay = atoi(getval("shutdown_delay")); - else + if (getval("shutdown_delay") != NULL) { + int tmp = atoi(getval("shutdown_delay")); + if (tmp >= 0) { + bcmxcp_status.shutdowndelay = (unsigned int)tmp; + } else { + fatal_with_errno(EXIT_FAILURE, + "Invalid setting for shutdown_delay: %s", + getval("shutdown_delay")); + } + } else { bcmxcp_status.shutdowndelay = 120; + } /* Get information on UPS from UPS ID block */ res = command_read_sequence(PW_ID_BLOCK_REQ, answer); @@ -1085,6 +1429,7 @@ void upsdrv_initinfo(void) /* Get number of CPU's in ID block */ len = answer[iIndex++]; + /* No overflow checks, len value is byte-sized here */ buf = len * 11; pTmp = xmalloc(buf+1); @@ -1104,12 +1449,12 @@ void upsdrv_initinfo(void) dstate_setinfo("ups.firmware", "%s", pTmp); - free(pTmp); - /* Increment index to point at end of CPU bytes. */ iIndex += len * 2; } + free(pTmp); + /* Get rating in kVA, if present */ if ((iRating = answer[iIndex++]) > 0) iRating *= 1000; @@ -1119,7 +1464,7 @@ void upsdrv_initinfo(void) iRating = get_word(answer+iIndex) * 50; iIndex += 2; } - dstate_setinfo("ups.power.nominal", "%d", iRating); + dstate_setinfo("ups.power.nominal", "%u", iRating); /* Get information on Phases from UPS */ nphases = (answer[iIndex++]); @@ -1128,10 +1473,10 @@ void upsdrv_initinfo(void) /* Init BCM/XCP <-> NUT meter map */ init_meter_map(); - /* Skip UPS' phase angle, as NUT do not care */ + /* Skip UPS' phase angle, as NUT do not care */ iIndex += 1; - /* set manufacturer name */ + /* Set manufacturer name */ dstate_setinfo("ups.mfr", "Eaton"); /* Get length of UPS description */ @@ -1149,70 +1494,81 @@ void upsdrv_initinfo(void) if (strstr(pTmp, power_rating) == NULL) { snprintfcat(pTmp, len+10, " %s", power_rating); } - dstate_setinfo("ups.model", "%s", rtrim(pTmp, ' ')); + dstate_setinfo("ups.model", "%s", str_rtrim(pTmp, ' ')); free(pTmp); /* Get meter map info from ups, and init our map */ len = answer[iIndex++]; - upsdebugx(2, "Length of meter map: %d\n", len); - init_ups_meter_map(answer+iIndex, len); + upsdebugx(2, "Length of meter map: %u\n", len); + /* Here and below, no range check needed - just initialized from unsigned char array */ + init_ups_meter_map(answer+iIndex, (unsigned char)len); iIndex += len; /* Next is alarm map */ len = answer[iIndex++]; - upsdebugx(2, "Length of alarm map: %d\n", len); - init_ups_alarm_map(answer+iIndex, len); + upsdebugx(2, "Length of alarm map: %u\n", len); + init_ups_alarm_map(answer+iIndex, (unsigned char)len); iIndex += len; /* Then the Config_block_length */ conf_block_len = get_word(answer+iIndex); - upsdebugx(2, "Length of Config_block: %d\n", conf_block_len); + upsdebugx(2, "Length of Config_block: %u\n", conf_block_len); iIndex += 2; /* Next is statistics map */ len = answer[iIndex++]; - upsdebugx(2, "Length of statistics map: %d\n", len); - /* init_statistics_map(answer+iIndex, len); */ + upsdebugx(2, "Length of statistics map: %u\n", len); + /* init_statistics_map(answer+iIndex, (unsigned char)len); */ iIndex += len; /* Size of the alarm history log */ + len = get_word(answer+iIndex); + upsdebugx(2, "Length of alarm history log: %u\n", len); iIndex += 2; - /* Size of custom event log */ + /* Size of custom event log, always 0 according to spec */ iIndex += 2; - /* Size of topology block */ + topology_block_len = get_word(answer+iIndex); + upsdebugx(2, "Length of topology block: %u\n", topology_block_len); iIndex += 2; /* Maximum supported command length */ - iIndex += 1; + len = answer[iIndex++]; + upsdebugx(2, "Length of max supported command length: %u\n", len); /* Size of command list block */ - if (iIndex < res) + if (iIndex < (unsigned int)res) cmd_list_len = get_word(answer+iIndex); - upsdebugx(2, "Length of command list: %d\n", cmd_list_len); + upsdebugx(2, "Length of command list: %u\n", cmd_list_len); iIndex += 2; /* Size of outlet monitoring block */ - if (iIndex < res) + if (iIndex < (unsigned int)res) outlet_block_len = get_word(answer+iIndex); - upsdebugx(2, "Length of outlet_block: %d\n", outlet_block_len); + upsdebugx(2, "Length of outlet_block: %u\n", outlet_block_len); iIndex += 2; /* Size of the alarm block */ - if (iIndex < res) + if (iIndex < (unsigned int)res) alarm_block_len = get_word(answer+iIndex); - upsdebugx(2, "Length of alarm_block: %d\n", alarm_block_len); + upsdebugx(2, "Length of alarm_block: %u\n", alarm_block_len); /* End of UPS ID block request */ /* Due to a bug in PW5115 firmware, we need to use blocklength > 8. The protocol state that outlet block is only implemented if there is at least 2 outlet block. 5115 has only one outlet, but has outlet block! */ if (outlet_block_len > 8) { - len = init_outlet(outlet_block_len); + if (outlet_block_len > 255) + fatal_with_errno(EXIT_FAILURE, "outlet_block_len overflow: %u", outlet_block_len); + len = init_outlet((unsigned char)outlet_block_len /* arg ignored */); - for(res = 1 ; res <= len ; res++) { - snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.shutdown.return", res); + for (res = 1 ; (unsigned int)res <= (unsigned int)len ; res++) { + snprintf(outlet_name, sizeof(outlet_name) - 1, "outlet.%zd.shutdown.return", res); + dstate_addcmd(outlet_name); + snprintf(outlet_name, sizeof(outlet_name) - 1, "outlet.%zd.load.on", res); + dstate_addcmd(outlet_name); + snprintf(outlet_name, sizeof(outlet_name) - 1, "outlet.%zd.load.off", res); dstate_addcmd(outlet_name); } } @@ -1225,88 +1581,85 @@ void upsdrv_initinfo(void) /* Get information on UPS commands */ if (cmd_list_len) - init_command_map(); - - /* FIXME: leave up to init_command_map() to add instant commands? */ - dstate_addcmd("shutdown.return"); - dstate_addcmd("shutdown.stayoff"); - dstate_addcmd("test.battery.start"); + got_cmd_list = init_command(cmd_list_len); + /* Add default commands if we were not able to query UPS for support */ + if (got_cmd_list == FALSE) { + dstate_addcmd("shutdown.return"); + dstate_addcmd("shutdown.stayoff"); + dstate_addcmd("test.battery.start"); + } + /* Get information on UPS topology */ + if (topology_block_len) + init_topology(); + /* Get information on system test capabilities */ + if (bcmxcp_command_map[PW_INIT_SYS_TEST].command_byte > 0) { + init_system_test_capabilities(); + } + /* Get information about configurable external variables*/ + init_ext_vars(); upsh.instcmd = instcmd; - upsh.setvar = setvar; - - return; + upsh.setvar = setvar; } void upsdrv_updateinfo(void) { unsigned char answer[PW_ANSWER_MAX_SIZE]; + unsigned char status, topology; char sValue[128]; - int iIndex, res; - float output, max_output, fValue = 0.0f; + int iIndex; + ssize_t res; + uint16_t value; + bool_t has_ups_load = FALSE; + int batt_status = 0; + const char *nutvalue; + float calculated_load; /* Get info from UPS */ res = command_read_sequence(PW_METER_BLOCK_REQ, answer); - if (res <= 0){ + if (res <= 0) { upslogx(LOG_ERR, "Short read from UPS"); dstate_datastale(); return; } /* Loop thru meter map, get all data UPS is willing to offer */ - for (iIndex = 0; iIndex < BCMXCP_METER_MAP_MAX; iIndex++){ + for (iIndex = 0; iIndex < BCMXCP_METER_MAP_MAX; iIndex++) { if (bcmxcp_meter_map[iIndex].format != 0 && bcmxcp_meter_map[iIndex].nut_entity != NULL) { decode_meter_map_entry(answer + bcmxcp_meter_map[iIndex].meter_block_index, bcmxcp_meter_map[iIndex].format, sValue); /* Set result */ dstate_setinfo(bcmxcp_meter_map[iIndex].nut_entity, "%s", sValue); + + /* Check if we read ups.load */ + if (has_ups_load == FALSE && !strcasecmp(bcmxcp_meter_map[iIndex].nut_entity, "ups.load")) { + has_ups_load = TRUE; + } } } - /* Set max load, if possible (must be calculated) */ - if (bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].format != 0 && /* Output VA */ - bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].format != 0) /* Max output VA */ - { - decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].meter_block_index, - bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].format, sValue); - output = atof(sValue); - decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].meter_block_index, - bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].format, sValue); - max_output = atof(sValue); - - fValue = 0.0; - if (max_output > 0.0) - fValue = 100 * (output / max_output); - dstate_setinfo("ups.load", "%5.1f", fValue); - } - else if (bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURR_PHASE_A].format != 0 && /* Output A */ - bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURR_PHASE_A_BAR_CHART].format != 0) /* Max output A */ - { - decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURR_PHASE_A].meter_block_index, - bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURR_PHASE_A].format, sValue); - output = atof(sValue); - decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURR_PHASE_A_BAR_CHART].meter_block_index, - bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURR_PHASE_A_BAR_CHART].format, sValue); - max_output = atof(sValue); - - fValue = 0.0; - if (max_output > 0.0) - fValue = 100 * (output / max_output); - dstate_setinfo("ups.load", "%5.1f", fValue); + /* Calculate ups.load if UPS does not report it directly */ + if (has_ups_load == FALSE) { + calculated_load = calculate_ups_load(answer); + if (calculated_load >= 0.0f) { + dstate_setinfo("ups.load", "%5.1f", calculated_load); + } } /* Due to a bug in PW5115 firmware, we need to use blocklength > 8. The protocol state that outlet block is only implemented if there is at least 2 outlet block. 5115 has only one outlet, but has outlet block. */ if (outlet_block_len > 8) { - init_outlet(outlet_block_len); + if (outlet_block_len > 255) + fatal_with_errno(EXIT_FAILURE, "outlet_block_len overflow: %u", outlet_block_len); + init_outlet((unsigned char)outlet_block_len /* arg ignored */); } /* Get alarm info from UPS */ res = command_read_sequence(PW_CUR_ALARM_REQ, answer); - if (res <= 0){ + if (res <= 0) { upslogx(LOG_ERR, "Short read from UPS"); dstate_datastale(); return; @@ -1316,27 +1669,32 @@ void upsdrv_updateinfo(void) bcmxcp_status.alarm_on_battery = 0; bcmxcp_status.alarm_low_battery = 0; - /* Set alarms */ + /* Set alarms */ alarm_init(); /* Loop thru alarm map, get all alarms UPS is willing to offer */ - for (iIndex = 0; iIndex < BCMXCP_ALARM_MAP_MAX; iIndex++){ + for (iIndex = 0; iIndex < BCMXCP_ALARM_MAP_MAX; iIndex++) { if (bcmxcp_alarm_map[iIndex].alarm_block_index >= 0 && bcmxcp_alarm_map[iIndex].alarm_desc != NULL) { - if (answer[bcmxcp_alarm_map[iIndex].alarm_block_index] > 0) { + if (answer[bcmxcp_alarm_map[iIndex].alarm_block_index] > 0) { alarm_set(bcmxcp_alarm_map[iIndex].alarm_desc); if (iIndex == BCMXCP_ALARM_UPS_ON_BATTERY) { bcmxcp_status.alarm_on_battery = 1; } - - if (iIndex == BCMXCP_ALARM_BATTERY_LOW) { + else if (iIndex == BCMXCP_ALARM_BATTERY_LOW) { bcmxcp_status.alarm_low_battery = 1; } + else if (iIndex == BCMXCP_ALARM_BATTERY_TEST_FAILED) { + bcmxcp_status.alarm_replace_battery = 1; + } + else if (iIndex == BCMXCP_ALARM_BATTERY_NEEDS_SERVICE) { + bcmxcp_status.alarm_replace_battery = 1; + } } } } - /* Confirm alarms */ + /* Confirm alarms */ alarm_commit(); } @@ -1350,7 +1708,6 @@ void upsdrv_updateinfo(void) } else { - unsigned char status, topology; /* Get overall status */ memcpy(&status, answer, sizeof(status)); @@ -1363,31 +1720,31 @@ void upsdrv_updateinfo(void) status_init(); switch (status) { - case 0x50: /* On line, everything is fine */ + case BCMXCP_STATUS_ONLINE: /* On line, everything is fine */ status_set("OL"); break; - case 0xf0: /* Off line */ + case BCMXCP_STATUS_ONBATTERY: /* Off line */ if (bcmxcp_status.alarm_on_battery == 0) status_set("OB"); break; - case 0xe0: /* Overload */ + case BCMXCP_STATUS_OVERLOAD: /* Overload */ status_set("OL"); status_set("OVER"); break; - case 0x63: /* Trim */ + case BCMXCP_STATUS_TRIM: /* Trim */ status_set("OL"); status_set("TRIM"); break; - case 0x62: - case 0x61: /* Boost */ + case BCMXCP_STATUS_BOOST1: + case BCMXCP_STATUS_BOOST2: /* Boost */ status_set("OL"); status_set("BOOST"); break; - case 0x60: /* Bypass */ + case BCMXCP_STATUS_BYPASS: /* Bypass */ status_set("OL"); status_set("BYPASS"); break; - case 0x10: /* Mostly off */ + case BCMXCP_STATUS_OFF: /* Mostly off */ status_set("OFF"); break; default: /* Unknown, assume it is OK... */ @@ -1404,279 +1761,452 @@ void upsdrv_updateinfo(void) status_set("OB"); if (bcmxcp_status.alarm_low_battery) status_set("LB"); + if (bcmxcp_status.alarm_replace_battery) + status_set("RB"); status_commit(); } + /* Get battery info from UPS, if exist */ + res = command_read_sequence(PW_BATTERY_REQ, answer); + if (res <= 0) + { + upsdebugx(1, "Failed to read Battery Status from UPS"); + } + else + { + /* Only parse the status (first byte) + * Powerware 5115 RM output: + * 02 00 78 1d 42 00 e0 17 42 1e 00 00 00 00 00 00 00 00 00 01 03 + * Powerware 9130 output: + * 03 0a d7 25 42 0a d7 25 42 00 9a 19 6d 43 cd cc 4c 3e 01 00 01 03 + */ + upsdebug_hex(2, "Battery Status", answer, (size_t)res); + batt_status = answer[BCMXCP_BATTDATA_BLOCK_BATT_TEST_STATUS]; + + if ((nutvalue = nut_find_infoval(batt_test_info, batt_status, TRUE)) != NULL) { + dstate_setinfo("ups.test.result", "%s", nutvalue); + upsdebugx(2, "Battery Status = %s (%i)", nutvalue, batt_status); + } + else { + upsdebugx(1, "Failed to extract Battery Status from answer"); + } + + /*Extracting internal batteries ABM status*/ + /*Placed first in ABM statuses list. For examples above - on position BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS (18): + PW5115RM - 0 - no external strings, no status bytes, + so next byte (19) - number of ABM statuses, next (20) - first ABM Status for internal batteries. + + PW9130 - 1 - one external string, so one additional status byte (#19 - 00 - no test run), next(20) - number of ABM statuses, + next (21) - ABM Status for internal batteries. + */ + value = + *(answer + BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS + + *(answer + BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS) * 1 + 2); + upsdebugx(2, "ABM Status = %u ", value); + if (value < 5) + dstate_setinfo("battery.charger.status", "%s", ABMStatus[value-1]); + } + + + res = command_read_sequence(PW_LIMIT_BLOCK_REQ, answer); + if (res <= 0) { + upsdebugx(1, "Failed to read EXT LIMITs from UPS"); + } else + { + /* Nominal input voltage */ + value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_VOLTAGE)); + + if (value != 0) { + dstate_setinfo("input.voltage.nominal", "%u", value); + } + + /* Bypass Voltage Low Deviation Limit / Transfer to Boost Voltage */ + value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_LOW_DEV_LIMIT)); + + if (value != 0) { + dstate_setinfo("input.transfer.boost.high", "%u", value); + } + + /* Bypass Voltage High Deviation Limit / Transfer to Buck Voltage */ + value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_HIGE_DEV_LIMIT)); + + if (value != 0) { + dstate_setinfo("input.transfer.trim.low", "%u", value); + } + + /* Minimum Supported Input Voltage */ + value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MIN_INPUT_VOLTAGE)); + + if (value != 0) { + dstate_setinfo("input.transfer.low", "%u", value); + } + + /* Maximum Supported Input Voltage */ + value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MAX_INPUT_VOLTAGE)); + + if (value != 0) { + dstate_setinfo("input.transfer.high", "%u", value); + } + + /* Horn Status: */ + value = answer[BCMXCP_EXT_LIMITS_BLOCK_HORN_STATUS]; + + if (value <= 2) { + dstate_setinfo("ups.beeper.status", "%s", horn_stat[value]); + } + /* AAmbient Temperature Upper Alarm Limit */ + value = answer[BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_HIGE]; + + if (value != 0) { + dstate_setinfo("ambient.temperature.high", "%u", value); + } + + /*Sleep minimum load*/ + value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_TH_LOAD]; + if (value != 0) { + dstate_setinfo("battery.energysave.load", "%u", value); + } + + /* Sleep delay*/ + value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_DELAY]; + if (value != 0) { + dstate_setinfo("battery.energysave.delay", "%u", value); + } + + /* Low batt minutes warning*/ + value = answer[BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING]; + if (value != 0) { + dstate_setinfo("battery.runtime.low", "%u", value); + } + + /* Return to mains delay */ + value = get_word(answer + BCMXCP_EXT_LIMITS_BLOCK_RETURN_STAB_DELAY); + if (value != 0) { + dstate_setinfo("input.transfer.delay", "%u", value); + } + + /* Minimum return capacity*/ + value = answer[BCMXCP_EXT_LIMITS_BLOCK_BATT_CAPACITY_RETURN]; + if (value != 0) { + dstate_setinfo("battery.charge.restart", "%u", value); + } + } + + res = command_read_sequence(PW_CONFIG_BLOCK_REQ, answer); + if (res <= 0) { + upsdebugx(1, "Failed to read CONF BLOCK from UPS"); + } + else + { + /*Nominal output voltage*/ + value = get_word((answer + BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_VOLTAGE)); + + if (value != 0) + dstate_setinfo("output.voltage.nominal", "%u", value); + /*Number of EBM*/ + value = (uint16_t) *(answer + BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD3); + if (value != 0) + dstate_setinfo("battery.packs", "%u", value); + + } + + dstate_dataok(); } +float calculate_ups_load(const unsigned char *answer) +{ + char sValue[128]; + float output = 0, max_output = -FLT_MAX, fValue = -FLT_MAX; + + if (bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].format != 0 && /* Output VA */ + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].format != 0) /* Max output VA */ + { + decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].meter_block_index, + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].format, sValue); + output = atof(sValue); + decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].meter_block_index, + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].format, sValue); + max_output = atof(sValue); + } + else if (bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].format != 0 && /* Output A */ + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].format != 0) /* Max output A */ + { + decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].meter_block_index, + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].format, sValue); + output = atof(sValue); + decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].meter_block_index, + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].format, sValue); + max_output = atof(sValue); + } + if (max_output > 0.0) + fValue = 100 * (output / max_output); + + return fValue; +} + void upsdrv_shutdown(void) { - /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ - unsigned char answer[5], cbuf[3]; + upsdebugx(1, "upsdrv_shutdown..."); - int res, sec; - - /* Get vars from ups.conf */ - if (getval("shutdown_delay") != NULL) - bcmxcp_status.shutdowndelay = atoi(getval("shutdown_delay")); - else - bcmxcp_status.shutdowndelay = 120; - - /* maybe try to detect the UPS here, but try a shutdown even if - it doesn't respond at first if possible */ - send_write_command(AUTHOR, 4); - - sleep(1); /* Need to. Have to wait at least 0,25 sec max 16 sec */ - - cbuf[0] = PW_LOAD_OFF_RESTART; - cbuf[1] = (unsigned char)(bcmxcp_status.shutdowndelay & 0x00ff); /* "delay" sec delay for shutdown, */ - cbuf[2] = (unsigned char)(bcmxcp_status.shutdowndelay >> 8); /* hige byte sec. From ups.conf. */ - - res = command_write_sequence(cbuf, 3, answer); - if (res <= 0) { - upslogx(LOG_ERR, "Short read from UPS"); - dstate_datastale(); + /* Try to shutdown with delay */ + if (instcmd("shutdown.return", NULL) == STAT_INSTCMD_HANDLED) { + /* Shutdown successful */ return; } - sec = (256 * (unsigned char)answer[3]) + (unsigned char)answer[2]; - - /* NOTE: get the response, and return info code located as first data byte after 4 header bytes. - Is implemented in answers to command packet's. - 0x31 Accepted - 0x32 not implemented - 0x33 Busy - 0x34 Unrecognized - 0x35 Parameter out of range - 0x36 Parameter invalid - 0x37 Accepted with parameter adjusted - */ - switch ((unsigned char) answer[0]) { - - case 0x31: { - upsdrv_comm_good(); - upslogx(LOG_NOTICE,"Going down in %d sec", sec); - break; - } - case 0x33: { - fatalx(EXIT_FAILURE, "shutdown disabled by front panel"); - break; - } - case 0x36: { - fatalx(EXIT_FAILURE, "Invalid parameter"); - break; - } - default: { - fatalx(EXIT_FAILURE, "shutdown not supported"); - break; - } + /* If the above doesn't work, try shutdown.stayoff */ + if (instcmd("shutdown.stayoff", NULL) == STAT_INSTCMD_HANDLED) { + /* Shutdown successful */ + return; } + + fatalx(EXIT_FAILURE, "Shutdown failed!"); } static int instcmd(const char *cmdname, const char *extra) { unsigned char answer[128], cbuf[6]; + char success_msg[40]; + char namebuf[MAX_NUT_NAME_LENGTH]; char varname[32]; const char *varvalue = NULL; - int res, sec; - int sddelay = 0x03; /* outlet off in 3 seconds, by default */ + ssize_t res; + int sec, outlet_num; + int sddelay = 0x03; /* outlet off in 3 seconds, by default */ - upsdebugx(1, "entering instcmd(%s)", cmdname); + upsdebugx(1, "entering instcmd(%s)(%s)", cmdname, extra); - /* ojw0000 outlet power cycle for PW5125 and perhaps others */ - if (!strcasecmp(cmdname, "outlet.1.shutdown.return") - || !strcasecmp(cmdname, "outlet.2.shutdown.return") - || !strcasecmp(cmdname, "outlet.3.shutdown.return") - ) { - send_write_command(AUTHOR, 4); - - sleep(1); /* Need to. Have to wait at least 0,25 sec max 16 sec */ - - /* Get the shutdown delay, if any */ - snprintf(varname, sizeof(varname)-1, "outlet.%c.delay.shutdown", cmdname[7]); - if ((varvalue = dstate_getinfo(varname)) != NULL) { - sddelay = atoi(dstate_getinfo(varname)); - } - - cbuf[0] = PW_LOAD_OFF_RESTART; - cbuf[1] = sddelay & 0xff; - cbuf[2] = sddelay >> 8; /* high byte of the 2 byte time argument */ - cbuf[3] = ( '1' == cmdname[7] ? 0x01 : 0x02); /* which outlet load segment? Assumes '1' or '2' at position 8 of the command string. */ - - /* ojw00000 the following copied from command "shutdown.return" below 2007Apr5 */ - res = command_write_sequence(cbuf, 4, answer); - if (res <= 0) { - upslogx(LOG_ERR, "Short read from UPS"); - dstate_datastale(); - return -1; - } - - sec = (256 * (unsigned char)answer[3]) + (unsigned char)answer[2]; - - switch ((unsigned char) answer[0]) { - - case 0x31: { - upslogx(LOG_NOTICE,"Going down in %d sec", sec); - return STAT_INSTCMD_HANDLED; - break; - } - case 0x33: { - upslogx(LOG_NOTICE, "[%s] disbled by front panel", cmdname); - return STAT_INSTCMD_UNKNOWN; - break; - } - case 0x36: { - upslogx(LOG_NOTICE, "[%s] Invalid parameter", cmdname); - return STAT_INSTCMD_UNKNOWN; - break; - } - default: { - upslogx(LOG_NOTICE, "[%s] not supported", cmdname); - return STAT_INSTCMD_UNKNOWN; - break; - } - } - - } /* ojw0000 end outlet power cycle */ - - /* FIXME: call upsdrv_shutdown() or use the present one! */ if (!strcasecmp(cmdname, "shutdown.return")) { send_write_command(AUTHOR, 4); - sleep(1); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ cbuf[0] = PW_LOAD_OFF_RESTART; - cbuf[1] = (unsigned char)(bcmxcp_status.shutdowndelay & 0x00ff); /* "delay" sec delay for shutdown, */ - cbuf[2] = (unsigned char)(bcmxcp_status.shutdowndelay >> 8); /* high byte sec. From ups.conf. */ + cbuf[1] = (unsigned char)(bcmxcp_status.shutdowndelay & 0x00ff); /* "delay" sec delay for shutdown, */ + cbuf[2] = (unsigned char)(bcmxcp_status.shutdowndelay >> 8); /* high byte sec. From ups.conf. */ res = command_write_sequence(cbuf, 3, answer); - if (res <= 0) { - upslogx(LOG_ERR, "Short read from UPS"); - dstate_datastale(); - return -1; - } sec = (256 * (unsigned char)answer[3]) + (unsigned char)answer[2]; + snprintf(success_msg, sizeof(success_msg)-1, "Going down in %d sec", sec); - switch ((unsigned char) answer[0]) { - - case 0x31: { - upslogx(LOG_NOTICE,"Going down in %d sec", sec); - return STAT_INSTCMD_HANDLED; - break; - } - case 0x33: { - upslogx(LOG_NOTICE, "[%s] disabled by front panel", cmdname); - return STAT_INSTCMD_UNKNOWN; - break; - } - case 0x36: { - upslogx(LOG_NOTICE, "[%s] Invalid parameter", cmdname); - return STAT_INSTCMD_UNKNOWN; - break; - } - default: { - upslogx(LOG_NOTICE, "[%s] not supported", cmdname); - return STAT_INSTCMD_UNKNOWN; - break; - } - } - + return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, success_msg); } if (!strcasecmp(cmdname, "shutdown.stayoff")) { send_write_command(AUTHOR, 4); - sleep(1); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ res = command_read_sequence(PW_UPS_OFF, answer); - if (res <= 0) { - upslogx(LOG_ERR, "Short read from UPS"); - dstate_datastale(); - return -1; - } - - switch ((unsigned char) answer[0]) { - - case 0x31: { - upslogx(LOG_NOTICE,"[%s] Going down NOW", cmdname); - return STAT_INSTCMD_HANDLED; - break; - } - case 0x33: { - upslogx(LOG_NOTICE, "[%s] disabled by front panel", cmdname); - return STAT_INSTCMD_UNKNOWN; - break; - } - case 0x36: { - upslogx(LOG_NOTICE, "[%s] Invalid parameter", cmdname); - return STAT_INSTCMD_UNKNOWN; - break; - } - default: { - upslogx(LOG_NOTICE, "[%s] not supported (code %c)", - cmdname, (unsigned char) answer[0]); - return STAT_INSTCMD_UNKNOWN; - break; - } - } + return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Going down NOW"); } + if (!strcasecmp(cmdname, "load.on")) { + send_write_command(AUTHOR, 4); + + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + res = command_read_sequence(PW_UPS_ON, answer); + + return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Enabling"); + } + + if (!strcasecmp(cmdname, "bypass.start")) { + send_write_command(AUTHOR, 4); + + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + res = command_read_sequence(PW_GO_TO_BYPASS, answer); + + return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Bypass enabled"); + } + + /* Note: test result will be parsed from Battery status block, + * part of the update loop, and published into ups.test.result + */ if (!strcasecmp(cmdname, "test.battery.start")) { send_write_command(AUTHOR, 4); - sleep(1); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ cbuf[0] = PW_INIT_BAT_TEST; - cbuf[1] = 0x0A; /* 10 sec start delay for test.*/ - cbuf[2] = 0x1E; /* 30 sec test duration.*/ + cbuf[1] = 0x0A; /* 10 sec start delay for test.*/ + cbuf[2] = 0x1E; /* 30 sec test duration.*/ res = command_write_sequence(cbuf, 3, answer); - if (res <= 0) { - upslogx(LOG_ERR, "Short read from UPS"); - dstate_datastale(); - return -1; - } - switch ((unsigned char) answer[0]) { - - case 0x31: { - upslogx(LOG_NOTICE,"[%s] Testing now", cmdname); - return STAT_INSTCMD_HANDLED; - break; - } - case 0x33: { - upslogx(LOG_NOTICE, "[%s] disabled by front panel", cmdname); - return STAT_INSTCMD_UNKNOWN; - break; - } - case 0x36: { - upslogx(LOG_NOTICE, "[%s] Invalid parameter", cmdname); - return STAT_INSTCMD_UNKNOWN; - break; - } - default: { - upslogx(LOG_NOTICE, "[%s] not supported", cmdname); - return STAT_INSTCMD_UNKNOWN; - break; - } - } + return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Testing battery now"); /* Get test info from UPS ? Should we wait for 50 sec and get the answer from the test. Or return, as we may lose line power and need to do a shutdown.*/ + } + if (!strcasecmp(cmdname, "test.system.start")) { + send_write_command(AUTHOR, 4); + + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + cbuf[0] = PW_INIT_SYS_TEST; + cbuf[1] = PW_SYS_TEST_GENERAL; + res = command_write_sequence(cbuf, 2, answer); + + return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Testing system now"); + } + + if (!strcasecmp(cmdname, "test.panel.start")) { + send_write_command(AUTHOR, 4); + + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + cbuf[0] = PW_INIT_SYS_TEST; + cbuf[1] = PW_SYS_TEST_FLASH_LIGHTS; + cbuf[2] = 0x0A; /* Flash and beep 10 times */ + res = command_write_sequence(cbuf, 3, answer); + + return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Testing panel now"); + } + + if (!strcasecmp(cmdname, "beeper.disable") || !strcasecmp(cmdname, "beeper.enable") || !strcasecmp(cmdname, "beeper.mute")) { + send_write_command(AUTHOR, 4); + + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + cbuf[0] = PW_SET_CONF_COMMAND; + cbuf[1] = PW_CONF_BEEPER; + switch (cmdname[7]) { + + case 'd': + case 'D': { + cbuf[2] = 0x0; /*disable beeper*/ + break; + } + case 'e': + case 'E': { + cbuf[2] = 0x1; /*enable beeper*/ + break; + } + case 'm': + case 'M': { + cbuf[2] = 0x2; + break; /*mute beeper*/ + } + } + cbuf[3] = 0x0; /*padding*/ + + res = command_write_sequence(cbuf, 4, answer); + return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Beeper status changed"); + } + + strncpy(namebuf, cmdname, sizeof(namebuf)); + namebuf[NUT_OUTLET_POSITION] = 'n'; /* Assumes a maximum of 9 outlets */ + + if (!strcasecmp(namebuf, "outlet.n.shutdown.return")) { + send_write_command(AUTHOR, 4); + + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + /* Get the shutdown delay, if any */ + snprintf(varname, sizeof(varname)-1, "outlet.%c.delay.shutdown", cmdname[NUT_OUTLET_POSITION]); + if ((varvalue = dstate_getinfo(varname)) != NULL) { + sddelay = atoi(varvalue); + } + + /*if -1 then use global shutdown_delay from ups.conf*/ + if (sddelay == -1) sddelay = (int)bcmxcp_status.shutdowndelay; + + outlet_num = cmdname[NUT_OUTLET_POSITION] - '0'; + if (outlet_num < 1 || outlet_num > 9) + return STAT_INSTCMD_FAILED; + + cbuf[0] = PW_LOAD_OFF_RESTART; + cbuf[1] = sddelay & 0xff; + cbuf[2] = (unsigned char)(sddelay >> 8); /* high byte of the 2 byte time argument */ + cbuf[3] = (unsigned char)outlet_num; /* which outlet load segment? Assumes outlet number at position 8 of the command string. */ + + res = command_write_sequence(cbuf, 4, answer); + + sec = (256 * (unsigned char)answer[3]) + (unsigned char)answer[2]; + snprintf(success_msg, sizeof(success_msg)-1, "Going down in %d sec", sec); + + return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, success_msg); + } + + if (!strcasecmp(namebuf, "outlet.n.load.on") || !strcasecmp(namebuf, "outlet.n.load.off")) { + send_write_command(AUTHOR, 4); + + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + outlet_num = cmdname[NUT_OUTLET_POSITION] - '0'; + if (outlet_num < 1 || outlet_num > 9) + return STAT_INSTCMD_FAILED; + + + cbuf[0] = (cmdname[NUT_OUTLET_POSITION+8] == 'n') ? PW_UPS_ON : PW_UPS_OFF; /* Cmd oN or not*/ + cbuf[1] = (unsigned char)outlet_num; /* Outlet number */ + + res = command_write_sequence(cbuf, 2, answer); + snprintf(success_msg, sizeof(success_msg)-1, + "Outlet %d is %s", + outlet_num, + ((cmdname[NUT_OUTLET_POSITION+8] == 'n') ? "On" : "Off")); + + return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, success_msg); } upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); return STAT_INSTCMD_UNKNOWN; } +static int decode_instcmd_exec(const ssize_t res, const unsigned char exec_status, const char *cmdname, const char *success_msg) +{ + if (res <= 0) { + upslogx(LOG_ERR, "[%s] Short read from UPS", cmdname); + dstate_datastale(); + return STAT_INSTCMD_FAILED; + } + + /* Decode the status code from command execution */ + switch (exec_status) { + case BCMXCP_RETURN_ACCEPTED: { + upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg); + upsdrv_comm_good(); + return STAT_INSTCMD_HANDLED; + } + case BCMXCP_RETURN_ACCEPTED_PARAMETER_ADJUST: { + upslogx(LOG_NOTICE, "[%s] Parameter adjusted", cmdname); + upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg); + upsdrv_comm_good(); + return STAT_INSTCMD_HANDLED; + } + case BCMXCP_RETURN_BUSY: { + upslogx(LOG_NOTICE, "[%s] Busy or disbled by front panel", cmdname); + return STAT_INSTCMD_FAILED; + } + case BCMXCP_RETURN_UNRECOGNISED: { + upslogx(LOG_NOTICE, "[%s] Unrecognised command byte or corrupt checksum", cmdname); + return STAT_INSTCMD_FAILED; + } + case BCMXCP_RETURN_INVALID_PARAMETER: { + upslogx(LOG_NOTICE, "[%s] Invalid parameter", cmdname); + return STAT_INSTCMD_INVALID; + } + case BCMXCP_RETURN_PARAMETER_OUT_OF_RANGE: { + upslogx(LOG_NOTICE, "[%s] Parameter out of range", cmdname); + return STAT_INSTCMD_INVALID; + } + default: { + upslogx(LOG_NOTICE, "[%s] Not supported", cmdname); + return STAT_INSTCMD_INVALID; + } + } +} void upsdrv_help(void) { @@ -1693,92 +2223,362 @@ int setvar (const char *varname, const char *val) { unsigned char answer[128], cbuf[5]; char namebuf[MAX_NUT_NAME_LENGTH]; - int res, sec, outlet_num; + char success_msg[SMALLBUF]; + ssize_t res; + int sec, outlet_num, tmp; int onOff_setting = PW_AUTO_OFF_DELAY; upsdebugx(1, "entering setvar(%s, %s)", varname, val); + if (!strcasecmp(varname, "input.transfer.boost.high")) { + + send_write_command(AUTHOR, 4); + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + tmp=atoi(val); + if (tmp < 0 || tmp > 460) { + return STAT_SET_INVALID; + } + + cbuf[0]=PW_SET_CONF_COMMAND; + cbuf[1]=PW_CONF_LOW_DEV_LIMIT; + cbuf[2]=tmp&0xff; + cbuf[3]=(unsigned char)(tmp>>8); + + res = command_write_sequence(cbuf, 4, answer); + snprintf(success_msg, sizeof(success_msg)-1, + " BOOST threshold volage set to %d V", tmp); + + return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); + + } + + if (!strcasecmp(varname, "input.transfer.trim.low")) { + + send_write_command(AUTHOR, 4); + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + tmp=atoi(val); + if (tmp < 110 || tmp > 540) { + return STAT_SET_INVALID; + } + + cbuf[0]=PW_SET_CONF_COMMAND; + cbuf[1]=PW_CONF_HIGH_DEV_LIMIT; + cbuf[2]=tmp&0xff; + cbuf[3]=(unsigned char)(tmp>>8); + + res = command_write_sequence(cbuf, 4, answer); + snprintf(success_msg, sizeof(success_msg)-1, + " TRIM threshold volage set to %d V", tmp); + + return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); + + } + + if (!strcasecmp(varname, "battery.runtime.low")) { + + send_write_command(AUTHOR, 4); + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + tmp=atoi(val); + if (tmp < 0 || tmp > 30) { + return STAT_SET_INVALID; + } + + cbuf[0]=PW_SET_CONF_COMMAND; + cbuf[1]=PW_CONF_LOW_BATT; + cbuf[2]=tmp&0xff; + cbuf[3]=0x0; + + res = command_write_sequence(cbuf, 4, answer); + snprintf(success_msg, sizeof(success_msg)-1, + " Low battery warning time set to %d min", tmp); + + return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); + + } + + if (!strcasecmp(varname, "input.transfer.delay")) { + + send_write_command(AUTHOR, 4); + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + tmp=atoi(val); + if (tmp < 1 || tmp > 18000) { + return STAT_SET_INVALID; + } + + cbuf[0]=PW_SET_CONF_COMMAND; + cbuf[1]=PW_CONF_RETURN_DELAY; + cbuf[2]=tmp&0xff; + cbuf[3]=(unsigned char)(tmp>>8); + + res = command_write_sequence(cbuf, 4, answer); + snprintf(success_msg, sizeof(success_msg)-1, + " Mains return delay set to %d sec", tmp); + + return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); + + } + + if (!strcasecmp(varname, "battery.charge.restart")) { + + send_write_command(AUTHOR, 4); + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + tmp=atoi(val); + if (tmp < 0 || tmp > 100) { + return STAT_SET_INVALID; + } + + cbuf[0]=PW_SET_CONF_COMMAND; + cbuf[1]=PW_CONF_RETURN_CAP; + cbuf[2]=tmp&0xff; + cbuf[3]=0x0; + + res = command_write_sequence(cbuf, 4, answer); + snprintf(success_msg, sizeof(success_msg)-1, + " Mains return minimum battery capacity set to %d %%", tmp); + + return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); + + } + + + if (!strcasecmp(varname, "ambient.temperature.high")) { + + send_write_command(AUTHOR, 4); + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + tmp=atoi(val); + if (tmp < 0 || tmp > 100) { + return STAT_SET_INVALID; + } + + cbuf[0]=PW_SET_CONF_COMMAND; + cbuf[1]=PW_CONF_MAX_TEMP; + cbuf[2]=tmp&0xff; + cbuf[3]=0x0; + + res = command_write_sequence(cbuf, 4, answer); + snprintf(success_msg, sizeof(success_msg)-1, + " Maximum temperature set to %d C", tmp); + + return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); + + } + + if (!strcasecmp(varname, "output.voltage.nominal")) { + + send_write_command(AUTHOR, 4); + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + tmp=atoi(val); + if (tmp < 0 || tmp > 460) { + return STAT_SET_INVALID; + } + + cbuf[0]=PW_SET_CONF_COMMAND; + cbuf[1]=PW_CONF_NOMINAL_OUT_VOLTAGE; + cbuf[2]=tmp&0xff; + cbuf[3]=(unsigned char)(tmp>>8); + + res = command_write_sequence(cbuf, 4, answer); + snprintf(success_msg, sizeof(success_msg)-1, + " Nominal output voltage set to %d V", tmp); + + return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); + + } + + if (!strcasecmp(varname, "battery.energysave.load")) { + + send_write_command(AUTHOR, 4); + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + tmp=atoi(val); + if (tmp < 0 || tmp > 100) { + return STAT_SET_INVALID; + } + + cbuf[0]=PW_SET_CONF_COMMAND; + cbuf[1]=PW_CONF_SLEEP_TH_LOAD; + cbuf[2]=tmp&0xff; + cbuf[3]=0x0; + + res = command_write_sequence(cbuf, 4, answer); + snprintf(success_msg, sizeof(success_msg)-1, " Minimum load before sleep countdown set to %d %%", tmp); + + return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); + + } + + if (!strcasecmp(varname, "battery.energysave.delay")) { + + send_write_command(AUTHOR, 4); + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + tmp=atoi(val); + if (tmp < 0 || tmp > 255) { + return STAT_SET_INVALID; + } + + cbuf[0]=PW_SET_CONF_COMMAND; + cbuf[1]=PW_CONF_SLEEP_DELAY; + cbuf[2]=tmp&0xff; + cbuf[3]=0x0; + + res = command_write_sequence(cbuf, 4, answer); + snprintf(success_msg, sizeof(success_msg)-1, + " Delay before sleep shutdown set to %d min", tmp); + + return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); + + + } + + if (!strcasecmp(varname, "battery.packs")) { + + send_write_command(AUTHOR, 4); + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ + + tmp=atoi(val); + if (tmp < 0 || tmp > 5) { + return STAT_SET_INVALID; + } + + cbuf[0]=PW_SET_CONF_COMMAND; + cbuf[1]=PW_CONF_BATT_STRINGS; + cbuf[2]=tmp&0xff; + cbuf[3]=0x0; + + res = command_write_sequence(cbuf, 4, answer); + snprintf(success_msg, sizeof(success_msg)-1, "EBM Count set to %d ", tmp); + + return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); + } + strncpy(namebuf, varname, sizeof(namebuf)); namebuf[NUT_OUTLET_POSITION] = 'n'; /* Assumes a maximum of 9 outlets */ - if ( (strcasecmp(namebuf, "outlet.n.delay.start")) && - (strcasecmp(namebuf, "outlet.n.delay.shutdown")) ) { - return STAT_SET_UNKNOWN; - } + if ( (!strcasecmp(namebuf, "outlet.n.delay.start")) || + (!strcasecmp(namebuf, "outlet.n.delay.shutdown"))) { - if (outlet_block_len <= 8) { - return STAT_SET_INVALID; - } - if (!strcasecmp(namebuf, "outlet.n.delay.start")) { - onOff_setting = PW_AUTO_ON_DELAY; - } + if (outlet_block_len <= 8) { + return STAT_SET_INVALID; + } - send_write_command(AUTHOR, 4); - /* Need to. Have to wait at least 0.25 sec max 16 sec */ - sleep (1); + if (!strcasecmp(namebuf, "outlet.n.delay.start")) { + onOff_setting = PW_AUTO_ON_DELAY; + } - outlet_num = varname[NUT_OUTLET_POSITION] - '0'; - if (outlet_num < 1 || outlet_num > 9) { - return STAT_SET_INVALID; - } + send_write_command(AUTHOR, 4); + sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */ - sec = atoi(val); - /* Check value: - * 0-32767 are valid values - * -1 means no Automatic off or restart - * for Auto Off Delay: - * 0-30 are valid but ill-advised */ - if (sec < -1 || sec > 0x7FFF) { - return STAT_SET_INVALID; - } + outlet_num = varname[NUT_OUTLET_POSITION] - '0'; + if (outlet_num < 1 || outlet_num > 9) { + return STAT_SET_INVALID; + } - cbuf[0] = PW_SET_OUTLET_COMMAND; /* Cmd */ - cbuf[1] = onOff_setting; /* Set Auto Off (1) or On (2) Delay */ - cbuf[2] = outlet_num; /* Outlet number */ - cbuf[3] = sec&0xff; /* Delay in seconds LSB */ - cbuf[4] = sec>>8; /* Delay in seconds MSB */ + sec = atoi(val); + /* Check value: + * 0-32767 are valid values + * -1 means no Automatic off or restart + * for Auto Off Delay: + * 0-30 are valid but ill-advised */ + if (sec < -1 || sec > 0x7FFF) { + return STAT_SET_INVALID; + } - res = command_write_sequence(cbuf, 5, answer); - if (res <= 0) { - upslogx(LOG_ERR, "Short read from UPS"); - dstate_datastale(); - return -1; - } + cbuf[0] = PW_SET_OUTLET_COMMAND; /* Cmd */ + cbuf[1] = (unsigned char)onOff_setting; /* Set Auto Off (1) or On (2) Delay */ + cbuf[2] = (unsigned char)outlet_num; /* Outlet number */ + cbuf[3] = sec&0xff; /* Delay in seconds LSB */ + cbuf[4] = (unsigned char)(sec>>8); /* Delay in seconds MSB */ - switch ((unsigned char) answer[0]) { + res = command_write_sequence(cbuf, 5, answer); + snprintf(success_msg, sizeof(success_msg)-1, + "Outlet %d %s delay set to %d sec", + outlet_num, + (onOff_setting == PW_AUTO_ON_DELAY) ? "start" : "shutdown", + sec); + + return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg); - case 0x31: { - upslogx(LOG_NOTICE,"Outlet %d %s delay set to %d sec", - outlet_num, (onOff_setting == PW_AUTO_ON_DELAY)?"start":"shutdown", sec); - dstate_setinfo(varname, "%d", sec); - return STAT_SET_HANDLED; - break; - } - case 0x33: { - upslogx(LOG_NOTICE, "Set [%s] failed due to UPS busy", varname); - /* TODO: we should probably retry... */ - return STAT_SET_UNKNOWN; - break; - } - case 0x35: { - upslogx(LOG_NOTICE, "Set [%s %s] failed due to parameter out of range", varname, val); - return STAT_SET_UNKNOWN; - break; - } - case 0x36: { - upslogx(LOG_NOTICE, "Set [%s %s] failed due to invalid parameter", varname, val); - return STAT_SET_UNKNOWN; - break; - } - default: { - upslogx(LOG_NOTICE, "Set [%s] not supported", varname); - return STAT_SET_FAILED; - break; - } } return STAT_SET_INVALID; } +static int decode_setvar_exec(const ssize_t res, const unsigned char exec_status, const char *cmdname, const char *success_msg) +{ + if (res <= 0) { + upslogx(LOG_ERR, "[%s] Short read from UPS", cmdname); + dstate_datastale(); + return STAT_SET_FAILED; + } + /* Decode the status code from command execution */ + switch (exec_status) { + case BCMXCP_RETURN_ACCEPTED: { + upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg); + upsdrv_comm_good(); + return STAT_SET_HANDLED; + } + case BCMXCP_RETURN_ACCEPTED_PARAMETER_ADJUST: { + upslogx(LOG_NOTICE, "[%s] Parameter adjusted", cmdname); + upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg); + upsdrv_comm_good(); + return STAT_SET_HANDLED; + } + case BCMXCP_RETURN_BUSY: { + upslogx(LOG_NOTICE, "[%s] Busy or disbled by front panel", cmdname); + return STAT_SET_FAILED; + } + case BCMXCP_RETURN_UNRECOGNISED: { + upslogx(LOG_NOTICE, "[%s] Unrecognised command byte or corrupt checksum", cmdname); + return STAT_SET_FAILED; + } + case BCMXCP_RETURN_INVALID_PARAMETER: { + upslogx(LOG_NOTICE, "[%s] Invalid parameter", cmdname); + return STAT_SET_INVALID; + } + case BCMXCP_RETURN_PARAMETER_OUT_OF_RANGE: { + upslogx(LOG_NOTICE, "[%s] Parameter out of range", cmdname); + return STAT_SET_INVALID; + } + default: { + upslogx(LOG_NOTICE, "[%s] Not supported", cmdname); + return STAT_SET_INVALID; + } + } +} + +/******************************* + * Extracted from usbhid-ups.c * + *******************************/ + +/* find the NUT value matching that XCP Item value */ +static const char *nut_find_infoval(info_lkp_t *xcp2info, const double value, const bool_t debug_output_nonexisting) +{ + info_lkp_t *info_lkp; + + /* if a conversion function is defined, use 'value' as argument for it */ + if (xcp2info->fun != NULL) { + return xcp2info->fun(value); + } + + /* use 'value' as an index for a lookup in an array */ + for (info_lkp = xcp2info; info_lkp->nut_value != NULL; info_lkp++) { + if (info_lkp->xcp_value == (long)value) { + upsdebugx(5, "nut_find_infoval: found %s (value: %ld)", info_lkp->nut_value, (long)value); + return info_lkp->nut_value; + } + } + if (debug_output_nonexisting == TRUE) { + upsdebugx(3, "nut_find_infoval: no matching INFO_* value for this XCP value (%g)", value); + } + return NULL; +} diff --git a/drivers/bcmxcp.h b/drivers/bcmxcp.h index beb19fe..7050d1c 100644 --- a/drivers/bcmxcp.h +++ b/drivers/bcmxcp.h @@ -1,382 +1,626 @@ -/* +/* * bcmxcp.h -- header for BCM/XCP module - */ + */ #ifndef _POWERWARE_H #define _POWERWARE_H #include "timehead.h" +/* Have to wait at least 0,25 sec max 16 sec */ +/* 1 second is too short for PW9120 (leads to communication errors). So we set it to 2 seconds */ +#define PW_SLEEP 2 + #define PW_MAX_TRY 3 /* How many times we try to send data. */ #define PW_COMMAND_START_BYTE (unsigned char)0xAB -#define PW_LAST_SEQ (unsigned char)0x80 /* bit flag to indicate final sequence */ -#define PW_SEQ_MASK (unsigned char)0x7F /* bit mask to extract just the sequence # */ +#define PW_LAST_SEQ (unsigned char)0x80 /* bit flag to indicate final sequence */ +#define PW_SEQ_MASK (unsigned char)0x7F /* bit mask to extract just the sequence # */ +#define PW_HEADER_LENGTH 4 /* Size of response header */ -#define PW_ANSWER_MAX_SIZE 256 +#define PW_ANSWER_MAX_SIZE 256 -/* No Autorisation required */ -#define PW_ID_BLOCK_REQ (unsigned char)0x31 /* Model name, ... length 1 */ -#define PW_STATUS_REQ (unsigned char)0x33 /* On Line, On Bypass, ... length 1-2 */ -#define PW_METER_BLOCK_REQ (unsigned char)0x34 /* Current UPS status (Load, utility,...) length 1 */ -#define PW_CUR_ALARM_REQ (unsigned char)0x35 /* Current alarm and event request. length 1 */ -#define PW_CONFIG_BLOC_REQ (unsigned char)0x36 /* Model serial#, ... length 1 */ -#define PW_BAT_TEST_REQ (unsigned char)0x3B /* Charging, floating, ... length 1 */ -#define PW_LIMIT_BLOCK_REQ (unsigned char)0x3C /* Configuration (Bypass thresholds,...). length 1 */ -#define PW_TEST_RESULT_REQ (unsigned char)0x3F /* ??. length 1 */ -#define PW_COMMAND_LIST_REQ (unsigned char)0x40 /* Available commands. length 1 */ -#define PW_OUT_MON_BLOCK_REQ (unsigned char)0x41 /* Outlet monitor request length 1 */ -#define PW_COM_CAP_REQ (unsigned char)0x42 /* Request communication capabilities. length 2 */ -#define PW_UPS_TOP_DATA_REQ (unsigned char)0x43 /* Requsest ups topology data requset. length 1 */ +/* No Autorisation required */ +#define PW_ID_BLOCK_REQ (unsigned char)0x31 /* Model name, ... length 1 */ +#define PW_EVENT_HISTORY_LOG_REQ (unsigned char)0x32 /* List alarms that have occurred. length 1 */ +#define PW_STATUS_REQ (unsigned char)0x33 /* On Line, On Bypass, ... length 1-2 */ +#define PW_METER_BLOCK_REQ (unsigned char)0x34 /* Current UPS status (Load, utility,...) length 1 */ +#define PW_CUR_ALARM_REQ (unsigned char)0x35 /* Current alarm and event request. length 1 */ +#define PW_CONFIG_BLOCK_REQ (unsigned char)0x36 /* Model serial#, ... length 1 */ +#define PW_UTILITY_STATISTICS_BLOCK_REQ (unsigned char)0x38 /* List utility power quality. length 1 */ +#define PW_WAVEFORM_BLOCK_REQ (unsigned char)0x3A /* Sampled waveform data. length 7 */ +#define PW_BATTERY_REQ (unsigned char)0x3B /* Charging, floating, ... length 1 */ +#define PW_LIMIT_BLOCK_REQ (unsigned char)0x3C /* Configuration (Bypass thresholds,...). length 1 */ +#define PW_TEST_RESULT_REQ (unsigned char)0x3F /* Get the results for a system test. length 1 */ +#define PW_COMMAND_LIST_REQ (unsigned char)0x40 /* Available commands. length 1 */ +#define PW_OUT_MON_BLOCK_REQ (unsigned char)0x41 /* Outlet monitor request length 1 */ +#define PW_COM_CAP_REQ (unsigned char)0x42 /* Request communication capabilities. length 2 */ +#define PW_UPS_TOP_DATA_REQ (unsigned char)0x43 /* Request ups topology data requset. length 1 */ +#define PW_COM_PORT_LIST_BLOCK_REQ (unsigned char)0x44 /* Request communication port list. length 1 */ +#define PW_REQUEST_SCRATCHPAD_DATA_REQ (unsigned char)0x45 /* Request data from scratchpad. length 2*/ -/* Need autorisation before this commands */ -#define PW_UPS_ON (unsigned char)0x89 /* UPS on command. length 1-2 */ -#define PW_LOAD_OFF_RESTART (unsigned char)0x8A /* Delayed LoadPowerOff & Restart command. length 2-4 */ -#define PW_UPS_OFF (unsigned char)0x8B /* UPS off command. length 1-2 */ -#define PW_UPS_ON_TIME (unsigned char)0x91 /* Scheduled UPS on in n minutes. length 3-4 */ -#define PW_UPS_OFF_TIME (unsigned char)0x93 /* Scheduled UPS off in n minutes. length 3-4 */ -#define PW_SET_CONF_COMMAND (unsigned char)0x95 /* Set configuration command. length 4 */ -#define PW_SET_OUTLET_COMMAND (unsigned char)0x97 /* Set outlet parameter command length 5. not in 5115 */ -#define PW_SET_COM_COMMAND (unsigned char)0x98 /* Set communication parameter command. length 5 */ -#define PW_SET_REQ_ONLY_MODE (unsigned char)0xA0 /* Set request only mode command. length 1 */ -#define PW_INIT_BAT_TEST (unsigned char)0xB1 /* Initiate battery test command. length 3 */ -#define PW_INIT_SYS_TEST (unsigned char)0xB2 /* Initiate general system test command. length 2 */ +/* Need autorisation before these commands */ +#define PW_GO_TO_BYPASS (unsigned char)0x88 /* Transfer load from inverter to bypass. length 1 or 3 */ +#define PW_UPS_ON (unsigned char)0x89 /* UPS on command. length 1-2 */ +#define PW_LOAD_OFF_RESTART (unsigned char)0x8A /* Delayed LoadPowerOff & Restart command. length 2-4 */ +#define PW_UPS_OFF (unsigned char)0x8B /* UPS off command. length 1-2 */ +#define PW_DECREMENT_OUTPUT_VOLTAGE (unsigned char)0x8C /* Decrease output voltage. length 1 */ +#define PW_INCREMENT_OUTPUT_VOLTAGE (unsigned char)0x8D /* Increase output voltage. length 1 */ +#define PW_SET_TIME_AND_DATE (unsigned char)0x90 /* Set the real-time clock inside UPS. length 8 */ +#define PW_UPS_ON_TIME (unsigned char)0x91 /* Scheduled UPS on in n minutes. length 3-4 */ +#define PW_UPS_ON_AT_TIME (unsigned char)0x92 /* Schedule UPS on at specified date and time. length 7-8 */ +#define PW_UPS_OFF_TIME (unsigned char)0x93 /* Scheduled UPS off in n minutes. length 3-4 */ +#define PW_UPS_OFF_AT_TIME (unsigned char)0x94 /* Schedule UPS off at specified date and time. length 7-8 */ +#define PW_SET_CONF_COMMAND (unsigned char)0x95 /* Set configuration command. length 4 */ +#define PW_SET_OUTLET_COMMAND (unsigned char)0x97 /* Set outlet parameter command length 5. not in 5115 */ +#define PW_SET_COM_COMMAND (unsigned char)0x98 /* Set communication parameter command. length 5 */ +#define PW_SET_SCRATHPAD_SECTOR (unsigned char)0x99 /* Write data to scratchpad. length 3 or 18 */ +#define PW_SET_POWER_STRATEGY (unsigned char)0x9A /* Set the power strategy. length 2 */ +#define PW_SET_REQ_ONLY_MODE (unsigned char)0xA0 /* Set request only mode command. length 1 */ +#define PW_SET_UNREQUESTED_MODE (unsigned char)0xA1 /* Set unrequested mode command. length 1 */ +#define PW_INIT_BAT_TEST (unsigned char)0xB1 /* Initiate battery test command. length 3 */ +#define PW_INIT_SYS_TEST (unsigned char)0xB2 /* Initiate general system test command. length 2 */ +#define PW_SELECT_SUBMODULE (unsigned char)0xCE /* Select a sub module. length 2-7 */ +#define PW_AUTHORIZATION_CODE (unsigned char)0xCF /* Authorization code. length 4 or 7 */ -/* Define the XCP ACK block responses */ -#define XCPRESP_ACK 0x31 /* Accepted and executed */ -#define XCPRESP_NOT_IMPL 0x32 /* Recognized, but not implemented */ -#define XCPRESP_BUSY 0x33 /* Recognized, but Busy and not executed */ -#define XCPRESP_UNRECOGN 0x34 /* Unrecognized cmd */ -#define XCPRESP_OUT_RANGE 0x35 /* Parameter was out of range; not executed */ -#define XCPRESP_PRM_INVLD 0x36 /* Parameter invalid; not executed */ -#define XCPRESP_PRM_ADJST 0x37 /* Parameter adjusted to nearest good value */ -#define XCPRESP_PRM_RDONLY 0x38 /* Parameter is Read-only - cannot be written (at this privilege level) */ +/* Define the XCP system test */ +#define PW_SYS_TEST_GENERAL (unsigned char)0x01 /* Initiate General system Test */ +#define PW_SYS_TEST_SCHEDULE_BATTERY_COMMISSION (unsigned char)0x02 /* Schedule Battery Commissioning Test */ +#define PW_SYS_TEST_ALTERNATE_AC_INPUT (unsigned char)0x03 /* Test Alternate AC Input */ +#define PW_SYS_TEST_FLASH_LIGHTS (unsigned char)0x04 /* Flash the Lights Test */ +#define PW_SYS_TEST_REPORT_CAPABILITIES (unsigned char)0xFF /* Report Systems Test Capabilities */ /* Outlet operations */ -#define PW_ALL_OUTLETS 0 -#define PW_AUTO_OFF_DELAY 1 -#define PW_AUTO_ON_DELAY 2 +#define PW_ALL_OUTLETS 0 +#define PW_AUTO_OFF_DELAY 1 +#define PW_AUTO_ON_DELAY 2 /* 0 means Abort countdown */ -#define PW_TURN_OFF_DELAY 3 -#define PW_TURN_ON_DELAY 4 +#define PW_TURN_OFF_DELAY 3 +#define PW_TURN_ON_DELAY 4 + +/* Config vars*/ +#define PW_CONF_BYPASS_FREQ_DEV_LIMIT 0x01 +#define PW_CONF_LOW_DEV_LIMIT 0x02 +#define PW_CONF_HIGH_DEV_LIMIT 0x03 +#define PW_CONF_PHASE_DEV_LIMIT 0x04 +#define PW_CONF_LOW_BATT 0x05 +#define PW_CONF_BEEPER 0x06 +#define PW_CONF_RETURN_DELAY 0x07 +#define PW_CONF_RETURN_CAP 0x08 +#define PW_CONF_MAX_TEMP 0x0a +#define PW_CONF_NOMINAL_OUT_VOLTAGE 0x0b +#define PW_CONF_SLEEP_TH_LOAD 0x0d +#define PW_CONF_SLEEP_DELAY 0x0e +#define PW_CONF_BATT_STRINGS 0x0f +#define PW_CONF_REQ 0xff /* Config block offsets */ -#define BCMXCP_CONFIG_BLOCK_MACHINE_TYPE_CODE 0 -#define BCMXCP_CONFIG_BLOCK_MODEL_NUMBER 2 -#define BCMXCP_CONFIG_BLOCK_MODEL_CONF_WORD 4 -#define BCMXCP_CONFIG_BLOCK_INPUT_FREQ_DEV_LIMIT 6 -#define BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_VOLTAGE 8 -#define BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_FREQ 10 -#define BCMXCP_CONFIG_BLOCK_OUTPUT_PHASE_ANGLE 12 -#define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_WORD1 14 -#define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_BYTE3 16 /* KEEP THIS UNTILL PARSING OK. USE THIS BYTE. */ -#define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_WORD2 16 -#define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_WORD3 18 -#define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_WORD4 20 -#define BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD1 22 /* Undefined at this time.*/ -#define BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD2 24 /* Per cell inverter shutdown voltage at full rated load. (volt/cell)* 100 */ -#define BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD3 26 /* LOW BYTE Number of battery strings. HIGH BYTE undefined at this time.*/ -#define BCMXCP_CONFIG_BLOCK_EXTENDED_BLOCK_LENGTH 47 -#define BCMXCP_CONFIG_BLOCK_SERIAL_NUMBER 64 +#define BCMXCP_CONFIG_BLOCK_MACHINE_TYPE_CODE 0 +#define BCMXCP_CONFIG_BLOCK_MODEL_NUMBER 2 +#define BCMXCP_CONFIG_BLOCK_MODEL_CONF_WORD 4 +#define BCMXCP_CONFIG_BLOCK_INPUT_FREQ_DEV_LIMIT 6 +#define BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_VOLTAGE 8 +#define BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_FREQ 10 +#define BCMXCP_CONFIG_BLOCK_OUTPUT_PHASE_ANGLE 12 +#define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_WORD1 14 +#define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_BYTE3 16 /* KEEP THIS UNTILL PARSING OK. USE THIS BYTE. */ +#define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_WORD2 16 +#define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_WORD3 18 +#define BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_WORD4 20 +#define BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD1 22 /* Undefined at this time.*/ +#define BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD2 24 /* Per cell inverter shutdown voltage at full rated load. (volt/cell)* 100 */ +#define BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD3 26 /* LOW BYTE Number of battery strings. HIGH BYTE undefined at this time.*/ +#define BCMXCP_CONFIG_BLOCK_EXTENDED_BLOCK_LENGTH 47 +#define BCMXCP_CONFIG_BLOCK_PART_NUMBER 48 +#define BCMXCP_CONFIG_BLOCK_SERIAL_NUMBER 64 + +/*Battery block offsets*/ + +#define BCMXCP_BATTDATA_BLOCK_BATT_TEST_STATUS 0 +#define BCMXCP_BATTDATA_BLOCK_BATT_VOLTS_T1 1 +#define BCMXCP_BATTDATA_BLOCK_BATT_VOLTS_T2 5 +#define BCMXCP_BATTDATA_BLOCK_TEST_DURATION 9 +#define BCMXCP_BATTDATA_BLOCK_UTIL_VOLT 10 +#define BCMXCP_BATTDATA_BLOCK_INPUT_CURRENT 14 +#define BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS 18 +/*BATT_TEST_STATUS for external strings (1 byte each) if BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS == 0 no external test statuses at all*/ +/*next - number of ABM Statuses - at least 1 for internal batteries*/ /* Index for Extende Limits block offsets */ -#define BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_VOLTAGE 0 -#define BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_FREQ 2 -#define BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_TRUE_POWER 4 -#define BCMXCP_EXT_LIMITS_BLOCK_COMM_SPEC_VERSION 6 -#define BCMXCP_EXT_LIMITS_BLOCK_FREQ_DEV_LIMIT 8 -#define BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_LOW_DEV_LIMIT 10 -#define BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_HIGE_DEV_LIMIT 12 -#define BCMXCP_EXT_LIMITS_BLOCK_PHASE_DEV_LIMIT 14 -#define BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING 16 -#define BCMXCP_EXT_LIMITS_BLOCK_HORN_STATUS 17 -#define BCMXCP_EXT_LIMITS_BLOCK_MIN_INPUT_VOLTAGE 18 -#define BCMXCP_EXT_LIMITS_BLOCK_MAX_INPUT_VOLTAGE 20 -#define BCMXCP_EXT_LIMITS_BLOCK_RETURN_STAB_DELAY 22 -#define BCMXCP_EXT_LIMITS_BLOCK_BATT_CAPACITY_RETURN 24 -#define BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_LOW 25 -#define BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_HIGE 26 +#define BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_VOLTAGE 0 +#define BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_FREQ 2 +#define BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_TRUE_POWER 4 +#define BCMXCP_EXT_LIMITS_BLOCK_COMM_SPEC_VERSION 6 +#define BCMXCP_EXT_LIMITS_BLOCK_FREQ_DEV_LIMIT 8 +#define BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_LOW_DEV_LIMIT 10 +#define BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_HIGE_DEV_LIMIT 12 +#define BCMXCP_EXT_LIMITS_BLOCK_PHASE_DEV_LIMIT 14 +#define BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING 16 +#define BCMXCP_EXT_LIMITS_BLOCK_HORN_STATUS 17 +#define BCMXCP_EXT_LIMITS_BLOCK_MIN_INPUT_VOLTAGE 18 +#define BCMXCP_EXT_LIMITS_BLOCK_MAX_INPUT_VOLTAGE 20 +#define BCMXCP_EXT_LIMITS_BLOCK_RETURN_STAB_DELAY 22 +#define BCMXCP_EXT_LIMITS_BLOCK_BATT_CAPACITY_RETURN 24 +#define BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_LOW 25 +#define BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_HIGE 26 +#define BCMXCP_EXT_LIMITS_BLOCK_SLEEP_TH_LOAD 29 +#define BCMXCP_EXT_LIMITS_BLOCK_SLEEP_DELAY 30 -/* Meter map offsets used */ -#define BCMXCP_METER_MAP_OUTPUT_VA 23 -#define BCMXCP_METER_MAP_LOAD_CURR_PHASE_A 65 -#define BCMXCP_METER_MAP_LOAD_CURR_PHASE_A_BAR_CHART 68 -#define BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART 71 +/* Indexes for meter map */ +#define BCMXCP_METER_MAP_OUTPUT_VOLTS_AB 0 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_VOLTS_BC 1 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_VOLTS_CA 2 /* mapped */ +#define BCMXCP_METER_MAP_INPUT_VOLTS_AB 3 /* mapped */ +#define BCMXCP_METER_MAP_INPUT_VOLTS_BC 4 /* mapped */ +#define BCMXCP_METER_MAP_INPUT_VOLTS_CA 5 /* mapped */ +#define BCMXCP_METER_MAP_INVERTER_VOLTS_AB 6 +#define BCMXCP_METER_MAP_INVERTER_VOLTS_BC 7 +#define BCMXCP_METER_MAP_INVERTER_VOLTS_CA 8 +#define BCMXCP_METER_MAP_BYPASS_VOLTS_AB 9 +#define BCMXCP_METER_MAP_BYPASS_VOLTS_BC 10 +#define BCMXCP_METER_MAP_BYPASS_VOLTS_CA 11 +#define BCMXCP_METER_MAP_MAIN_LOGIC_POWER 12 +#define BCMXCP_METER_MAP_SECONDARY_V_PLUS_POWER 13 +#define BCMXCP_METER_MAP_SECONDARY_V_MINUS_POWER 14 +#define BCMXCP_METER_MAP_INVERTER_AVG_CURRENT_PHASE_A 15 +#define BCMXCP_METER_MAP_INVERTER_AVG_CURRENT_PHASE_B 16 +#define BCMXCP_METER_MAP_INVERTER_AVG_CURRENT_PHASE_C 17 +#define BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_A 18 /* mapped */ +#define BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_B 19 /* mapped */ +#define BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_C 20 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_WATTS 21 +#define BCMXCP_METER_MAP_INPUT_WATTS 22 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_VA 23 /* mapped */ +#define BCMXCP_METER_MAP_INPUT_VA 24 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_POWER_FACTOR 25 /* mapped */ +#define BCMXCP_METER_MAP_INPUT_POWER_FACTOR 26 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_FREQUENCY 27 /* mapped */ +#define BCMXCP_METER_MAP_INPUT_FREQUENCY 28 /* mapped */ +#define BCMXCP_METER_MAP_INVERTER_FREQUENCY 29 +#define BCMXCP_METER_MAP_BYPASS_FREQUENCY 30 /* mapped */ +#define BCMXCP_METER_MAP_DC_LINK_VOLTS_DC 31 +#define BCMXCP_METER_MAP_BATTERY_CURRENT 32 /* mapped */ +#define BCMXCP_METER_MAP_BATTERY_VOLTAGE 33 /* mapped */ +#define BCMXCP_METER_MAP_PERCENT_BATTERY_LEFT 34 /* mapped */ +#define BCMXCP_METER_MAP_BATTERY_TIME_REMAINING 35 /* mapped */ +#define BCMXCP_METER_MAP_BATTERY_CHARGE_TIME 36 +#define BCMXCP_METER_MAP_PEAK_INVERTER_CURRENT_PHASE_A 37 +#define BCMXCP_METER_MAP_PEAK_INVERTER_CURRENT_PHASE_B 38 +#define BCMXCP_METER_MAP_PEAK_INVERTER_CURRENT_PHASE_C 39 +#define BCMXCP_METER_MAP_AVG_INPUT_CURRENT_3_PHASE_SUM 40 +#define BCMXCP_METER_MAP_BATTERY_DCUV_BAR_CHART 41 /* mapped */ +#define BCMXCP_METER_MAP_INPUT_CURRENT_BAR_CHART 42 +#define BCMXCP_METER_MAP_LOW_BATTERY_WARNING_V_BAR_CHART 43 /* mapped */ +#define BCMXCP_METER_MAP_DC_VOLTS_BAR_CHART 44 +#define BCMXCP_METER_MAP_BATTERY_CHARGING_CURRENT_BAR_CHART 45 +#define BCMXCP_METER_MAP_BATTERY_DISCHARGING_CURRENT_BAR_CHART 46 /* mapped */ +#define BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_A 47 /* mapped */ +#define BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_B 48 /* mapped */ +#define BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_C 49 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_VA_PHASE_A 50 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_VA_PHASE_B 51 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_VA_PHASE_C 52 /* mapped */ +#define BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_A 53 /* mapped */ +#define BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_B 54 /* mapped */ +#define BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_C 55 /* mapped */ +#define BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_A 56 /* mapped */ +#define BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_B 57 /* mapped */ +#define BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_C 58 /* mapped */ +#define BCMXCP_METER_MAP_INVERTER_VOLTS_PHASE_A 59 +#define BCMXCP_METER_MAP_INVERTER_VOLTS_PHASE_B 60 +#define BCMXCP_METER_MAP_INVERTER_VOLTS_PHASE_C 61 +#define BCMXCP_METER_MAP_AMBIENT_TEMPERATURE 62 /* mapped */ +#define BCMXCP_METER_MAP_HEATSINK_TEMPERATURE 63 /* mapped */ +#define BCMXCP_METER_MAP_POWER_SUPPLY_TEMPERATURE 64 /* mapped */ +#define BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A 65 /* mapped */ +#define BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_B 66 /* mapped */ +#define BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_C 67 /* mapped */ +#define BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART 68 /* mapped */ +#define BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_B_BAR_CHART 69 /* mapped */ +#define BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_C_BAR_CHART 70 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART 71 /* mapped */ +#define BCMXCP_METER_MAP_DATE 72 /* mapped */ +#define BCMXCP_METER_MAP_TIME 73 /* mapped */ +#define BCMXCP_METER_MAP_POSITIVE_DC_LINK_RAIL_VOLTAGE 74 +#define BCMXCP_METER_MAP_NEGATIVE_DC_LINK_RAIL_VOLTAGE 75 +#define BCMXCP_METER_MAP_AUTO_BALANCE_VOLTAGE_DC 76 +#define BCMXCP_METER_MAP_BATTERY_TEMPERATURE 77 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_VOLTS_A 78 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_VOLTS_B 79 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_VOLTS_C 80 /* mapped */ +#define BCMXCP_METER_MAP_NEUTRAL_CURRENT 81 +#define BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_A 82 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_B 83 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_C 84 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_A_B_C_BAR_CHART 85 /* mapped */ +#define BCMXCP_METER_MAP_RECTIFIER_DC_CURRENT 86 +#define BCMXCP_METER_MAP_POSITIVE_BATTERY_VOLTAGE 87 +#define BCMXCP_METER_MAP_NEGATIVE_BATTERY_VOLTAGE 88 +#define BCMXCP_METER_MAP_POSITIVE_BATTERY_CURRENT 89 +#define BCMXCP_METER_MAP_NEGATIVE_BATTERY_CURRENT 90 +#define BCMXCP_METER_MAP_LINE_EVENT_COUNTER 91 /* mapped */ +#define BCMXCP_METER_MAP_OUTPUT_V1_PERCENT 92 +#define BCMXCP_METER_MAP_OUTPUT_V2_PERCENT 93 +#define BCMXCP_METER_MAP_OUTPUT_V3_PERCENT 94 +#define BCMXCP_METER_MAP_OUTPUT_I1_PERCENT 95 +#define BCMXCP_METER_MAP_OUTPUT_I2_PERCENT 96 +#define BCMXCP_METER_MAP_OUTPUT_I3_PERCENT 97 +#define BCMXCP_METER_MAP_INPUT_V1_PERCENT 98 +#define BCMXCP_METER_MAP_INPUT_V2_PERCENT 99 +#define BCMXCP_METER_MAP_INPUT_V3_PERCENT 100 +#define BCMXCP_METER_MAP_INPUT_I1_PERCENT 101 +#define BCMXCP_METER_MAP_INPUT_I2_PERCENT 102 +#define BCMXCP_METER_MAP_INPUT_I3_PERCENT 103 +#define BCMXCP_METER_MAP_GROUND_CURRENT 104 +#define BCMXCP_METER_MAP_OUTPUT_CREST_FACTOR_L1 105 +#define BCMXCP_METER_MAP_OUTPUT_CREST_FACTOR_L2 106 +#define BCMXCP_METER_MAP_OUTPUT_CREST_FACTOR_L3 107 +#define BCMXCP_METER_MAP_OUTPUT_KW_HOUR 108 +#define BCMXCP_METER_MAP_INPUT_VOLTAGE_THD_LINE1 109 +#define BCMXCP_METER_MAP_INPUT_VOLTAGE_THD_LINE2 110 +#define BCMXCP_METER_MAP_INPUT_VOLTAGE_THD_LINE3 111 +#define BCMXCP_METER_MAP_INPUT_CURRENT_THD_LINE1 112 +#define BCMXCP_METER_MAP_INPUT_CURRENT_THD_LINE2 113 +#define BCMXCP_METER_MAP_INPUT_CURRENT_THD_LINE3 114 +#define BCMXCP_METER_MAP_OUTPUT_VOLTAGE_THD_LINE1 115 +#define BCMXCP_METER_MAP_OUTPUT_VOLTAGE_THD_LINE2 116 +#define BCMXCP_METER_MAP_OUTPUT_VOLTAGE_THD_LINE3 117 +#define BCMXCP_METER_MAP_OUTPUT_CURRENT_THD_LINE1 118 +#define BCMXCP_METER_MAP_OUTPUT_CURRENT_THD_LINE2 119 +#define BCMXCP_METER_MAP_OUTPUT_CURRENT_THD_LINE3 120 +#define BCMXCP_METER_MAP_INPUT_CREST_FACTOR_L1 121 +#define BCMXCP_METER_MAP_INPUT_CREST_FACTOR_L2 122 +#define BCMXCP_METER_MAP_INPUT_CREST_FACTOR_L3 123 +#define BCMXCP_METER_MAP_INPUT_KW_HOUR 124 +#define BCMXCP_METER_MAP_BATTERY_LIFE_REMAINING 125 +#define BCMXCP_METER_MAP_SECONDARY_NEUTRAL_CURRENT 126 +#define BCMXCP_METER_MAP_SECONDARY_GROUND_CURRENT 127 +#define BCMXCP_METER_MAP_HOURS_OF_OPERATION 128 /* Indexes for alarm map */ -#define BCMXCP_ALARM_INVERTER_AC_OVER_VOLTAGE 0 -#define BCMXCP_ALARM_INVERTER_AC_UNDER_VOLTAGE 1 -#define BCMXCP_ALARM_INVERTER_OVER_OR_UNDER_FREQ 2 -#define BCMXCP_ALARM_BYPASS_AC_OVER_VOLTAGE 3 -#define BCMXCP_ALARM_BYPASS_AC_UNDER_VOLTAGE 4 -#define BCMXCP_ALARM_BYPASS_OVER_OR_UNDER_FREQ 5 -#define BCMXCP_ALARM_INPUT_AC_OVER_VOLTAGE 6 -#define BCMXCP_ALARM_INPUT_AC_UNDER_VOLTAGE 7 -#define BCMXCP_ALARM_INPUT_UNDER_OR_OVER_FREQ 8 -#define BCMXCP_ALARM_OUTPUT_OVER_VOLTAGE 9 -#define BCMXCP_ALARM_OUTPUT_UNDER_VOLTAGE 10 -#define BCMXCP_ALARM_OUTPUT_UNDER_OR_OVER_FREQ 11 -#define BCMXCP_ALARM_REMOTE_EMERGENCY_PWR_OFF 12 -#define BCMXCP_ALARM_REMOTE_GO_TO_BYPASS 13 -#define BCMXCP_ALARM_BUILDING_ALARM_6 14 -#define BCMXCP_ALARM_BUILDING_ALARM_5 15 -#define BCMXCP_ALARM_BUILDING_ALARM_4 16 -#define BCMXCP_ALARM_BUILDING_ALARM_3 17 -#define BCMXCP_ALARM_BUILDING_ALARM_2 18 -#define BCMXCP_ALARM_BUILDING_ALARM_1 19 -#define BCMXCP_ALARM_STATIC_SWITCH_OVER_TEMP 20 -#define BCMXCP_ALARM_CHARGER_OVER_TEMP 21 -#define BCMXCP_ALARM_CHARGER_LOGIC_PWR_FAIL 22 -#define BCMXCP_ALARM_CHARGER_OVER_VOLTAGE_OR_CURRENT 23 -#define BCMXCP_ALARM_INVERTER_OVER_TEMP 24 -#define BCMXCP_ALARM_OUTPUT_OVERLOAD 25 -#define BCMXCP_ALARM_RECTIFIER_INPUT_OVER_CURRENT 26 -#define BCMXCP_ALARM_INVERTER_OUTPUT_OVER_CURRENT 27 -#define BCMXCP_ALARM_DC_LINK_OVER_VOLTAGE 28 -#define BCMXCP_ALARM_DC_LINK_UNDER_VOLTAGE 29 -#define BCMXCP_ALARM_RECTIFIER_FAILED 30 -#define BCMXCP_ALARM_INVERTER_FAULT 31 -#define BCMXCP_ALARM_BATTERY_CONNECTOR_FAIL 32 -#define BCMXCP_ALARM_BYPASS_BREAKER_FAIL 33 -#define BCMXCP_ALARM_CHARGER_FAIL 34 -#define BCMXCP_ALARM_RAMP_UP_FAILED 35 -#define BCMXCP_ALARM_STATIC_SWITCH_FAILED 36 -#define BCMXCP_ALARM_ANALOG_AD_REF_FAIL 37 -#define BCMXCP_ALARM_BYPASS_UNCALIBRATED 38 -#define BCMXCP_ALARM_RECTIFIER_UNCALIBRATED 39 -#define BCMXCP_ALARM_OUTPUT_UNCALIBRATED 40 -#define BCMXCP_ALARM_INVERTER_UNCALIBRATED 41 -#define BCMXCP_ALARM_DC_VOLT_UNCALIBRATED 42 -#define BCMXCP_ALARM_OUTPUT_CURRENT_UNCALIBRATED 43 -#define BCMXCP_ALARM_RECTIFIER_CURRENT_UNCALIBRATED 44 -#define BCMXCP_ALARM_BATTERY_CURRENT_UNCALIBRATED 45 -#define BCMXCP_ALARM_INVERTER_ON_OFF_STAT_FAIL 46 -#define BCMXCP_ALARM_BATTERY_CURRENT_LIMIT 47 -#define BCMXCP_ALARM_INVERTER_STARTUP_FAIL 48 -#define BCMXCP_ALARM_ANALOG_BOARD_AD_STAT_FAIL 49 -#define BCMXCP_ALARM_OUTPUT_CURRENT_OVER_100 50 -#define BCMXCP_ALARM_BATTERY_GROUND_FAULT 51 -#define BCMXCP_ALARM_WAITING_FOR_CHARGER_SYNC 52 -#define BCMXCP_ALARM_NV_RAM_FAIL 53 -#define BCMXCP_ALARM_ANALOG_BOARD_AD_TIMEOUT 54 -#define BCMXCP_ALARM_SHUTDOWN_IMMINENT 55 -#define BCMXCP_ALARM_BATTERY_LOW 56 -#define BCMXCP_ALARM_UTILITY_FAIL 57 -#define BCMXCP_ALARM_OUTPUT_SHORT_CIRCUIT 58 -#define BCMXCP_ALARM_UTILITY_NOT_PRESENT 59 -#define BCMXCP_ALARM_FULL_TIME_CHARGING 60 -#define BCMXCP_ALARM_FAST_BYPASS_COMMAND 61 -#define BCMXCP_ALARM_AD_ERROR 62 -#define BCMXCP_ALARM_INTERNAL_COM_FAIL 63 -#define BCMXCP_ALARM_RECTIFIER_SELFTEST_FAIL 64 -#define BCMXCP_ALARM_RECTIFIER_EEPROM_FAIL 65 -#define BCMXCP_ALARM_RECTIFIER_EPROM_FAIL 66 -#define BCMXCP_ALARM_INPUT_LINE_VOLTAGE_LOSS 67 -#define BCMXCP_ALARM_BATTERY_DC_OVER_VOLTAGE 68 -#define BCMXCP_ALARM_POWER_SUPPLY_OVER_TEMP 69 -#define BCMXCP_ALARM_POWER_SUPPLY_FAIL 70 -#define BCMXCP_ALARM_POWER_SUPPLY_5V_FAIL 71 -#define BCMXCP_ALARM_POWER_SUPPLY_12V_FAIL 72 -#define BCMXCP_ALARM_HEATSINK_OVER_TEMP 73 -#define BCMXCP_ALARM_HEATSINK_TEMP_SENSOR_FAIL 74 -#define BCMXCP_ALARM_RECTIFIER_CURRENT_OVER_125 75 -#define BCMXCP_ALARM_RECTIFIER_FAULT_INTERRUPT_FAIL 76 -#define BCMXCP_ALARM_RECTIFIER_POWER_CAPASITOR_FAIL 77 -#define BCMXCP_ALARM_INVERTER_PROGRAM_STACK_ERROR 78 -#define BCMXCP_ALARM_INVERTER_BOARD_SELFTEST_FAIL 79 -#define BCMXCP_ALARM_INVERTER_AD_SELFTEST_FAIL 80 -#define BCMXCP_ALARM_INVERTER_RAM_SELFTEST_FAIL 81 -#define BCMXCP_ALARM_NV_MEMORY_CHECKSUM_FAIL 82 -#define BCMXCP_ALARM_PROGRAM_CHECKSUM_FAIL 83 -#define BCMXCP_ALARM_INVERTER_CPU_SELFTEST_FAIL 84 -#define BCMXCP_ALARM_NETWORK_NOT_RESPONDING 85 -#define BCMXCP_ALARM_FRONT_PANEL_SELFTEST_FAIL 86 -#define BCMXCP_ALARM_NODE_EEPROM_VERIFICATION_ERROR 87 -#define BCMXCP_ALARM_OUTPUT_AC_OVER_VOLT_TEST_FAIL 88 -#define BCMXCP_ALARM_OUTPUT_DC_OVER_VOLTAGE 89 -#define BCMXCP_ALARM_INPUT_PHASE_ROTATION_ERROR 90 -#define BCMXCP_ALARM_INVERTER_RAMP_UP_TEST_FAILED 91 -#define BCMXCP_ALARM_INVERTER_OFF_COMMAND 92 -#define BCMXCP_ALARM_INVERTER_ON_COMMAND 93 -#define BCMXCP_ALARM_TO_BYPASS_COMMAND 94 -#define BCMXCP_ALARM_FROM_BYPASS_COMMAND 95 -#define BCMXCP_ALARM_AUTO_MODE_COMMAND 96 -#define BCMXCP_ALARM_EMERGENCY_SHUTDOWN_COMMAND 97 -#define BCMXCP_ALARM_SETUP_SWITCH_OPEN 98 -#define BCMXCP_ALARM_INVERTER_OVER_VOLT_INT 99 -#define BCMXCP_ALARM_INVERTER_UNDER_VOLT_INT 100 -#define BCMXCP_ALARM_ABSOLUTE_DCOV_ACOV 101 -#define BCMXCP_ALARM_PHASE_A_CURRENT_LIMIT 102 -#define BCMXCP_ALARM_PHASE_B_CURRENT_LIMIT 103 -#define BCMXCP_ALARM_PHASE_C_CURRENT_LIMIT 104 -#define BCMXCP_ALARM_BYPASS_NOT_AVAILABLE 105 -#define BCMXCP_ALARM_RECTIFIER_BREAKER_OPEN 106 -#define BCMXCP_ALARM_BATTERY_CONTACTOR_OPEN 107 -#define BCMXCP_ALARM_INVERTER_CONTACTOR_OPEN 108 -#define BCMXCP_ALARM_BYPASS_BREAKER_OPEN 109 -#define BCMXCP_ALARM_INV_BOARD_ACOV_INT_TEST_FAIL 110 -#define BCMXCP_ALARM_INVERTER_OVER_TEMP_TRIP 111 -#define BCMXCP_ALARM_INV_BOARD_ACUV_INT_TEST_FAIL 112 -#define BCMXCP_ALARM_INVERTER_VOLTAGE_FEEDBACK_ERROR 113 -#define BCMXCP_ALARM_DC_UNDER_VOLTAGE_TIMEOUT 114 -#define BCMXCP_ALARM_AC_UNDER_VOLTAGE_TIMEOUT 115 -#define BCMXCP_ALARM_DC_UNDER_VOLTAGE_WHILE_CHARGE 116 -#define BCMXCP_ALARM_INVERTER_VOLTAGE_BIAS_ERROR 117 -#define BCMXCP_ALARM_RECTIFIER_PHASE_ROTATION 118 -#define BCMXCP_ALARM_BYPASS_PHASER_ROTATION 119 -#define BCMXCP_ALARM_SYSTEM_INTERFACE_BOARD_FAIL 120 -#define BCMXCP_ALARM_PARALLEL_BOARD_FAIL 121 -#define BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_A 122 -#define BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_B 123 -#define BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_C 124 -#define BCMXCP_ALARM_DC_OVER_VOLTAGE_TIMEOUT 125 -#define BCMXCP_ALARM_BATTERY_TOTALLY_DISCHARGED 126 -#define BCMXCP_ALARM_INVERTER_PHASE_BIAS_ERROR 127 -#define BCMXCP_ALARM_INVERTER_VOLTAGE_BIAS_ERROR_2 128 -#define BCMXCP_ALARM_DC_LINK_BLEED_COMPLETE 129 -#define BCMXCP_ALARM_LARGE_CHARGER_INPUT_CURRENT 130 -#define BCMXCP_ALARM_INV_VOLT_TOO_LOW_FOR_RAMP_LEVEL 131 -#define BCMXCP_ALARM_LOSS_OF_REDUNDANCY 132 -#define BCMXCP_ALARM_LOSS_OF_SYNC_BUS 133 -#define BCMXCP_ALARM_RECTIFIER_BREAKER_SHUNT_TRIP 134 -#define BCMXCP_ALARM_LOSS_OF_CHARGER_SYNC 135 -#define BCMXCP_ALARM_INVERTER_LOW_LEVEL_TEST_TIMEOUT 136 -#define BCMXCP_ALARM_OUTPUT_BREAKER_OPEN 137 -#define BCMXCP_ALARM_CONTROL_POWER_ON 138 -#define BCMXCP_ALARM_INVERTER_ON 139 -#define BCMXCP_ALARM_CHARGER_ON 140 -#define BCMXCP_ALARM_BYPASS_ON 141 -#define BCMXCP_ALARM_BYPASS_POWER_LOSS 142 -#define BCMXCP_ALARM_ON_MANUAL_BYPASS 143 -#define BCMXCP_ALARM_BYPASS_MANUAL_TURN_OFF 144 -#define BCMXCP_ALARM_INVERTER_BLEEDING_DC_LINK_VOLT 145 -#define BCMXCP_ALARM_CPU_ISR_ERROR 146 -#define BCMXCP_ALARM_SYSTEM_ISR_RESTART 147 -#define BCMXCP_ALARM_PARALLEL_DC 148 -#define BCMXCP_ALARM_BATTERY_NEEDS_SERVICE 149 -#define BCMXCP_ALARM_BATTERY_CHARGING 150 -#define BCMXCP_ALARM_BATTERY_NOT_CHARGED 151 -#define BCMXCP_ALARM_DISABLED_BATTERY_TIME 152 -#define BCMXCP_ALARM_SERIES_7000_ENABLE 153 -#define BCMXCP_ALARM_OTHER_UPS_ON 154 -#define BCMXCP_ALARM_PARALLEL_INVERTER 155 -#define BCMXCP_ALARM_UPS_IN_PARALLEL 156 -#define BCMXCP_ALARM_OUTPUT_BREAKER_REALY_FAIL 157 -#define BCMXCP_ALARM_CONTROL_POWER_OFF 158 -#define BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_A 159 -#define BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_B 160 -#define BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_C 161 -#define BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_A 162 -#define BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_B 163 -#define BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_C 164 -#define BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_A 165 -#define BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_B 166 -#define BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_C 167 -#define BCMXCP_ALARM_UPS_ON_BATTERY 168 -#define BCMXCP_ALARM_UPS_ON_BYPASS 169 -#define BCMXCP_ALARM_LOAD_DUMPED 170 -#define BCMXCP_ALARM_LOAD_ON_INVERTER 171 -#define BCMXCP_ALARM_UPS_ON_COMMAND 172 -#define BCMXCP_ALARM_UPS_OFF_COMMAND 173 -#define BCMXCP_ALARM_LOW_BATTERY_SHUTDOWN 174 -#define BCMXCP_ALARM_AUTO_ON_ENABLED 175 -#define BCMXCP_ALARM_SOFTWARE_INCOMPABILITY_DETECTED 176 -#define BCMXCP_ALARM_INVERTER_TEMP_SENSOR_FAILED 177 -#define BCMXCP_ALARM_DC_START_OCCURED 178 -#define BCMXCP_ALARM_IN_PARALLEL_OPERATION 179 -#define BCMXCP_ALARM_SYNCING_TO_BYPASS 180 -#define BCMXCP_ALARM_RAMPING_UPS_UP 181 -#define BCMXCP_ALARM_INVERTER_ON_DELAY 182 -#define BCMXCP_ALARM_CHARGER_ON_DELAY 183 -#define BCMXCP_ALARM_WAITING_FOR_UTIL_INPUT 184 -#define BCMXCP_ALARM_CLOSE_BYPASS_BREAKER 185 -#define BCMXCP_ALARM_TEMPORARY_BYPASS_OPERATION 186 -#define BCMXCP_ALARM_SYNCING_TO_OUTPUT 187 -#define BCMXCP_ALARM_BYPASS_FAILURE 188 -#define BCMXCP_ALARM_AUTO_OFF_COMMAND_EXECUTED 189 -#define BCMXCP_ALARM_AUTO_ON_COMMAND_EXECUTED 190 -#define BCMXCP_ALARM_BATTERY_TEST_FAILED 191 -#define BCMXCP_ALARM_FUSE_FAIL 192 -#define BCMXCP_ALARM_FAN_FAIL 193 -#define BCMXCP_ALARM_SITE_WIRING_FAULT 194 -#define BCMXCP_ALARM_BACKFEED_CONTACTOR_FAIL 195 -#define BCMXCP_ALARM_ON_BUCK 196 -#define BCMXCP_ALARM_ON_BOOST 197 -#define BCMXCP_ALARM_ON_DOUBLE_BOOST 198 -#define BCMXCP_ALARM_BATTERIES_DISCONNECTED 199 -#define BCMXCP_ALARM_UPS_CABINET_OVER_TEMP 200 -#define BCMXCP_ALARM_TRANSFORMER_OVER_TEMP 201 -#define BCMXCP_ALARM_AMBIENT_UNDER_TEMP 202 -#define BCMXCP_ALARM_AMBIENT_OVER_TEMP 203 -#define BCMXCP_ALARM_CABINET_DOOR_OPEN 204 -#define BCMXCP_ALARM_CABINET_DOOR_OPEN_VOLT_PRESENT 205 -#define BCMXCP_ALARM_AUTO_SHUTDOWN_PENDING 206 -#define BCMXCP_ALARM_TAP_SWITCHING_REALY_PENDING 207 -#define BCMXCP_ALARM_UNABLE_TO_CHARGE_BATTERIES 208 -#define BCMXCP_ALARM_STARTUP_FAILURE_CHECK_EPO 209 -#define BCMXCP_ALARM_AUTOMATIC_STARTUP_PENDING 210 -#define BCMXCP_ALARM_MODEM_FAILED 211 -#define BCMXCP_ALARM_INCOMING_MODEM_CALL_STARTED 212 -#define BCMXCP_ALARM_OUTGOING_MODEM_CALL_STARTED 213 -#define BCMXCP_ALARM_MODEM_CONNECTION_ESTABLISHED 214 -#define BCMXCP_ALARM_MODEM_CALL_COMPLETED_SUCCESS 215 -#define BCMXCP_ALARM_MODEM_CALL_COMPLETED_FAIL 216 -#define BCMXCP_ALARM_INPUT_BREAKER_FAIL 217 -#define BCMXCP_ALARM_SYSINIT_IN_PROGRESS 218 -#define BCMXCP_ALARM_AUTOCALIBRATION_FAIL 219 -#define BCMXCP_ALARM_SELECTIVE_TRIP_OF_MODULE 220 -#define BCMXCP_ALARM_INVERTER_OUTPUT_FAILURE 221 -#define BCMXCP_ALARM_ABNORMAL_OUTPUT_VOLT_AT_STARTUP 222 -#define BCMXCP_ALARM_RECTIFIER_OVER_TEMP 223 -#define BCMXCP_ALARM_CONFIG_ERROR 224 -#define BCMXCP_ALARM_REDUNDANCY_LOSS_DUE_TO_OVERLOAD 225 -#define BCMXCP_ALARM_ON_ALTERNATE_AC_SOURCE 226 -#define BCMXCP_ALARM_IN_HIGH_EFFICIENCY_MODE 227 -#define BCMXCP_ALARM_SYSTEM_NOTICE_ACTIVE 228 -#define BCMXCP_ALARM_SYSTEM_ALARM_ACTIVE 229 -#define BCMXCP_ALARM_ALTERNATE_POWER_SOURCE_NOT_AVAILABLE 230 -#define BCMXCP_ALARM_CURRENT_BALANCE_FAILURE 231 -#define BCMXCP_ALARM_CHECK_AIR_FILTER 232 -#define BCMXCP_ALARM_SUBSYSTEM_NOTICE_ACTIVE 233 -#define BCMXCP_ALARM_SUBSYSTEM_ALARM_ACTIVE 234 -#define BCMXCP_ALARM_CHARGER_ON_COMMAND 235 -#define BCMXCP_ALARM_CHARGER_OFF_COMMAND 236 -#define BCMXCP_ALARM_UPS_NORMAL 237 -#define BCMXCP_ALARM_EXTERNAL_COMMUNICATION_FAILURE 238 +#define BCMXCP_ALARM_INVERTER_AC_OVER_VOLTAGE 0 +#define BCMXCP_ALARM_INVERTER_AC_UNDER_VOLTAGE 1 +#define BCMXCP_ALARM_INVERTER_OVER_OR_UNDER_FREQ 2 +#define BCMXCP_ALARM_BYPASS_AC_OVER_VOLTAGE 3 +#define BCMXCP_ALARM_BYPASS_AC_UNDER_VOLTAGE 4 +#define BCMXCP_ALARM_BYPASS_OVER_OR_UNDER_FREQ 5 +#define BCMXCP_ALARM_INPUT_AC_OVER_VOLTAGE 6 +#define BCMXCP_ALARM_INPUT_AC_UNDER_VOLTAGE 7 +#define BCMXCP_ALARM_INPUT_UNDER_OR_OVER_FREQ 8 +#define BCMXCP_ALARM_OUTPUT_OVER_VOLTAGE 9 +#define BCMXCP_ALARM_OUTPUT_UNDER_VOLTAGE 10 +#define BCMXCP_ALARM_OUTPUT_UNDER_OR_OVER_FREQ 11 +#define BCMXCP_ALARM_REMOTE_EMERGENCY_PWR_OFF 12 +#define BCMXCP_ALARM_REMOTE_GO_TO_BYPASS 13 +#define BCMXCP_ALARM_BUILDING_ALARM_6 14 +#define BCMXCP_ALARM_BUILDING_ALARM_5 15 +#define BCMXCP_ALARM_BUILDING_ALARM_4 16 +#define BCMXCP_ALARM_BUILDING_ALARM_3 17 +#define BCMXCP_ALARM_BUILDING_ALARM_2 18 +#define BCMXCP_ALARM_BUILDING_ALARM_1 19 +#define BCMXCP_ALARM_STATIC_SWITCH_OVER_TEMP 20 +#define BCMXCP_ALARM_CHARGER_OVER_TEMP 21 +#define BCMXCP_ALARM_CHARGER_LOGIC_PWR_FAIL 22 +#define BCMXCP_ALARM_CHARGER_OVER_VOLTAGE_OR_CURRENT 23 +#define BCMXCP_ALARM_INVERTER_OVER_TEMP 24 +#define BCMXCP_ALARM_OUTPUT_OVERLOAD 25 +#define BCMXCP_ALARM_RECTIFIER_INPUT_OVER_CURRENT 26 +#define BCMXCP_ALARM_INVERTER_OUTPUT_OVER_CURRENT 27 +#define BCMXCP_ALARM_DC_LINK_OVER_VOLTAGE 28 +#define BCMXCP_ALARM_DC_LINK_UNDER_VOLTAGE 29 +#define BCMXCP_ALARM_RECTIFIER_FAILED 30 +#define BCMXCP_ALARM_INVERTER_FAULT 31 +#define BCMXCP_ALARM_BATTERY_CONNECTOR_FAIL 32 +#define BCMXCP_ALARM_BYPASS_BREAKER_FAIL 33 +#define BCMXCP_ALARM_CHARGER_FAIL 34 +#define BCMXCP_ALARM_RAMP_UP_FAILED 35 +#define BCMXCP_ALARM_STATIC_SWITCH_FAILED 36 +#define BCMXCP_ALARM_ANALOG_AD_REF_FAIL 37 +#define BCMXCP_ALARM_BYPASS_UNCALIBRATED 38 +#define BCMXCP_ALARM_RECTIFIER_UNCALIBRATED 39 +#define BCMXCP_ALARM_OUTPUT_UNCALIBRATED 40 +#define BCMXCP_ALARM_INVERTER_UNCALIBRATED 41 +#define BCMXCP_ALARM_DC_VOLT_UNCALIBRATED 42 +#define BCMXCP_ALARM_OUTPUT_CURRENT_UNCALIBRATED 43 +#define BCMXCP_ALARM_RECTIFIER_CURRENT_UNCALIBRATED 44 +#define BCMXCP_ALARM_BATTERY_CURRENT_UNCALIBRATED 45 +#define BCMXCP_ALARM_INVERTER_ON_OFF_STAT_FAIL 46 +#define BCMXCP_ALARM_BATTERY_CURRENT_LIMIT 47 +#define BCMXCP_ALARM_INVERTER_STARTUP_FAIL 48 +#define BCMXCP_ALARM_ANALOG_BOARD_AD_STAT_FAIL 49 +#define BCMXCP_ALARM_OUTPUT_CURRENT_OVER_100 50 +#define BCMXCP_ALARM_BATTERY_GROUND_FAULT 51 +#define BCMXCP_ALARM_WAITING_FOR_CHARGER_SYNC 52 +#define BCMXCP_ALARM_NV_RAM_FAIL 53 +#define BCMXCP_ALARM_ANALOG_BOARD_AD_TIMEOUT 54 +#define BCMXCP_ALARM_SHUTDOWN_IMMINENT 55 +#define BCMXCP_ALARM_BATTERY_LOW 56 +#define BCMXCP_ALARM_UTILITY_FAIL 57 +#define BCMXCP_ALARM_OUTPUT_SHORT_CIRCUIT 58 +#define BCMXCP_ALARM_UTILITY_NOT_PRESENT 59 +#define BCMXCP_ALARM_FULL_TIME_CHARGING 60 +#define BCMXCP_ALARM_FAST_BYPASS_COMMAND 61 +#define BCMXCP_ALARM_AD_ERROR 62 +#define BCMXCP_ALARM_INTERNAL_COM_FAIL 63 +#define BCMXCP_ALARM_RECTIFIER_SELFTEST_FAIL 64 +#define BCMXCP_ALARM_RECTIFIER_EEPROM_FAIL 65 +#define BCMXCP_ALARM_RECTIFIER_EPROM_FAIL 66 +#define BCMXCP_ALARM_INPUT_LINE_VOLTAGE_LOSS 67 +#define BCMXCP_ALARM_BATTERY_DC_OVER_VOLTAGE 68 +#define BCMXCP_ALARM_POWER_SUPPLY_OVER_TEMP 69 +#define BCMXCP_ALARM_POWER_SUPPLY_FAIL 70 +#define BCMXCP_ALARM_POWER_SUPPLY_5V_FAIL 71 +#define BCMXCP_ALARM_POWER_SUPPLY_12V_FAIL 72 +#define BCMXCP_ALARM_HEATSINK_OVER_TEMP 73 +#define BCMXCP_ALARM_HEATSINK_TEMP_SENSOR_FAIL 74 +#define BCMXCP_ALARM_RECTIFIER_CURRENT_OVER_125 75 +#define BCMXCP_ALARM_RECTIFIER_FAULT_INTERRUPT_FAIL 76 +#define BCMXCP_ALARM_RECTIFIER_POWER_CAPACITOR_FAIL 77 +#define BCMXCP_ALARM_RECTIFIER_POWER_CAPASITOR_FAIL 77 /* Legacy - typo */ +#define BCMXCP_ALARM_INVERTER_PROGRAM_STACK_ERROR 78 +#define BCMXCP_ALARM_INVERTER_BOARD_SELFTEST_FAIL 79 +#define BCMXCP_ALARM_INVERTER_AD_SELFTEST_FAIL 80 +#define BCMXCP_ALARM_INVERTER_RAM_SELFTEST_FAIL 81 +#define BCMXCP_ALARM_NV_MEMORY_CHECKSUM_FAIL 82 +#define BCMXCP_ALARM_PROGRAM_CHECKSUM_FAIL 83 +#define BCMXCP_ALARM_INVERTER_CPU_SELFTEST_FAIL 84 +#define BCMXCP_ALARM_NETWORK_NOT_RESPONDING 85 +#define BCMXCP_ALARM_FRONT_PANEL_SELFTEST_FAIL 86 +#define BCMXCP_ALARM_NODE_EEPROM_VERIFICATION_ERROR 87 +#define BCMXCP_ALARM_OUTPUT_AC_OVER_VOLT_TEST_FAIL 88 +#define BCMXCP_ALARM_OUTPUT_DC_OVER_VOLTAGE 89 +#define BCMXCP_ALARM_INPUT_PHASE_ROTATION_ERROR 90 +#define BCMXCP_ALARM_INVERTER_RAMP_UP_TEST_FAILED 91 +#define BCMXCP_ALARM_INVERTER_OFF_COMMAND 92 +#define BCMXCP_ALARM_INVERTER_ON_COMMAND 93 +#define BCMXCP_ALARM_TO_BYPASS_COMMAND 94 +#define BCMXCP_ALARM_FROM_BYPASS_COMMAND 95 +#define BCMXCP_ALARM_AUTO_MODE_COMMAND 96 +#define BCMXCP_ALARM_EMERGENCY_SHUTDOWN_COMMAND 97 +#define BCMXCP_ALARM_SETUP_SWITCH_OPEN 98 +#define BCMXCP_ALARM_INVERTER_OVER_VOLT_INT 99 +#define BCMXCP_ALARM_INVERTER_UNDER_VOLT_INT 100 +#define BCMXCP_ALARM_ABSOLUTE_DCOV_ACOV 101 +#define BCMXCP_ALARM_PHASE_A_CURRENT_LIMIT 102 +#define BCMXCP_ALARM_PHASE_B_CURRENT_LIMIT 103 +#define BCMXCP_ALARM_PHASE_C_CURRENT_LIMIT 104 +#define BCMXCP_ALARM_BYPASS_NOT_AVAILABLE 105 +#define BCMXCP_ALARM_RECTIFIER_BREAKER_OPEN 106 +#define BCMXCP_ALARM_BATTERY_CONTACTOR_OPEN 107 +#define BCMXCP_ALARM_INVERTER_CONTACTOR_OPEN 108 +#define BCMXCP_ALARM_BYPASS_BREAKER_OPEN 109 +#define BCMXCP_ALARM_INV_BOARD_ACOV_INT_TEST_FAIL 110 +#define BCMXCP_ALARM_INVERTER_OVER_TEMP_TRIP 111 +#define BCMXCP_ALARM_INV_BOARD_ACUV_INT_TEST_FAIL 112 +#define BCMXCP_ALARM_INVERTER_VOLTAGE_FEEDBACK_ERROR 113 +#define BCMXCP_ALARM_DC_UNDER_VOLTAGE_TIMEOUT 114 +#define BCMXCP_ALARM_AC_UNDER_VOLTAGE_TIMEOUT 115 +#define BCMXCP_ALARM_DC_UNDER_VOLTAGE_WHILE_CHARGE 116 +#define BCMXCP_ALARM_INVERTER_VOLTAGE_BIAS_ERROR 117 +#define BCMXCP_ALARM_RECTIFIER_PHASE_ROTATION 118 +#define BCMXCP_ALARM_BYPASS_PHASER_ROTATION 119 +#define BCMXCP_ALARM_SYSTEM_INTERFACE_BOARD_FAIL 120 +#define BCMXCP_ALARM_PARALLEL_BOARD_FAIL 121 +#define BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_A 122 +#define BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_B 123 +#define BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_C 124 +#define BCMXCP_ALARM_DC_OVER_VOLTAGE_TIMEOUT 125 +#define BCMXCP_ALARM_BATTERY_TOTALLY_DISCHARGED 126 +#define BCMXCP_ALARM_INVERTER_PHASE_BIAS_ERROR 127 +#define BCMXCP_ALARM_INVERTER_VOLTAGE_BIAS_ERROR_2 128 +#define BCMXCP_ALARM_DC_LINK_BLEED_COMPLETE 129 +#define BCMXCP_ALARM_LARGE_CHARGER_INPUT_CURRENT 130 +#define BCMXCP_ALARM_INV_VOLT_TOO_LOW_FOR_RAMP_LEVEL 131 +#define BCMXCP_ALARM_LOSS_OF_REDUNDANCY 132 +#define BCMXCP_ALARM_LOSS_OF_SYNC_BUS 133 +#define BCMXCP_ALARM_RECTIFIER_BREAKER_SHUNT_TRIP 134 +#define BCMXCP_ALARM_LOSS_OF_CHARGER_SYNC 135 +#define BCMXCP_ALARM_INVERTER_LOW_LEVEL_TEST_TIMEOUT 136 +#define BCMXCP_ALARM_OUTPUT_BREAKER_OPEN 137 +#define BCMXCP_ALARM_CONTROL_POWER_ON 138 +#define BCMXCP_ALARM_INVERTER_ON 139 +#define BCMXCP_ALARM_CHARGER_ON 140 +#define BCMXCP_ALARM_BYPASS_ON 141 +#define BCMXCP_ALARM_BYPASS_POWER_LOSS 142 +#define BCMXCP_ALARM_ON_MANUAL_BYPASS 143 +#define BCMXCP_ALARM_BYPASS_MANUAL_TURN_OFF 144 +#define BCMXCP_ALARM_INVERTER_BLEEDING_DC_LINK_VOLT 145 +#define BCMXCP_ALARM_CPU_ISR_ERROR 146 +#define BCMXCP_ALARM_SYSTEM_ISR_RESTART 147 +#define BCMXCP_ALARM_PARALLEL_DC 148 +#define BCMXCP_ALARM_BATTERY_NEEDS_SERVICE 149 +#define BCMXCP_ALARM_BATTERY_CHARGING 150 +#define BCMXCP_ALARM_BATTERY_NOT_CHARGED 151 +#define BCMXCP_ALARM_DISABLED_BATTERY_TIME 152 +#define BCMXCP_ALARM_SERIES_7000_ENABLE 153 +#define BCMXCP_ALARM_OTHER_UPS_ON 154 +#define BCMXCP_ALARM_PARALLEL_INVERTER 155 +#define BCMXCP_ALARM_UPS_IN_PARALLEL 156 +#define BCMXCP_ALARM_OUTPUT_BREAKER_REALY_FAIL 157 +#define BCMXCP_ALARM_CONTROL_POWER_OFF 158 +#define BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_A 159 +#define BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_B 160 +#define BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_C 161 +#define BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_A 162 +#define BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_B 163 +#define BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_C 164 +#define BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_A 165 +#define BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_B 166 +#define BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_C 167 +#define BCMXCP_ALARM_UPS_ON_BATTERY 168 +#define BCMXCP_ALARM_UPS_ON_BYPASS 169 +#define BCMXCP_ALARM_LOAD_DUMPED 170 +#define BCMXCP_ALARM_LOAD_ON_INVERTER 171 +#define BCMXCP_ALARM_UPS_ON_COMMAND 172 +#define BCMXCP_ALARM_UPS_OFF_COMMAND 173 +#define BCMXCP_ALARM_LOW_BATTERY_SHUTDOWN 174 +#define BCMXCP_ALARM_AUTO_ON_ENABLED 175 +#define BCMXCP_ALARM_SOFTWARE_INCOMPABILITY_DETECTED 176 +#define BCMXCP_ALARM_INVERTER_TEMP_SENSOR_FAILED 177 +#define BCMXCP_ALARM_DC_START_OCCURED 178 +#define BCMXCP_ALARM_IN_PARALLEL_OPERATION 179 +#define BCMXCP_ALARM_SYNCING_TO_BYPASS 180 +#define BCMXCP_ALARM_RAMPING_UPS_UP 181 +#define BCMXCP_ALARM_INVERTER_ON_DELAY 182 +#define BCMXCP_ALARM_CHARGER_ON_DELAY 183 +#define BCMXCP_ALARM_WAITING_FOR_UTIL_INPUT 184 +#define BCMXCP_ALARM_CLOSE_BYPASS_BREAKER 185 +#define BCMXCP_ALARM_TEMPORARY_BYPASS_OPERATION 186 +#define BCMXCP_ALARM_SYNCING_TO_OUTPUT 187 +#define BCMXCP_ALARM_BYPASS_FAILURE 188 +#define BCMXCP_ALARM_AUTO_OFF_COMMAND_EXECUTED 189 +#define BCMXCP_ALARM_AUTO_ON_COMMAND_EXECUTED 190 +#define BCMXCP_ALARM_BATTERY_TEST_FAILED 191 +#define BCMXCP_ALARM_FUSE_FAIL 192 +#define BCMXCP_ALARM_FAN_FAIL 193 +#define BCMXCP_ALARM_SITE_WIRING_FAULT 194 +#define BCMXCP_ALARM_BACKFEED_CONTACTOR_FAIL 195 +#define BCMXCP_ALARM_ON_BUCK 196 +#define BCMXCP_ALARM_ON_BOOST 197 +#define BCMXCP_ALARM_ON_DOUBLE_BOOST 198 +#define BCMXCP_ALARM_BATTERIES_DISCONNECTED 199 +#define BCMXCP_ALARM_UPS_CABINET_OVER_TEMP 200 +#define BCMXCP_ALARM_TRANSFORMER_OVER_TEMP 201 +#define BCMXCP_ALARM_AMBIENT_UNDER_TEMP 202 +#define BCMXCP_ALARM_AMBIENT_OVER_TEMP 203 +#define BCMXCP_ALARM_CABINET_DOOR_OPEN 204 +#define BCMXCP_ALARM_CABINET_DOOR_OPEN_VOLT_PRESENT 205 +#define BCMXCP_ALARM_AUTO_SHUTDOWN_PENDING 206 +#define BCMXCP_ALARM_TAP_SWITCHING_REALY_PENDING 207 +#define BCMXCP_ALARM_UNABLE_TO_CHARGE_BATTERIES 208 +#define BCMXCP_ALARM_STARTUP_FAILURE_CHECK_EPO 209 +#define BCMXCP_ALARM_AUTOMATIC_STARTUP_PENDING 210 +#define BCMXCP_ALARM_MODEM_FAILED 211 +#define BCMXCP_ALARM_INCOMING_MODEM_CALL_STARTED 212 +#define BCMXCP_ALARM_OUTGOING_MODEM_CALL_STARTED 213 +#define BCMXCP_ALARM_MODEM_CONNECTION_ESTABLISHED 214 +#define BCMXCP_ALARM_MODEM_CALL_COMPLETED_SUCCESS 215 +#define BCMXCP_ALARM_MODEM_CALL_COMPLETED_FAIL 216 +#define BCMXCP_ALARM_INPUT_BREAKER_FAIL 217 +#define BCMXCP_ALARM_SYSINIT_IN_PROGRESS 218 +#define BCMXCP_ALARM_AUTOCALIBRATION_FAIL 219 +#define BCMXCP_ALARM_SELECTIVE_TRIP_OF_MODULE 220 +#define BCMXCP_ALARM_INVERTER_OUTPUT_FAILURE 221 +#define BCMXCP_ALARM_ABNORMAL_OUTPUT_VOLT_AT_STARTUP 222 +#define BCMXCP_ALARM_RECTIFIER_OVER_TEMP 223 +#define BCMXCP_ALARM_CONFIG_ERROR 224 +#define BCMXCP_ALARM_REDUNDANCY_LOSS_DUE_TO_OVERLOAD 225 +#define BCMXCP_ALARM_ON_ALTERNATE_AC_SOURCE 226 +#define BCMXCP_ALARM_IN_HIGH_EFFICIENCY_MODE 227 +#define BCMXCP_ALARM_SYSTEM_NOTICE_ACTIVE 228 +#define BCMXCP_ALARM_SYSTEM_ALARM_ACTIVE 229 +#define BCMXCP_ALARM_ALTERNATE_POWER_SOURCE_NOT_AVAILABLE 230 +#define BCMXCP_ALARM_CURRENT_BALANCE_FAILURE 231 +#define BCMXCP_ALARM_CHECK_AIR_FILTER 232 +#define BCMXCP_ALARM_SUBSYSTEM_NOTICE_ACTIVE 233 +#define BCMXCP_ALARM_SUBSYSTEM_ALARM_ACTIVE 234 +#define BCMXCP_ALARM_CHARGER_ON_COMMAND 235 +#define BCMXCP_ALARM_CHARGER_OFF_COMMAND 236 +#define BCMXCP_ALARM_UPS_NORMAL 237 +#define BCMXCP_ALARM_INVERTER_PHASE_ROTATION 238 +#define BCMXCP_ALARM_UPS_OFF 239 +#define BCMXCP_ALARM_EXTERNAL_COMMUNICATION_FAILURE 240 +#define BCMXCP_ALARM_BATTERY_TEST_INPROGRESS 256 +#define BCMXCP_ALARM_SYSTEM_TEST_INPROGRESS 257 +#define BCMXCP_ALARM_BATTERY_TEST_ABORTED 258 -#define BCMXCP_METER_MAP_MAX 91 /* Max no of entries in BCM/XCP meter map */ -#define BCMXCP_ALARM_MAP_MAX 240 /* Max no of entries in BCM/XCP alarm map (adjusted upwards to nearest multi of 8 */ +#define BCMXCP_COMMAND_MAP_MAX 208 /* Max no of entries in BCM/XCP meter map (adjusted upwards to nearest multi of 8) */ +#define BCMXCP_METER_MAP_MAX 136 /* Max no of entries in BCM/XCP meter map (adjusted upwards to nearest multi of 8) */ +#define BCMXCP_ALARM_MAP_MAX 264 /* Max no of entries in BCM/XCP alarm map (adjusted upwards to nearest multi of 8) */ + +/* Return codes for XCP ACK block responses */ +#define BCMXCP_RETURN_ACCEPTED 0x31 /* Accepted and executed (or execution in progress) */ +#define BCMXCP_RETURN_NOT_IMPLEMENTED 0x32 /* Recognized but not implemented */ +#define BCMXCP_RETURN_BUSY 0x33 /* Recognized but not currently able to execute (busy) */ +#define BCMXCP_RETURN_UNRECOGNISED 0x34 /* Unrecognized -- e.g., command byte not in valid range, or command has been corrupted (bad checksum) */ +#define BCMXCP_RETURN_PARAMETER_OUT_OF_RANGE 0x35 /* Command recognized, but its Parameter value is out of range */ +#define BCMXCP_RETURN_INVALID_PARAMETER 0x36 /* Command recognized, but its Parameter is invalid (e.g., no such parameter, bad Outlet number) */ +#define BCMXCP_RETURN_ACCEPTED_PARAMETER_ADJUST 0x37 /* Accepted, with parameter adjusted to nearest good value */ +/*#define BCMXCP_RETURN_READONLY 0x38 */ /* Parameter is Read-only - cannot be written (at this privilege level) (this is not listed in spec document */ + +/* UPS status */ +#define BCMXCP_STATUS_ONLINE 0x50 +#define BCMXCP_STATUS_ONBATTERY 0xf0 +#define BCMXCP_STATUS_OVERLOAD 0xe0 +#define BCMXCP_STATUS_TRIM 0x63 +#define BCMXCP_STATUS_BOOST1 0x61 +#define BCMXCP_STATUS_BOOST2 0x62 +#define BCMXCP_STATUS_BYPASS 0x60 +#define BCMXCP_STATUS_OFF 0x10 + +/* UPS topology block info */ +#define BCMXCP_TOPOLOGY_NONE 0x0000 /* None; use the Table of Elements */ +#define BCMXCP_TOPOLOGY_OFFLINE_SWITCHER_1P 0x0010 /* Off-line switcher, Single Phase */ +#define BCMXCP_TOPOLOGY_LINEINT_UPS_1P 0x0020 /* Line-Interactive UPS, Single Phase */ +#define BCMXCP_TOPOLOGY_LINEINT_UPS_2P 0x0021 /* Line-Interactive UPS, Two Phase */ +#define BCMXCP_TOPOLOGY_LINEINT_UPS_3P 0x0022 /* Line-Interactive UPS, Three Phase */ +#define BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_1P 0x0030 /* Dual AC Input, On-Line UPS, Single Phase */ +#define BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_2P 0x0031 /* Dual AC Input, On-Line UPS, Two Phase */ +#define BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_3P 0x0032 /* Dual AC Input, On-Line UPS, Three Phase */ +#define BCMXCP_TOPOLOGY_ONLINE_UPS_1P 0x0040 /* On-Line UPS, Single Phase */ +#define BCMXCP_TOPOLOGY_ONLINE_UPS_2P 0x0041 /* On-Line UPS, Two Phase */ +#define BCMXCP_TOPOLOGY_ONLINE_UPS_3P 0x0042 /* On-Line UPS, Three Phase */ +#define BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_1P 0x0050 /* Parallel Redundant On-Line UPS, Single Phase */ +#define BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_2P 0x0051 /* Parallel Redundant On-Line UPS, Two Phase */ +#define BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_3P 0x0052 /* Parallel Redundant On-Line UPS, Three Phase */ +#define BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_1P 0x0060 /* Parallel for Capacity On-Line UPS, Single Phase */ +#define BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_2P 0x0061 /* Parallel for Capacity On-Line UPS, Two Phase */ +#define BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_3P 0x0062 /* Parallel for Capacity On-Line UPS, Three Phase */ +#define BCMXCP_TOPOLOGY_SYSTEM_BYPASS_MODULE_3P 0x0102 /* System Bypass Module, Three Phase */ +#define BCMXCP_TOPOLOGY_HOT_TIE_CABINET_3P 0x0122 /* Hot-Tie Cabinet, Three Phase */ +#define BCMXCP_TOPOLOGY_OUTLET_CONTROLLER_1P 0x0200 /* Outlet Controller, Single Phase */ +#define BCMXCP_TOPOLOGY_DUAL_AC_STATIC_SWITCH_3P 0x0222 /* Dual AC Input Static Switch Module, 3 Phase */ + +typedef struct { /* Entry in BCM/XCP - UPS mapping table */ + const char *command_desc; /* Description of this command */ + unsigned char command_byte; /* The command byte for this command, 0 = not supported */ +} BCMXCP_COMMAND_MAP_ENTRY_t; + +extern BCMXCP_COMMAND_MAP_ENTRY_t bcmxcp_command_map[BCMXCP_COMMAND_MAP_MAX]; typedef struct { /* Entry in BCM/XCP - UPS - NUT mapping table */ - const char *nut_entity; /* The NUT variable name */ - unsigned char format; /* The format of the data - float, long etc */ - unsigned int meter_block_index; /* The position of this meter in the UPS meter block */ -} BCMXCP_METER_MAP_ENTRY_t; + const char *nut_entity; /* The NUT variable name */ + unsigned char format; /* The format of the data - float, long etc */ + unsigned int meter_block_index; /* The position of this meter in the UPS meter block */ +} BCMXCP_METER_MAP_ENTRY_t; -BCMXCP_METER_MAP_ENTRY_t - bcmxcp_meter_map[BCMXCP_METER_MAP_MAX]; +extern BCMXCP_METER_MAP_ENTRY_t bcmxcp_meter_map[BCMXCP_METER_MAP_MAX]; -typedef struct { /* Entry in BCM/XCP - UPS mapping table */ - int alarm_block_index; /* Index of this alarm in alarm block. -1 = not existing */ - const char *alarm_desc; /* Description of this alarm */ -} BCMXCP_ALARM_MAP_ENTRY_t; +typedef struct { /* Entry in BCM/XCP - UPS mapping table */ + int alarm_block_index; /* Index of this alarm in alarm block. -1 = not existing */ + const char *alarm_desc; /* Description of this alarm */ +} BCMXCP_ALARM_MAP_ENTRY_t; -BCMXCP_ALARM_MAP_ENTRY_t - bcmxcp_alarm_map[BCMXCP_ALARM_MAP_MAX]; +extern BCMXCP_ALARM_MAP_ENTRY_t bcmxcp_alarm_map[BCMXCP_ALARM_MAP_MAX]; -typedef struct { /* A place to store status info and other data not for NUT */ - unsigned char topology_mask; /* Configuration block byte 16, masks valid status bits */ - unsigned int lowbatt; /* Seconds of runtime left left when LB alarm is set */ - unsigned int shutdowndelay; /* Shutdown delay in seconds, from ups.conf */ - int alarm_on_battery; /* On Battery alarm active? */ - int alarm_low_battery; /* Battery Low alarm active? */ -} BCMXCP_STATUS_t; +typedef struct { /* A place to store status info and other data not for NUT */ + unsigned char topology_mask; /* Configuration block byte 16, masks valid status bits */ + unsigned int lowbatt; /* Seconds of runtime left left when LB alarm is set */ + unsigned int shutdowndelay; /* Shutdown delay in seconds, from ups.conf */ + int alarm_on_battery; /* On Battery alarm active? */ + int alarm_low_battery; /* Battery Low alarm active? */ + int alarm_replace_battery; /* Battery needs replacement! */ +} BCMXCP_STATUS_t; -BCMXCP_STATUS_t - bcmxcp_status; +extern BCMXCP_STATUS_t bcmxcp_status; int checksum_test(const unsigned char*); unsigned char calc_checksum(const unsigned char *buf); - + +/* from usbhid-ups.h */ +typedef struct { + const long xcp_value; /* XCP value */ + const char *nut_value; /* NUT value */ + const char *(*fun)(double xcp_value); /* optional XCP to NUT mapping */ + double (*nuf)(const char *nut_value); /* optional NUT to HID mapping */ +} info_lkp_t; + +/* use explicit booleans */ +#ifndef FALSE +typedef enum ebool { FALSE, TRUE } bool_t; +#else +typedef int bool_t; +#endif + #endif /*_POWERWARE_H */ diff --git a/drivers/bcmxcp_io.h b/drivers/bcmxcp_io.h index 15495f8..5293e05 100644 --- a/drivers/bcmxcp_io.h +++ b/drivers/bcmxcp_io.h @@ -1,17 +1,17 @@ -/* +/* * bcmxcp_io.h -- header for BCM/XCP IO module - */ + */ #ifndef BCMXCP_IO__ #define BCMXCP_IO__ -#include "main.h" /* for usbdrv_info_t */ +#include "main.h" /* for usbdrv_info_t */ void send_read_command(unsigned char command); -void send_write_command(unsigned char *command, int command_length); -int get_answer(unsigned char *data, unsigned char command); -int command_read_sequence(unsigned char command, unsigned char *data); -int command_write_sequence(unsigned char *command, int command_length, unsigned char *answer); +void send_write_command(unsigned char *command, size_t command_length); +ssize_t get_answer(unsigned char *data, unsigned char command); +ssize_t command_read_sequence(unsigned char command, unsigned char *data); +ssize_t command_write_sequence(unsigned char *command, size_t command_length, unsigned char *answer); void upsdrv_initups(void); void upsdrv_cleanup(void); void upsdrv_reconnect(void); diff --git a/drivers/bcmxcp_ser.c b/drivers/bcmxcp_ser.c index 0c7e6b3..58595f3 100644 --- a/drivers/bcmxcp_ser.c +++ b/drivers/bcmxcp_ser.c @@ -1,12 +1,12 @@ #include "main.h" #include "bcmxcp.h" #include "bcmxcp_io.h" +#include "bcmxcp_ser.h" #include "serial.h" +#include "nut_stdint.h" -#define PW_MAX_BAUD 5 - -#define SUBDRIVER_NAME "RS-232 communication subdriver" -#define SUBDRIVER_VERSION "0.18" +#define SUBDRIVER_NAME "RS-232 communication subdriver" +#define SUBDRIVER_VERSION "0.21" /* communication driver description structure */ upsdrv_info_t comm_upsdrv_info = { @@ -17,23 +17,32 @@ upsdrv_info_t comm_upsdrv_info = { { NULL } }; -struct pw_baud_rate { - int rate; - int name; -} pw_baud_rates[] = { - { B1200, 1200 }, - { B2400, 2400 }, - { B4800, 4800 }, - { B9600, 9600 }, +#define PW_MAX_BAUD 5 + +/* NOT static: also used from nut-scanner, so extern'ed via bcmxcp_ser.h */ +pw_baud_rate_t pw_baud_rates[] = { { B19200, 19200 }, + { B9600, 9600 }, + { B4800, 4800 }, + { B2400, 2400 }, + { B1200, 1200 }, + /* end of structure. */ + { 0, 0 } }; -unsigned char AUT[4] = {0xCF, 0x69, 0xE8, 0xD5}; /* Autorisation command */ +/* NOT static: also used from nut-scanner, so extern'ed via bcmxcp_ser.h */ +unsigned char BCMXCP_AUTHCMD[4] = {0xCF, 0x69, 0xE8, 0xD5}; /* Authorisation command */ -static void send_command(unsigned char *command, int command_length) +static void send_command(unsigned char *command, size_t command_length) { - int retry = 0, sent; - unsigned char sbuf[128]; + int retry = 0; + ssize_t sent; + unsigned char sbuf[1024]; + + if (command_length > UCHAR_MAX) { + upsdebugx (3, "%s: ERROR: command_length too long for the character protocol", __func__); + return; + } /* Prepare the send buffer */ sbuf[0] = PW_COMMAND_START_BYTE; @@ -45,16 +54,23 @@ static void send_command(unsigned char *command, int command_length) sbuf[command_length] = calc_checksum(sbuf); command_length += 1; + upsdebug_hex (3, "send_command", sbuf, command_length); + while (retry++ < PW_MAX_TRY) { if (retry == PW_MAX_TRY) { - ser_send_char(upsfd, 0x1d); /* last retry is preceded by a ESC.*/ + ser_send_char(upsfd, 0x1d); /* last retry is preceded by a ESC.*/ usleep(250000); } sent = ser_send_buf(upsfd, sbuf, command_length); - if (sent == command_length) { + if (sent < 0) { + upslogx(LOG_ERR, "%s(): error reading from ser_send_buf()", __func__); + return; + } + + if ((size_t)sent == command_length) { return; } } @@ -65,87 +81,90 @@ void send_read_command(unsigned char command) send_command(&command, 1); } -void send_write_command(unsigned char *command, int command_length) +void send_write_command(unsigned char *command, size_t command_length) { send_command(command, command_length); } /* get the answer of a command from the ups. And check that the answer is for this command */ -int get_answer(unsigned char *data, unsigned char command) +ssize_t get_answer(unsigned char *data, unsigned char command) { - unsigned char my_buf[128]; /* packet has a maximum length of 121+5 bytes */ - int length, end_length = 0, res, endblock = 0, start = 0; + unsigned char my_buf[128]; /* packet has a maximum length of 121+5 bytes */ + ssize_t res; + size_t length, end_length = 0, endblock = 0, start = 0; unsigned char block_number, sequence, pre_sequence = 0; - while (endblock != 1){ + while (endblock != 1) { do { /* Read PW_COMMAND_START_BYTE byte */ res = ser_get_char(upsfd, my_buf, 1, 0); if (res != 1) { - upsdebugx(1,"Receive error (PW_COMMAND_START_BYTE): %d, cmd=%x!!!\n", res, command); + upsdebugx(1, + "Receive error (PW_COMMAND_START_BYTE): %zd, cmd=%x!!!\n", + res, command); return -1; } start++; } while ((my_buf[0] != PW_COMMAND_START_BYTE) && (start < 128)); - + if (start == 128) { ser_comm_fail("Receive error (PW_COMMAND_START_BYTE): packet not on start!!%x\n", my_buf[0]); return -1; } /* Read block number byte */ - res = ser_get_char(upsfd, my_buf+1, 1, 0); + res = ser_get_char(upsfd, my_buf + 1, 1, 0); if (res != 1) { - ser_comm_fail("Receive error (Block number): %d!!!\n", res); + ser_comm_fail("Receive error (Block number): %zd!!!\n", res); return -1; } block_number = (unsigned char)my_buf[1]; if (command <= 0x43) { - if ((command - 0x30) != block_number){ + if ((command - 0x30) != block_number) { ser_comm_fail("Receive error (Request command): %x!!!\n", block_number); return -1; } } if (command >= 0x89) { - if ((command == 0xA0) && (block_number != 0x01)){ + if ((command == 0xA0) && (block_number != 0x01)) { ser_comm_fail("Receive error (Requested only mode command): %x!!!\n", block_number); return -1; } - if ((command != 0xA0) && (block_number != 0x09)){ + if ((command != 0xA0) && (block_number != 0x09)) { ser_comm_fail("Receive error (Control command): %x!!!\n", block_number); return -1; } } /* Read data length byte */ - res = ser_get_char(upsfd, my_buf+2, 1, 0); + res = ser_get_char(upsfd, my_buf + 2, 1, 0); if (res != 1) { - ser_comm_fail("Receive error (length): %d!!!\n", res); + ser_comm_fail("Receive error (length): %zd!!!\n", res); return -1; } length = (unsigned char)my_buf[2]; if (length < 1) { - ser_comm_fail("Receive error (length): packet length %x!!!\n", length); + ser_comm_fail("Receive error (length): packet length %zx!!!\n", length); return -1; } /* Read sequence byte */ - res = ser_get_char(upsfd, my_buf+3, 1, 0); + res = ser_get_char(upsfd, my_buf + 3, 1, 0); if (res != 1) { - ser_comm_fail("Receive error (sequence): %d!!!\n", res); + ser_comm_fail("Receive error (sequence): %zd!!!\n", res); return -1; } @@ -162,19 +181,23 @@ int get_answer(unsigned char *data, unsigned char command) pre_sequence = sequence; - /* Try to read all the remainig bytes */ - res = ser_get_buf_len(upsfd, my_buf+4, length, 1, 0); + /* Try to read all the remaining bytes */ + res = ser_get_buf_len(upsfd, my_buf + 4, length, 1, 0); + if (res < 0) { + ser_comm_fail("%s(): ser_get_buf_len() returned error code %zd", __func__, res); + return res; + } - if (res != length) { - ser_comm_fail("Receive error (data): got %d bytes instead of %d!!!\n", res, length); + if ((size_t)res != length) { + ser_comm_fail("Receive error (data): got %zd bytes instead of %zu!!!\n", res, length); return -1; } /* Get the checksum byte */ - res = ser_get_char(upsfd, my_buf+(4+length), 1, 0); + res = ser_get_char(upsfd, my_buf + (4 + length), 1, 0); if (res != 1) { - ser_comm_fail("Receive error (checksum): %x!!!\n", res); + ser_comm_fail("Receive error (checksum): %zx!!!\n", res); return -1; } @@ -184,19 +207,22 @@ int get_answer(unsigned char *data, unsigned char command) return -1; } - memcpy(data+end_length, my_buf+4, length); + memcpy(data+end_length, my_buf + 4, length); end_length += length; } + upsdebug_hex (5, "get_answer", data, end_length); ser_comm_good(); - return end_length; + assert(end_length < SSIZE_MAX); + return (ssize_t)end_length; } -static int command_sequence(unsigned char *command, int command_length, unsigned char *answer) +static ssize_t command_sequence(unsigned char *command, size_t command_length, unsigned char *answer) { - int bytes_read, retry = 0; + ssize_t bytes_read; + int retry = 0; while (retry++ < PW_MAX_TRY) { @@ -217,9 +243,9 @@ static int command_sequence(unsigned char *command, int command_length, unsigned } /* Sends a single command (length=1). and get the answer */ -int command_read_sequence(unsigned char command, unsigned char *answer) +ssize_t command_read_sequence(unsigned char command, unsigned char *answer) { - int bytes_read; + ssize_t bytes_read; bytes_read = command_sequence(&command, 1, answer); @@ -231,9 +257,9 @@ int command_read_sequence(unsigned char command, unsigned char *answer) } /* Sends a setup command (length > 1) */ -int command_write_sequence(unsigned char *command, int command_length, unsigned char *answer) +ssize_t command_write_sequence(unsigned char *command, size_t command_length, unsigned char *answer) { - int bytes_read; + ssize_t bytes_read; bytes_read = command_sequence(command, command_length, answer); @@ -249,18 +275,80 @@ void upsdrv_comm_good() ser_comm_good(); } -void pw_comm_setup(const char *port) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) ) +# pragma GCC diagnostic push +#endif + +#if (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#if (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +static void pw_comm_setup(const char *port) { - unsigned char command = PW_SET_REQ_ONLY_MODE; - unsigned char id_command = PW_ID_BLOCK_REQ; - unsigned char answer[256]; - int i = 0, baud, mybaud = 0, ret = -1; + unsigned char command = PW_SET_REQ_ONLY_MODE; + unsigned char id_command = PW_ID_BLOCK_REQ; + unsigned char answer[256]; + int i = 0; + ssize_t ret = -1; + speed_t mybaud = 0, baud; if (getval("baud_rate") != NULL) { - baud = atoi(getval("baud_rate")); - - for(i = 0; i < PW_MAX_BAUD; i++) { + int br = atoi(getval("baud_rate")); + /* Note that atoi() behavior on erroneous input is undefined */ + if (br < 0) { + upslogx(LOG_ERR, "baud_rate option is invalid"); + return; + } + + /* FIXME: speed_t does not define a SPEED_MAX value nor + * guarantee that it is an int (just a typedef from + * termios.h happens to say that on some systems)... + * But since we convert this setting from int, we assume... + */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +/* Note for gating macros above: unsuffixed HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP + * means support of contexts both inside and outside function body, so the push + * above and pop below (outside this finction) are not used. + */ +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +/* Note that the individual warning pragmas for use inside function bodies + * are named without a _INSIDEFUNC suffix, for simplicity and legacy reasons + */ +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + switch(sizeof(speed_t)) { + case 8: assert (br < INT64_MAX); break; + case 4: assert (br < INT32_MAX); break; + case 2: assert (br < INT16_MAX); break; + default: assert (br < INT_MAX); + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif + baud = (speed_t)br; + + for (i = 0; i < PW_MAX_BAUD; i++) { if (baud == pw_baud_rates[i].name) { mybaud = pw_baud_rates[i].rate; break; @@ -272,9 +360,9 @@ void pw_comm_setup(const char *port) } ser_set_speed(upsfd, device_path, mybaud); - ser_send_char(upsfd, 0x1d); /* send ESC to take it out of menu */ + ser_send_char(upsfd, 0x1d); /* send ESC to take it out of menu */ usleep(90000); - send_write_command(AUT, 4); + send_write_command(BCMXCP_AUTHCMD, 4); usleep(500000); ret = command_sequence(&command, 1, answer); if (ret <= 0) { @@ -283,11 +371,14 @@ void pw_comm_setup(const char *port) } if (ret > 0) { - upslogx(LOG_INFO, "Connected to UPS on %s with baudrate %d", port, baud); + /* Cast baud into max length unsigned, despite the POSIX + * standard some systems vary in definition of this type + */ + upslogx(LOG_INFO, "Connected to UPS on %s with baudrate %llu", port, (unsigned long long int)baud); return; } - upslogx(LOG_ERR, "No response from UPS on %s with baudrate %d", port, baud); + upslogx(LOG_ERR, "No response from UPS on %s with baudrate %llu", port, (unsigned long long int)baud); } upslogx(LOG_INFO, "Attempting to autodect baudrate"); @@ -295,9 +386,9 @@ void pw_comm_setup(const char *port) for (i=0; i 0) { - upslogx(LOG_INFO, "Connected to UPS on %s with baudrate %d", port, pw_baud_rates[i].name); + upslogx(LOG_INFO, "Connected to UPS on %s with baudrate %zu", port, pw_baud_rates[i].name); return; } - upsdebugx(2, "No response from UPS on %s with baudrate %d", port, pw_baud_rates[i].name); + upsdebugx(2, "No response from UPS on %s with baudrate %zu", port, pw_baud_rates[i].name); } fatalx(EXIT_FAILURE, "Can't connect to the UPS on port %s!\n", port); } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) ) +# pragma GCC diagnostic pop +#endif void upsdrv_initups(void) { @@ -331,3 +425,4 @@ void upsdrv_cleanup(void) void upsdrv_reconnect(void) { } + diff --git a/drivers/bcmxcp_ser.h b/drivers/bcmxcp_ser.h new file mode 100644 index 0000000..1f3f880 --- /dev/null +++ b/drivers/bcmxcp_ser.h @@ -0,0 +1,22 @@ +/* + * bcmxcp_ser.h -- header for BCM/XCP RS-232 module + */ + +#ifndef BCMXCP_SER__ +#define BCMXCP_SER__ + +#include "serial.h" /* pulls termios.h to define speed_t */ + +/* This header is needed for this line, to avoid warnings about it not + * being static in C file (can't hide, is also needed by nut-scanner) + */ +extern unsigned char BCMXCP_AUTHCMD[4]; + +typedef struct { + speed_t rate; /* Value like B19200 defined in termios.h; note: NOT the bitrate numerically */ + size_t name; /* Actual rate... WHY is this "name" - number to print interactively? */ +} pw_baud_rate_t; + +extern pw_baud_rate_t pw_baud_rates[]; + +#endif /* BCMXCP_SER__ */ diff --git a/drivers/bcmxcp_usb.c b/drivers/bcmxcp_usb.c index 8c30a4d..e219714 100644 --- a/drivers/bcmxcp_usb.c +++ b/drivers/bcmxcp_usb.c @@ -9,10 +9,9 @@ #include #include #include -#include -#define SUBDRIVER_NAME "USB communication subdriver" -#define SUBDRIVER_VERSION "0.19" +#define SUBDRIVER_NAME "USB communication subdriver" +#define SUBDRIVER_VERSION "0.27" /* communication driver description structure */ upsdrv_info_t comm_upsdrv_info = { @@ -26,13 +25,15 @@ upsdrv_info_t comm_upsdrv_info = { #define MAX_TRY 4 /* Powerware */ -#define POWERWARE 0x0592 +#define POWERWARE 0x0592 /* Phoenixtec Power Co., Ltd */ -#define PHOENIXTEC 0x06da +#define PHOENIXTEC 0x06da /* Hewlett Packard */ -#define HP_VENDORID 0x03f0 +#define HP_VENDORID 0x03f0 + +static USBDevice_t curDevice; /* USB functions */ usb_dev_handle *nutusb_open(const char *port); @@ -42,27 +43,36 @@ void nutusb_comm_fail(const char *fmt, ...) __attribute__ ((__format__ (__printf__, 1, 2))); void nutusb_comm_good(void); /* function pointer, set depending on which device is used */ -int (*usb_set_descriptor)(usb_dev_handle *udev, unsigned char type, - unsigned char index, void *buf, int size); +/* FIXME? Use usb_ctrl_* typedefs*/ +static int (*usb_set_descriptor)(usb_dev_handle *udev, unsigned char type, + unsigned char index, void *buf, size_t size); /* usb_set_descriptor() for Powerware devices */ -static int usb_set_powerware(usb_dev_handle *udev, unsigned char type, unsigned char index, void *buf, int size) +/* FIXME? Use usb_ctrl_* typedefs*/ +static int usb_set_powerware(usb_dev_handle *udev, unsigned char type, unsigned char index, void *buf, size_t size) { - return usb_control_msg(udev, USB_ENDPOINT_OUT, USB_REQ_SET_DESCRIPTOR, (type << 8) + index, 0, buf, size, 1000); + assert (size < INT_MAX); + return usb_control_msg(udev, USB_ENDPOINT_OUT, USB_REQ_SET_DESCRIPTOR, (type << 8) + index, 0, buf, (int)size, 1000); } -static void *powerware_ups(void) { +static void *powerware_ups(USBDevice_t *device) { + NUT_UNUSED_VARIABLE(device); usb_set_descriptor = &usb_set_powerware; return NULL; } /* usb_set_descriptor() for Phoenixtec devices */ -static int usb_set_phoenixtec(usb_dev_handle *udev, unsigned char type, unsigned char index, void *buf, int size) +/* FIXME? Use usb_ctrl_* typedefs*/ +static int usb_set_phoenixtec(usb_dev_handle *udev, unsigned char type, unsigned char index, void *buf, size_t size) { - return usb_control_msg(udev, 0x42, 0x0d, (0x00 << 8) + 0x0, 0, buf, size, 1000); + NUT_UNUSED_VARIABLE(index); + NUT_UNUSED_VARIABLE(type); + assert (size < INT_MAX); + return usb_control_msg(udev, 0x42, 0x0d, (0x00 << 8) + 0x0, 0, buf, (int)size, 1000); } -static void *phoenixtec_ups(void) { +static void *phoenixtec_ups(USBDevice_t *device) { + NUT_UNUSED_VARIABLE(device); usb_set_descriptor = &usb_set_phoenixtec; return NULL; } @@ -79,20 +89,20 @@ static usb_device_id_t pw_usb_device_table[] = { { USB_DEVICE(HP_VENDORID, 0x1f01), &phoenixtec_ups }, /* T750 */ { USB_DEVICE(HP_VENDORID, 0x1f02), &phoenixtec_ups }, - + /* Terminating entry */ - { -1, -1, NULL } + { 0, 0, NULL } }; /* limit the amount of spew that goes in the syslog when we lose the UPS */ -#define USB_ERR_LIMIT 10 /* start limiting after 10 in a row */ -#define USB_ERR_RATE 10 /* then only print every 10th error */ -#define XCP_USB_TIMEOUT 5000 +#define USB_ERR_LIMIT 10 /* start limiting after 10 in a row */ +#define USB_ERR_RATE 10 /* then only print every 10th error */ +#define XCP_USB_TIMEOUT 5000 /* in msec */ /* global variables */ -usb_dev_handle *upsdev = NULL; -extern int exit_flag; -static unsigned int comm_failures = 0; +static usb_dev_handle *upsdev = NULL; +extern int exit_flag; +static unsigned int comm_failures = 0; /* Functions implementations */ void send_read_command(unsigned char command) @@ -104,11 +114,12 @@ void send_read_command(unsigned char command) buf[1] = 0x01; /* data length */ buf[2] = command; /* command to send */ buf[3] = calc_checksum(buf); /* checksum */ + upsdebug_hex (3, "send_read_command", buf, 4); usb_set_descriptor(upsdev, USB_DT_STRING, 4, buf, 4); /* FIXME: Ignore error */ } } -void send_write_command(unsigned char *command, int command_length) +void send_write_command(unsigned char *command, size_t command_length) { unsigned char sbuf[128]; @@ -122,46 +133,74 @@ void send_write_command(unsigned char *command, int command_length) /* Add checksum */ sbuf[command_length] = calc_checksum(sbuf); command_length += 1; + upsdebug_hex (3, "send_write_command", sbuf, command_length); usb_set_descriptor(upsdev, USB_DT_STRING, 4, sbuf, command_length); /* FIXME: Ignore error */ } } +#define PW_HEADER_SIZE (PW_HEADER_LENGTH + 1) +#define PW_CMD_BUFSIZE 256 /* get the answer of a command from the ups. And check that the answer is for this command */ -int get_answer(unsigned char *data, unsigned char command) +ssize_t get_answer(unsigned char *data, unsigned char command) { - unsigned char buf[1024], *my_buf = buf; - int length, end_length, res, endblock, bytes_read, ellapsed_time; + unsigned char buf[PW_CMD_BUFSIZE], *my_buf = buf; + ssize_t res; + int endblock, need_data; + long elapsed_time; /* milliseconds */ + ssize_t tail; + size_t bytes_read, end_length, length; unsigned char block_number, sequence, seq_num; struct timeval start_time, now; if (upsdev == NULL) return -1; - length = 1; /* non zero to enter the read loop */ - end_length = 0; /* total length of sequence(s), not counting header(s) */ - endblock = 0; /* signal the last sequence in the block */ - bytes_read = 0; /* total length of data read, including XCP header */ + need_data = PW_HEADER_SIZE; /* 4 - cmd response header length, 1 for csum */ + end_length = 0; /* total length of sequence(s), not counting header(s) */ + endblock = 0; /* signal the last sequence in the block */ + bytes_read = 0; /* total length of data read, including XCP header */ res = 0; - ellapsed_time = 0; - seq_num = 1; /* current theoric sequence */ + elapsed_time = 0; + seq_num = 1; /* current theoric sequence */ upsdebugx(1, "entering get_answer(%x)", command); /* Store current time */ gettimeofday(&start_time, NULL); + memset(&buf, 0x0, PW_CMD_BUFSIZE); - while ( (!endblock) && ((XCP_USB_TIMEOUT - ellapsed_time) > 0) ) { +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + /* Stay ahead of possible redefinitions... */ + assert (XCP_USB_TIMEOUT < INT_MAX); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic pop +#endif + + while ( (!endblock) && ((XCP_USB_TIMEOUT - elapsed_time) > 0) ) { /* Get (more) data if needed */ - if ((length - bytes_read) > 0) { - res = usb_interrupt_read(upsdev, 0x81, - (char *)&buf[bytes_read], - (PW_ANSWER_MAX_SIZE - bytes_read), - (XCP_USB_TIMEOUT - ellapsed_time)); + if (need_data > 0) { + res = usb_interrupt_read(upsdev, + 0x81, + (usb_ctrl_charbuf) buf + bytes_read, + 128, + (int)(XCP_USB_TIMEOUT - elapsed_time)); /* Update time */ gettimeofday(&now, NULL); - ellapsed_time = (now.tv_sec - start_time.tv_sec)*1000 + + elapsed_time = (now.tv_sec - start_time.tv_sec)*1000 + (now.tv_usec - start_time.tv_usec)/1000; /* Check libusb return value */ @@ -179,58 +218,50 @@ int get_answer(unsigned char *data, unsigned char command) /* FIXME: */ continue; } - - /* Else, we got some input bytes */ - bytes_read += res; + /* Else, we got some input bytes */ + bytes_read += (size_t)res; + need_data -= res; upsdebug_hex(1, "get_answer", buf, bytes_read); } + if (need_data > 0) /* We need more data */ + continue; + /* Now validate XCP frame */ /* Check header */ - if ( my_buf[0] != 0xAB ) { - upsdebugx(2, "get_answer: wrong header"); - return -1; + if ( my_buf[0] != PW_COMMAND_START_BYTE ) { + upsdebugx(2, "get_answer: wrong header 0xab vs %02x", my_buf[0]); + /* Sometime we read something wrong. bad cables? bad ports? */ + my_buf = memchr(my_buf, PW_COMMAND_START_BYTE, bytes_read); + if (!my_buf) + return -1; } - /* These validations seem not needed! */ /* Read block number byte */ block_number = my_buf[1]; upsdebugx(1, "get_answer: block_number = %x", block_number); -#if 0 - if (command <= 0x43) { - if ((command - 0x30) != block_number){ - nutusb_comm_fail("Receive error (Request command): BLOCK: %x (instead of %x), COMMAND: %x!\n", - block_number, (command - 0x30), command); - return -1; - } - } - - if (command >= 0x89) { - if ((command == 0xA0) && (block_number != 0x01)){ - nutusb_comm_fail("Receive error (Request command): BLOCK: %x (instead of 0x01), COMMAND: %x!\n", block_number, command); - return -1; - } - else if ((command != 0xA0) && (block_number != 0x09)){ - nutusb_comm_fail("Receive error (Request command): BLOCK: %x (instead of 0x09), COMMAND: %x!\n", block_number, command); - return -1; - } - } -#endif /* if 0 */ /* Check data length byte (remove the header length) */ length = my_buf[2]; - upsdebugx(3, "get_answer: data length = %d", length); - if ((bytes_read - 5) < length) { - upsdebugx(2, "get_answer: need to read %d more data", length - (bytes_read - 5)); + upsdebugx(3, "get_answer: data length = %zu", length); + if (bytes_read < (length + PW_HEADER_SIZE)) { + if (need_data < 0) --need_data; /* count zerro byte too */ + need_data += length + 1; /* packet lenght + checksum */ + upsdebugx(2, "get_answer: need to read %d more data", need_data); continue; } /* Check if Length conforms to XCP (121 for normal, 140 for Test mode) */ /* Use the more generous length for testing */ - if (length > 140 ) { + if (length > 140) { upsdebugx(2, "get_answer: bad length"); return -1; } + if (bytes_read >= SSIZE_MAX) { + upsdebugx(2, "get_answer: bad length (incredibly large read)"); + return -1; + } + /* Test the Sequence # */ sequence = my_buf[3]; if ((sequence & PW_SEQ_MASK) != seq_num) { @@ -258,24 +289,40 @@ int get_answer(unsigned char *data, unsigned char command) } else { seq_num++; + upsdebugx(2, "get_answer: next sequence is %d", seq_num); } /* copy the current valid XCP frame back */ - memcpy(data+end_length, my_buf+4, length); + memcpy(data+end_length, my_buf + 4, length); /* increment pointers to process the next sequence */ end_length += length; - my_buf += length + 5; + + /* Work around signedness of comparison result, SSIZE_MAX checked above: */ + tail = (ssize_t)bytes_read; + tail -= (ssize_t)(length + PW_HEADER_SIZE); + if (tail > 0) + my_buf = memmove(&buf[0], my_buf + length + PW_HEADER_SIZE, (size_t)tail); + else if (tail == 0) + my_buf = &buf[0]; + else { /* if (tail < 0) */ + upsdebugx(1, "get_answer(): did not expect to get negative tail size: %zd", tail); + return -1; + } + + bytes_read = (size_t)tail; } - upsdebugx(4, "get_answer: exiting (len=%d)", end_length); - return end_length; + + upsdebug_hex (5, "get_answer", data, end_length); + assert (end_length < SSIZE_MAX); + return (ssize_t)end_length; } /* Sends a single command (length=1). and get the answer */ -int command_read_sequence(unsigned char command, unsigned char *data) +ssize_t command_read_sequence(unsigned char command, unsigned char *data) { - int bytes_read = 0; - int retry = 0; - + ssize_t bytes_read = 0; + size_t retry = 0; + while ((bytes_read < 1) && (retry < 5)) { send_read_command(command); bytes_read = get_answer(data, command); @@ -292,13 +339,14 @@ int command_read_sequence(unsigned char command, unsigned char *data) } /* Sends a setup command (length > 1) */ -int command_write_sequence(unsigned char *command, int command_length, unsigned char *answer) +ssize_t command_write_sequence(unsigned char *command, size_t command_length, unsigned char *answer) { - int bytes_read = 0; - int retry = 0; + ssize_t bytes_read = 0; + size_t retry = 0; while ((bytes_read < 1) && (retry < 5)) { send_write_command(command, command_length); + sleep(PW_SLEEP); bytes_read = get_answer(answer, command[0]); retry ++; } @@ -327,17 +375,28 @@ void upsdrv_cleanup(void) { upslogx(LOG_ERR, "CLOSING\n"); nutusb_close(upsdev, "USB"); + free(curDevice.Vendor); + free(curDevice.Product); + free(curDevice.Serial); + free(curDevice.Bus); + free(curDevice.Device); } void upsdrv_reconnect(void) { - upslogx(LOG_WARNING, "RECONNECT USB DEVICE\n"); + upsdebugx(4, "=================================================="); + upsdebugx(4, "= device has been disconnected, try to reconnect ="); + upsdebugx(4, "=================================================="); + nutusb_close(upsdev, "USB"); upsdev = NULL; upsdrv_initups(); } /* USB functions */ +static void nutusb_open_error(const char *port) + __attribute__((noreturn)); + static void nutusb_open_error(const char *port) { printf("Unable to find POWERWARE UPS device on USB bus (%s)\n\n", port); @@ -353,25 +412,82 @@ static void nutusb_open_error(const char *port) /* FIXME: this part of the opening can go into common... */ static usb_dev_handle *open_powerware_usb(void) { - struct usb_bus *busses = usb_get_busses(); +#if WITH_LIBUSB_1_0 + libusb_device **devlist; + ssize_t devcount = 0; + libusb_device_handle *udev; + struct libusb_device_descriptor dev_desc; + uint8_t bus; + int i; + + devcount = libusb_get_device_list(NULL, &devlist); + if (devcount <= 0) + fatal_with_errno(EXIT_FAILURE, "No USB device found"); + + for (i = 0; i < devcount; i++) { + + libusb_device *device = devlist[i]; + libusb_get_device_descriptor(device, &dev_desc); + + if (dev_desc.bDeviceClass != LIBUSB_CLASS_PER_INTERFACE) { + continue; + } + + curDevice.VendorID = dev_desc.idVendor; + curDevice.ProductID = dev_desc.idProduct; + bus = libusb_get_bus_number(device); + curDevice.Bus = (char *)malloc(4); + if (curDevice.Bus == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + sprintf(curDevice.Bus, "%03d", bus); + + /* FIXME: we should also retrieve + * dev->descriptor.iManufacturer + * dev->descriptor.iProduct + * dev->descriptor.iSerialNumber + * as in libusb.c->libusb_open() + * This is part of the things to put in common... */ + + if (is_usb_device_supported(pw_usb_device_table, &curDevice) == SUPPORTED) { + libusb_open(device, &udev); + libusb_free_device_list(devlist, 1); + return udev; + } + } + libusb_free_device_list(devlist, 1); +#else /* not WITH_LIBUSB_1_0 */ + struct usb_bus *busses = usb_get_busses(); struct usb_bus *bus; - + for (bus = busses; bus; bus = bus->next) { struct usb_device *dev; - + for (dev = bus->devices; dev; dev = dev->next) { if (dev->descriptor.bDeviceClass != USB_CLASS_PER_INTERFACE) { continue; } - if (is_usb_device_supported(pw_usb_device_table, - dev->descriptor.idVendor, dev->descriptor.idProduct) == SUPPORTED) { + curDevice.VendorID = dev->descriptor.idVendor; + curDevice.ProductID = dev->descriptor.idProduct; + curDevice.Bus = xstrdup(bus->dirname); + + /* FIXME: we should also retrieve + * dev->descriptor.iManufacturer + * dev->descriptor.iProduct + * dev->descriptor.iSerialNumber + * as in libusb.c->libusb_open() + * This is part of the things to put in common... */ + + if (is_usb_device_supported(pw_usb_device_table, &curDevice) == SUPPORTED) { return usb_open(dev); } } } +#endif /* WITH_LIBUSB_1_0 */ return 0; } @@ -380,15 +496,24 @@ usb_dev_handle *nutusb_open(const char *port) int dev_claimed = 0; usb_dev_handle *dev_h = NULL; int retry, errout = 0; + int ret = 0; upsdebugx(1, "entering nutusb_open()"); + warn_if_bad_usb_port_filename(device_path); /* Initialize Libusb */ +#if WITH_LIBUSB_1_0 + if (libusb_init(NULL) < 0) { + libusb_exit(NULL); + fatal_with_errno(EXIT_FAILURE, "Failed to init libusb 1.0"); + } +#else /* not WITH_LIBUSB_1_0 */ usb_init(); usb_find_busses(); usb_find_devices(); +#endif /* WITH_LIBUSB_1_0 */ - for (retry = 0; retry <= MAX_TRY ; retry++) + for (retry = 0; retry < MAX_TRY ; retry++) { dev_h = open_powerware_usb(); if (!dev_h) { @@ -396,12 +521,12 @@ usb_dev_handle *nutusb_open(const char *port) errout = 1; } else { - upsdebugx(1, "device %s opened successfully", usb_device(dev_h)->filename); + upsdebugx(1, "device %s opened successfully", curDevice.Bus); errout = 0; - if (usb_claim_interface(dev_h, 0) < 0) + if ((ret = usb_claim_interface(dev_h, 0)) < 0) { - upsdebugx(1, "Can't claim POWERWARE USB interface: %s", usb_strerror()); + upsdebugx(1, "Can't claim POWERWARE USB interface: %s", nut_usb_strerror(ret)); errout = 1; } else { @@ -410,9 +535,13 @@ usb_dev_handle *nutusb_open(const char *port) } /* FIXME: the above part of the opening can go into common... up to here at least */ - if (usb_clear_halt(dev_h, 0x81) < 0) + if ((ret = usb_clear_halt(dev_h, 0x81)) < 0) { - upsdebugx(1, "Can't reset POWERWARE USB endpoint: %s", usb_strerror()); + upsdebugx(1, "Can't reset POWERWARE USB endpoint: %s", nut_usb_strerror(ret)); + if (dev_claimed) + usb_release_interface(dev_h, 0); + usb_reset(dev_h); + sleep(5); /* Wait reconnect */ errout = 1; } else @@ -436,8 +565,7 @@ usb_dev_handle *nutusb_open(const char *port) if (dev_h && dev_claimed) usb_release_interface(dev_h, 0); - if (dev_h) - usb_close(dev_h); + nutusb_close(dev_h, port); if (errout == 1) nutusb_open_error(port); @@ -448,24 +576,32 @@ usb_dev_handle *nutusb_open(const char *port) /* FIXME: this part can go into common... */ int nutusb_close(usb_dev_handle *dev_h, const char *port) { + int ret = 0; + NUT_UNUSED_VARIABLE(port); + if (dev_h) { usb_release_interface(dev_h, 0); - return usb_close(dev_h); +#if WITH_LIBUSB_1_0 + libusb_close(dev_h); + libusb_exit(NULL); +#else + ret = usb_close(dev_h); +#endif } - - return 0; + + return ret; } void nutusb_comm_fail(const char *fmt, ...) { - int ret; - char why[SMALLBUF]; - va_list ap; + int ret; + char why[SMALLBUF]; + va_list ap; /* this means we're probably here because select was interrupted */ if (exit_flag != 0) - return; /* ignored, since we're about to exit anyway */ + return; /* ignored, since we're about to exit anyway */ comm_failures++; @@ -480,6 +616,7 @@ void nutusb_comm_fail(const char *fmt, ...) if ((comm_failures > USB_ERR_LIMIT) && ((comm_failures % USB_ERR_LIMIT) != 0)) { /* Try reconnection */ + upsdebugx(1, "Got to reconnect!\n"); upsdrv_reconnect(); return; } diff --git a/drivers/belkin-hid.c b/drivers/belkin-hid.c index d3b294b..c1aaccf 100644 --- a/drivers/belkin-hid.c +++ b/drivers/belkin-hid.c @@ -1,8 +1,9 @@ -/* belkin-hid.h - data to monitor Belkin UPS Systems USB/HID devices with NUT +/* belkin-hid.c - data to monitor Belkin UPS Systems USB/HID devices with NUT * * Copyright (C) - * 2003 - 2008 Arnaud Quette + * 2003 - 2008 Arnaud Quette * 2005 Peter Selinger + * 2011, 2014 Charles Lepple * * Sponsored by MGE UPS SYSTEMS * @@ -28,7 +29,9 @@ #include "belkin-hid.h" #include "usb-common.h" -#define BELKIN_HID_VERSION "Belkin HID 0.12" +#include /* for fabs() */ + +#define BELKIN_HID_VERSION "Belkin/Liebert HID 0.18" /* Belkin */ #define BELKIN_VENDORID 0x050d @@ -36,7 +39,12 @@ /* Liebert */ #define LIEBERT_VENDORID 0x10af -/* USB IDs device table */ +/*! USB IDs device table. + * Note that there are at least two Liebert firmware types which both report + * a VID:PID of 10af:0001. The newer ones tend not to have the Belkin broken + * Usage Pages (and therefore use standard HID PDC paths) but they have + * incorrect exponents for some fields. + */ static usb_device_id_t belkin_usb_device_table[] = { /* F6C800-UNV */ { USB_DEVICE(BELKIN_VENDORID, 0x0980), NULL }, @@ -54,16 +62,141 @@ static usb_device_id_t belkin_usb_device_table[] = { { USB_DEVICE(BELKIN_VENDORID, 0x0751), NULL }, /* F6H375-USB */ { USB_DEVICE(BELKIN_VENDORID, 0x0375), NULL }, + /* Regulator PRO-USB */ + { USB_DEVICE(BELKIN_VENDORID, 0x0f51), NULL }, /* F6C1100-UNV, F6C1200-UNV */ { USB_DEVICE(BELKIN_VENDORID, 0x1100), NULL }, + /* Liebert GXT4 UPS */ + { USB_DEVICE(LIEBERT_VENDORID, 0x0004), NULL }, /* Liebert PowerSure PSA UPS */ { USB_DEVICE(LIEBERT_VENDORID, 0x0001), NULL }, + /* Liebert PowerSure PSI 1440 */ + { USB_DEVICE(LIEBERT_VENDORID, 0x0004), NULL }, + /* Liebert GXT3 */ + { USB_DEVICE(LIEBERT_VENDORID, 0x0008), NULL }, /* Terminating entry */ - { -1, -1, NULL } + { 0, 0, NULL } }; +static const char *liebert_online_fun(double value); +static const char *liebert_discharging_fun(double value); +static const char *liebert_charging_fun(double value); +static const char *liebert_lowbatt_fun(double value); +static const char *liebert_replacebatt_fun(double value); +static const char *liebert_shutdownimm_fun(double value); +static const char *liebert_config_voltage_fun(double value); +static const char *liebert_line_voltage_fun(double value); + +static info_lkp_t liebert_online_info[] = { + { 0, NULL, liebert_online_fun, NULL } +}; + +static info_lkp_t liebert_discharging_info[] = { + { 0, NULL, liebert_discharging_fun, NULL } +}; + +static info_lkp_t liebert_charging_info[] = { + { 0, NULL, liebert_charging_fun, NULL } +}; + +static info_lkp_t liebert_lowbatt_info[] = { + { 0, NULL, liebert_lowbatt_fun, NULL } +}; + +static info_lkp_t liebert_replacebatt_info[] = { + { 0, NULL, liebert_replacebatt_fun, NULL } +}; + +static info_lkp_t liebert_shutdownimm_info[] = { + { 0, NULL, liebert_shutdownimm_fun, NULL } +}; + +static info_lkp_t liebert_config_voltage_info[] = { + { 0, NULL, liebert_config_voltage_fun, NULL }, +}; + +static info_lkp_t liebert_line_voltage_info[] = { + { 0, NULL, liebert_line_voltage_fun, NULL }, +}; + +static double liebert_config_voltage_mult = 1.0; +static double liebert_line_voltage_mult = 1.0; +static char liebert_conversion_buf[10]; + +/* These lookup functions also cover the 1e-7 factor which seems to be due to a + * broken report descriptor in certain Liebert units. + */ +static const char *liebert_online_fun(double value) +{ + return value ? "online" : "!online"; +} + +static const char *liebert_discharging_fun(double value) +{ + return value ? "dischrg" : "!dischrg"; +} + +static const char *liebert_charging_fun(double value) +{ + return value ? "chrg" : "!chrg"; +} + +static const char *liebert_lowbatt_fun(double value) +{ + return value ? "lowbatt" : "!lowbatt"; +} + +static const char *liebert_replacebatt_fun(double value) +{ + return value ? "replacebatt" : "!replacebatt"; +} + +static const char *liebert_shutdownimm_fun(double value) +{ + return value ? "shutdownimm" : "!shutdownimm"; +} + +/*! Apply heuristics to Liebert ConfigVoltage for correction of other values. + * Logic is weird since the ConfigVoltage item comes after InputVoltage and + * OutputVoltage. + */ +static const char *liebert_config_voltage_fun(double value) +{ + if( value < 1 ) { + if( fabs(value - 1e-7) < 1e-9 ) { + liebert_config_voltage_mult = 1e8; + liebert_line_voltage_mult = 1e7; /* stomp this in case input voltage was low */ + upsdebugx(2, "ConfigVoltage = %g -> assuming correction factor = %g", + value, liebert_config_voltage_mult); + } else { + upslogx(LOG_NOTICE, "ConfigVoltage exponent looks wrong, but not correcting."); + } + } + + snprintf(liebert_conversion_buf, sizeof(liebert_conversion_buf), "%.1f", + value * liebert_config_voltage_mult); + return liebert_conversion_buf; +} + +static const char *liebert_line_voltage_fun(double value) +{ + if( value < 1 ) { + if( fabs(value - 1e-7) < 1e-9 ) { + liebert_line_voltage_mult = 1e7; + upsdebugx(2, "Input/OutputVoltage = %g -> assuming correction factor = %g", + value, liebert_line_voltage_mult); + } else { + upslogx(LOG_NOTICE, "LineVoltage exponent looks wrong, but not correcting."); + } + } + + snprintf(liebert_conversion_buf, sizeof(liebert_conversion_buf), "%.1f", + value * liebert_line_voltage_mult); + return liebert_conversion_buf; +} + /* some conversion functions specific to Belkin */ /* returns statically allocated string - must not use it again before @@ -78,7 +211,7 @@ static const char *belkin_firmware_conversion_fun(double value) } static info_lkp_t belkin_firmware_conversion[] = { - { 0, NULL, belkin_firmware_conversion_fun } + { 0, NULL, belkin_firmware_conversion_fun, NULL } }; static const char *belkin_upstype_conversion_fun(double value) @@ -101,7 +234,7 @@ static const char *belkin_upstype_conversion_fun(double value) } static info_lkp_t belkin_upstype_conversion[] = { - { 0, NULL, belkin_upstype_conversion_fun } + { 0, NULL, belkin_upstype_conversion_fun, NULL } }; static const char *belkin_sensitivity_conversion_fun(double value) @@ -118,17 +251,17 @@ static const char *belkin_sensitivity_conversion_fun(double value) } static info_lkp_t belkin_sensitivity_conversion[] = { - { 0, NULL, belkin_sensitivity_conversion_fun } + { 0, NULL, belkin_sensitivity_conversion_fun, NULL } }; static info_lkp_t belkin_test_info[] = { - { 0, "No test initiated", NULL }, - { 1, "Done and passed", NULL }, - { 2, "Done and warning", NULL }, - { 3, "Done and error", NULL }, - { 4, "Aborted", NULL }, - { 5, "In progress", NULL }, - { 0, NULL, NULL } + { 0, "No test initiated", NULL, NULL }, + { 1, "Done and passed", NULL, NULL }, + { 2, "Done and warning", NULL, NULL }, + { 3, "Done and error", NULL, NULL }, + { 4, "Aborted", NULL, NULL }, + { 5, "In progress", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static const char *belkin_overload_conversion_fun(double value) @@ -141,7 +274,7 @@ static const char *belkin_overload_conversion_fun(double value) } static info_lkp_t belkin_overload_conversion[] = { - { 0, NULL, belkin_overload_conversion_fun } + { 0, NULL, belkin_overload_conversion_fun, NULL } }; static const char *belkin_overheat_conversion_fun(double value) @@ -154,7 +287,7 @@ static const char *belkin_overheat_conversion_fun(double value) } static info_lkp_t belkin_overheat_conversion[] = { - { 0, NULL, belkin_overheat_conversion_fun } + { 0, NULL, belkin_overheat_conversion_fun, NULL } }; static const char *belkin_commfault_conversion_fun(double value) @@ -167,7 +300,7 @@ static const char *belkin_commfault_conversion_fun(double value) } static info_lkp_t belkin_commfault_conversion[] = { - { 0, NULL, belkin_commfault_conversion_fun } + { 0, NULL, belkin_commfault_conversion_fun, NULL } }; static const char *belkin_awaitingpower_conversion_fun(double value) @@ -180,7 +313,7 @@ static const char *belkin_awaitingpower_conversion_fun(double value) } static info_lkp_t belkin_awaitingpower_conversion[] = { - { 0, NULL, belkin_awaitingpower_conversion_fun } + { 0, NULL, belkin_awaitingpower_conversion_fun, NULL } }; static const char *belkin_online_conversion_fun(double value) @@ -193,7 +326,7 @@ static const char *belkin_online_conversion_fun(double value) } static info_lkp_t belkin_online_conversion[] = { - { 0, NULL, belkin_online_conversion_fun } + { 0, NULL, belkin_online_conversion_fun, NULL } }; static const char *belkin_lowbatt_conversion_fun(double value) @@ -206,7 +339,7 @@ static const char *belkin_lowbatt_conversion_fun(double value) } static info_lkp_t belkin_lowbatt_conversion[] = { - { 0, NULL, belkin_lowbatt_conversion_fun } + { 0, NULL, belkin_lowbatt_conversion_fun, NULL } }; static const char *belkin_depleted_conversion_fun(double value) @@ -219,7 +352,7 @@ static const char *belkin_depleted_conversion_fun(double value) } static info_lkp_t belkin_depleted_conversion[] = { - { 0, NULL, belkin_depleted_conversion_fun } + { 0, NULL, belkin_depleted_conversion_fun, NULL } }; static const char *belkin_replacebatt_conversion_fun(double value) @@ -232,7 +365,7 @@ static const char *belkin_replacebatt_conversion_fun(double value) } static info_lkp_t belkin_replacebatt_conversion[] = { - { 0, NULL, belkin_replacebatt_conversion_fun } + { 0, NULL, belkin_replacebatt_conversion_fun, NULL } }; /* --------------------------------------------------------------- */ @@ -338,12 +471,27 @@ static hid_info_t belkin_hid2nut[] = { { "ups.serial", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%s", 0, stringid_conversion }, { "ups.test.result", 0, 0, "UPS.BELKINControls.BELKINTest", NULL, "%s", 0, belkin_test_info }, { "ups.type", 0, 0, "UPS.BELKINDevice.BELKINUPSType", NULL, "%s", 0, belkin_upstype_conversion }, - + /* Liebert PSA: */ + { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, /* why .broken above? */ + { "input.frequency", 0, 0, "UPS.Input.Frequency", NULL, "%s", 0, divide_by_10_conversion }, + { "input.voltage", 0, 0, "UPS.Input.Voltage", NULL, "%s", 0, liebert_line_voltage_info }, + { "output.voltage", 0, 0, "UPS.Output.Voltage", NULL, "%s", 0, liebert_line_voltage_info }, + /* You would think these next two would be off by the same factor. You'd be wrong. */ + { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%s", 0, liebert_line_voltage_info }, + { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%s", HU_FLAG_STATIC, liebert_config_voltage_info }, + { "ups.load", 0, 0, "UPS.Output.PercentLoad", NULL, "%.0f", 0, NULL }, /* status */ - { "BOOL", 0, 0, "UPS.PowerSummary.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, - { "BOOL", 0, 0, "UPS.PowerSummary.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, - { "BOOL", 0, 0, "UPS.PowerSummary.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, - { "BOOL", 0, 0, "UPS.PowerSummary.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_discharging_info }, /* might not need to be liebert_* version */ + { "BOOL", 0, 0, "UPS.PowerSummary.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_charging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.ShutdownImminent", NULL, NULL, 0, liebert_shutdownimm_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_discharging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_charging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, liebert_shutdownimm_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_replacebatt_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, liebert_lowbatt_info }, + /* { "BOOL", 0, 0, "UPS.PowerSummary.BelowRemainingCapacityLimit", NULL, "%s", 0, lowbatt_info }, broken! */ { "BOOL", 0, 0, "UPS.BELKINStatus.BELKINPowerStatus", NULL, NULL, 0, belkin_overload_conversion }, { "BOOL", 0, 0, "UPS.BELKINStatus.BELKINPowerStatus", NULL, NULL, 0, belkin_overheat_conversion }, @@ -381,6 +529,22 @@ static hid_info_t belkin_hid2nut[] = { yet implemented) workaround, see the belkinunv(8) man page. -PS 2005/08/28 */ +#if 0 + /* added for debugging Liebert GXT3 : */ + { "unmapped.ups.powersummary.iserialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.ioeminformation", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", 0, stringid_conversion }, + { "unmapped.ups.powersummary.designcapacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.remainingtimelimit", 0, 0, "UPS.PowerSummary.RemainingTimeLimit", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.capacitymode", 0, 0, "UPS.PowerSummary.CapacityMode", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.batterypresent", 0, 0, "UPS.PowerSummary.BatteryPresent", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.fullchargecapacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.capacitygranularity1", 0, 0, "UPS.PowerSummary.CapacityGranularity1", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.capacitygranularity2", 0, 0, "UPS.PowerSummary.CapacityGranularity2", NULL, "%.0f", 0, NULL }, + { "unmapped.ups.powersummary.iproduct", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%.0f", 0, NULL }, +#endif + /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; @@ -430,7 +594,7 @@ static const char *belkin_format_serial(HIDDevice_t *hd) { * the device is supported by this subdriver, else 0. */ static int belkin_claim(HIDDevice_t *hd) { - int status = is_usb_device_supported(belkin_usb_device_table, hd->VendorID, hd->ProductID); + int status = is_usb_device_supported(belkin_usb_device_table, hd); switch (status) { @@ -477,4 +641,5 @@ subdriver_t belkin_subdriver = { belkin_format_model, belkin_format_mfr, belkin_format_serial, + fix_report_desc, }; diff --git a/drivers/belkin-hid.h b/drivers/belkin-hid.h index d37db9e..7c5a483 100644 --- a/drivers/belkin-hid.h +++ b/drivers/belkin-hid.h @@ -1,7 +1,7 @@ /* belkin-hid.h - data to monitor Belkin UPS Systems USB/HID devices with NUT * - * Copyright (C) - * 2003 - 2005 Arnaud Quette + * Copyright (C) + * 2003 - 2005 Arnaud Quette * 2005 Peter Selinger * * Sponsored by MGE UPS SYSTEMS diff --git a/drivers/belkin.c b/drivers/belkin.c index ad5a658..6757fc7 100644 --- a/drivers/belkin.c +++ b/drivers/belkin.c @@ -28,10 +28,10 @@ #include "belkin.h" #define DRIVER_NAME "Belkin Smart protocol driver" -#define DRIVER_VERSION "0.24" +#define DRIVER_VERSION "0.25" -static int init_communication(void); -static int get_belkin_reply(char *buf); +static ssize_t init_communication(void); +static ssize_t get_belkin_reply(char *buf); /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -51,9 +51,10 @@ static void send_belkin_command(char cmd, const char *subcmd, const char *data) upsdebugx(3, "Send Command: %s, %s", subcmd, data); } -static int init_communication(void) +static ssize_t init_communication(void) { - int i, res; + int i; + ssize_t res; char temp[SMALLBUF]; for (i = 0; i < 10; i++) { @@ -107,9 +108,9 @@ static char *get_belkin_field(const char *in, char *out, size_t outlen, size_t n return NULL; } -static int get_belkin_reply(char *buf) +static ssize_t get_belkin_reply(char *buf) { - int ret; + ssize_t ret; long cnt; char tmp[8]; @@ -119,10 +120,11 @@ static int get_belkin_reply(char *buf) ret = ser_get_buf_len(upsfd, (unsigned char *)tmp, 7, 2, 0); if (ret != 7) { - ser_comm_fail("Initial read returned %d bytes", ret); + ser_comm_fail("Initial read returned %zd bytes", ret); return -1; } + /* cnt is <=999 so long is overkill; ok to cast into useconds_t though */ tmp[7] = 0; cnt = strtol(tmp + 4, NULL, 10); upsdebugx(3, "Received: %s", tmp); @@ -137,15 +139,15 @@ static int get_belkin_reply(char *buf) } /* give it time to respond to us */ - usleep(5000 * cnt); + usleep(5000 * (useconds_t)cnt); - ret = ser_get_buf_len(upsfd, (unsigned char *)buf, cnt, 2, 0); + ret = ser_get_buf_len(upsfd, (unsigned char *)buf, (size_t)cnt, 2, 0); buf[cnt] = 0; upsdebugx(3, "Received: %s", buf); if (ret != cnt) { - ser_comm_fail("Second read returned %d bytes, expected %ld", ret, cnt); + ser_comm_fail("Second read returned %zd bytes, expected %ld", ret, cnt); return -1; } @@ -154,9 +156,9 @@ static int get_belkin_reply(char *buf) return ret; } -static int do_broken_rat(char *buf) +static ssize_t do_broken_rat(char *buf) { - int ret; + ssize_t ret; long cnt; char tmp[8]; @@ -166,10 +168,11 @@ static int do_broken_rat(char *buf) ret = ser_get_buf_len(upsfd, (unsigned char *)tmp, 7, 2, 0); if (ret != 7) { - ser_comm_fail("Initial read returned %d bytes", ret); + ser_comm_fail("Initial read returned %zd bytes", ret); return -1; } + /* cnt is <=999 so long is overkill; ok to cast into useconds_t though */ tmp[7] = 0; cnt = strtol(tmp + 4, NULL, 10); upsdebugx(3, "Received: %s", tmp); @@ -184,20 +187,20 @@ static int do_broken_rat(char *buf) } /* give it time to respond to us */ - usleep(5000 * cnt); + usleep(5000 * (useconds_t)cnt); /* firmware 001 only sends 50 bytes instead of the proper 53 */ if (cnt == 53) { cnt = 50; } - ret = ser_get_buf_len(upsfd, (unsigned char *)buf, cnt, 2, 0); + ret = ser_get_buf_len(upsfd, (unsigned char *)buf, (size_t)cnt, 2, 0); buf[cnt] = 0; upsdebugx(3, "Received: %s", buf); if (ret != cnt) { - ser_comm_fail("Second read returned %d bytes, expected %ld", ret, cnt); + ser_comm_fail("Second read returned %zd bytes, expected %ld", ret, cnt); return -1; } @@ -210,7 +213,7 @@ static int do_broken_rat(char *buf) void upsdrv_updateinfo(void) { static int retry = 0; - int res; + ssize_t res; char temp[SMALLBUF], st[SMALLBUF]; send_belkin_command(STATUS, STAT_STATUS, ""); @@ -262,14 +265,14 @@ void upsdrv_updateinfo(void) get_belkin_field(temp, st, sizeof(st), 10); res = atoi(st); get_belkin_field(temp, st, sizeof(st), 2); - + if (*st == '1' || res < LOW_BAT) { status_set("LB"); /* low battery */ } get_belkin_field(temp, st, sizeof(st), 10); dstate_setinfo("battery.charge", "%.0f", strtod(st, NULL)); - + get_belkin_field(temp, st, sizeof(st), 9); dstate_setinfo("battery.temperature", "%.0f", strtod(st, NULL)); @@ -302,7 +305,7 @@ void upsdrv_updateinfo(void) get_belkin_field(temp, st, sizeof(st), 7); dstate_setinfo("ups.load", "%.0f", strtod(st, NULL)); } - + send_belkin_command(STATUS, TEST_RESULT, ""); res = get_belkin_reply(temp); if (res > 0) { @@ -348,7 +351,7 @@ void upsdrv_updateinfo(void) /* power down the attached load immediately */ void upsdrv_shutdown(void) { - int res; + ssize_t res; res = init_communication(); if (res < 0) { @@ -371,7 +374,7 @@ void upsdrv_shutdown(void) /* handle "beeper.disable" */ static void do_beeper_off(void) { - int res; + ssize_t res; char temp[SMALLBUF]; const char *arg; @@ -453,7 +456,7 @@ static int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -482,12 +485,12 @@ void upsdrv_initups(void) void upsdrv_initinfo(void) { - int res; + ssize_t res; char temp[SMALLBUF], st[SMALLBUF]; res = init_communication(); if (res < 0) { - fatalx(EXIT_FAILURE, + fatalx(EXIT_FAILURE, "Unable to detect an Belkin Smart protocol UPS on port %s\n" "Check the cabling, port name or model name and try again", device_path ); diff --git a/drivers/belkinunv.c b/drivers/belkinunv.c index 5698313..c508563 100644 --- a/drivers/belkinunv.c +++ b/drivers/belkinunv.c @@ -36,7 +36,6 @@ battery.runtime battery.voltage battery.voltage.nominal - driver.version.internal input.frequency input.frequency.nominal e.g. 60 for 60Hz input.sensitivity (RW) normal/medium/low @@ -48,7 +47,7 @@ input.voltage.nominal output.frequency output.voltage - ups.beeper.status (RW) enabled/disabled/muted + ups.beeper.status (RW) enabled/disabled/muted ups.firmware ups.load ups.model @@ -56,12 +55,12 @@ ups.status ups.temperature ups.test.result - ups.delay.restart read-only: time to restart - ups.delay.shutdown read-only: time to shutdown + ups.delay.restart read-only: time to restart + ups.delay.shutdown read-only: time to shutdown ups.type ONLINE/OFFLINE/LINEINT - + COMMANDS: - + beeper.disable beeper.enable beeper.mute @@ -95,7 +94,7 @@ #include "serial.h" #define DRIVER_NAME "Belkin 'Universal UPS' driver" -#define DRIVER_VERSION "0.07" +#define DRIVER_VERSION "0.08" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -112,48 +111,48 @@ upsdrv_info_t upsdrv_info = { #define MAXMSGSIZE 25 /* definitions of register numbers for Belkin UPS */ -#define REG_VOLTRATING 0x01 -#define REG_FREQRATING 0x02 -#define REG_POWERRATING 0x03 +#define REG_VOLTRATING 0x01 +#define REG_FREQRATING 0x02 +#define REG_POWERRATING 0x03 #define REG_BATVOLTRATING 0x04 -#define REG_XFER_LO 0x06 -#define REG_XFER_LO_MAX 0x07 -#define REG_XFER_LO_MIN 0x08 -#define REG_XFER_HI 0x09 -#define REG_XFER_HI_MAX 0x0a -#define REG_XFER_HI_MIN 0x0b -#define REG_VOLTSENS 0x0c -#define REG_UPSMODEL 0x0d -#define REG_UPSMODEL2 0x0e -#define REG_FIRMWARE 0x0f -#define REG_TESTSTATUS 0x10 -#define REG_ALARMSTATUS 0x11 +#define REG_XFER_LO 0x06 +#define REG_XFER_LO_MAX 0x07 +#define REG_XFER_LO_MIN 0x08 +#define REG_XFER_HI 0x09 +#define REG_XFER_HI_MAX 0x0a +#define REG_XFER_HI_MIN 0x0b +#define REG_VOLTSENS 0x0c +#define REG_UPSMODEL 0x0d +#define REG_UPSMODEL2 0x0e +#define REG_FIRMWARE 0x0f +#define REG_TESTSTATUS 0x10 +#define REG_ALARMSTATUS 0x11 #define REG_SHUTDOWNTIMER 0x15 #define REG_RESTARTTIMER 0x16 -#define REG_INPUTVOLT 0x18 -#define REG_INPUTFREQ 0x19 -#define REG_TEMPERATURE 0x1a -#define REG_OUTPUTVOLT 0x1b -#define REG_OUTPUTFREQ 0x1c -#define REG_LOAD 0x1e -#define REG_BATSTAT2 0x1f -#define REG_BATVOLT 0x20 -#define REG_BATLEVEL 0x21 -#define REG_UPSSTATUS 0x22 -#define REG_BATSTATUS 0x23 -#define REG_TIMELEFT 0x3f +#define REG_INPUTVOLT 0x18 +#define REG_INPUTFREQ 0x19 +#define REG_TEMPERATURE 0x1a +#define REG_OUTPUTVOLT 0x1b +#define REG_OUTPUTFREQ 0x1c +#define REG_LOAD 0x1e +#define REG_BATSTAT2 0x1f +#define REG_BATVOLT 0x20 +#define REG_BATLEVEL 0x21 +#define REG_UPSSTATUS 0x22 +#define REG_BATSTATUS 0x23 +#define REG_TIMELEFT 0x3f /* flags for REG_UPSSTATUS */ #define US_ACFAILURE 0x0001 #define US_OVERLOAD 0x0010 -#define US_OFF 0x0020 +#define US_OFF 0x0020 #define US_OVERHEAT 0x0040 #define US_UPSFAULT 0x0080 #define US_WAITING 0x2000 #define US_BUZZER 0x8000 /* flags for REG_BATSTATUS */ -#define BS_LOW 0x04 +#define BS_LOW 0x04 #define BS_CHARGING 0x10 #define BS_ONBATTERY 0x20 #define BS_DEPLETED 0x40 @@ -162,24 +161,24 @@ upsdrv_info_t upsdrv_info = { /* size of an array */ #define asize(x) ((int)(sizeof(x)/sizeof(x[0]))) -const char *upstype[3] = { - "ONLINE", - "OFFLINE", +static const char *upstype[3] = { + "ONLINE", + "OFFLINE", "LINEINT" }; -const char *voltsens[3] = { - "normal", - "medium", +static const char *voltsens[3] = { + "normal", + "medium", "low" }; -const char *teststatus[6] = { - "no test performed", - "test passed", - "test failed", - "test failed", - "test aborted", +static const char *teststatus[6] = { + "no test performed", + "test passed", + "test failed", + "test failed", + "test aborted", "test in progress" }; @@ -248,7 +247,7 @@ static void belkin_nut_open_tty(void) byte, checksum). Return length of message, or -1 if not well-formed */ static int belkin_nut_receive(unsigned char *buf, int bufsize) { - int r; + ssize_t r; int n=0; int len; @@ -286,7 +285,8 @@ static int belkin_nut_receive(unsigned char *buf, int bufsize) { if (n+len > bufsize) { return -1; } - r = ser_get_buf_len(upsfd, &buf[4], len, 3, 0); + /* Casting is okay, len is range-limited to unsigned char */ + r = ser_get_buf_len(upsfd, &buf[4], (size_t)len, 3, 0); if (r!=len) { upslogx(LOG_ERR, "Short read from UPS"); return -1; @@ -303,9 +303,10 @@ static int belkin_nut_receive(unsigned char *buf, int bufsize) { /* read the value of a string register from UPS. Return NULL on failure, else an allocated string. */ -static char *belkin_nut_read_str(int reg) { +static char *belkin_nut_read_str(unsigned char reg) { unsigned char buf[MAXMSGSIZE]; - int len, r; + ssize_t r; + size_t len; char *str; /* send the request */ @@ -336,6 +337,10 @@ static char *belkin_nut_read_str(int reg) { } /* convert the answer to a string */ + if (buf[2] < 1) { + upslogx(LOG_ERR, "Invalid response from UPS: string too short to be true"); + return NULL; + } len = buf[2]-1; str = (char *)xmalloc(len+1); memcpy(str, &buf[4], len); @@ -345,9 +350,10 @@ static char *belkin_nut_read_str(int reg) { /* read the value of an integer register from UPS. Return -1 on failure. */ -static int belkin_nut_read_int(int reg) { +static int belkin_nut_read_int(unsigned char reg) { unsigned char buf[MAXMSGSIZE]; - int len, r; + int len; + ssize_t r; /* send the request */ buf[0] = 0x7e; @@ -390,9 +396,9 @@ static int belkin_nut_read_int(int reg) { /* write the value of an integer register to UPS. Return -1 on failure, else 0 */ -static int belkin_nut_write_int(int reg, int val) { +static int belkin_nut_write_int(unsigned char reg, int val) { unsigned char buf[MAXMSGSIZE]; - int r; + ssize_t r; /* send the request */ buf[0] = 0x7e; @@ -402,7 +408,7 @@ static int belkin_nut_write_int(int reg, int val) { buf[4] = val & 0xff; buf[5] = (val>>8) & 0xff; buf[6] = belkin_checksum(buf, 6); - + r = ser_send_buf(upsfd, buf, 7); if (r<0) { upslogx(LOG_ERR, "Failed write to UPS"); @@ -446,14 +452,14 @@ static int belkin_std_open_tty(const char *device) { struct termios tios; struct flock flock; char buf[128]; - int r; - + ssize_t r; + /* open the device */ fd = open(device, O_RDWR | O_NONBLOCK); if (fd == -1) { return -1; } - + /* set communications parameters: 2400 baud, 8 bits, 1 stop bit, no parity, enable reading, hang up when done, ignore modem control lines. */ @@ -466,7 +472,7 @@ static int belkin_std_open_tty(const char *device) { close(fd); return -1; } - + /* signal the UPS to enter "smart" mode. This is done by setting RTS and dropping DTR for at least 0.25 seconds. RTS and DTR refer to two specific pins in the 9-pin serial connector. Note: this must @@ -483,7 +489,7 @@ static int belkin_std_open_tty(const char *device) { close(fd); return -1; } - + /* lock the port */ memset(&flock, 0, sizeof(flock)); flock.l_type = F_RDLCK; @@ -492,11 +498,11 @@ static int belkin_std_open_tty(const char *device) { close(fd); return -1; } - + /* sleep at least 0.25 seconds for the UPS to wake up. Belkin's own software sleeps 1 second, so that's what we do, too. */ usleep(1000000); - + /* flush incoming data again, and read any remaining garbage bytes. There should not be any. */ r = tcflush(fd, TCIFLUSH); @@ -504,27 +510,27 @@ static int belkin_std_open_tty(const char *device) { close(fd); return -1; } - + r = read(fd, buf, 127); if (r == -1 && errno != EAGAIN) { close(fd); return -1; } - + /* leave port in non-blocking state */ - + return fd; } /* blocking read with 1-second timeout (use non-blocking i/o) */ static int belkin_std_upsread(int fd, unsigned char *buf, int n) { int count = 0; - int r; + ssize_t r; int tries = 0; - + while (count < n) { - r = read(fd, &buf[count], n-count); - if (r==-1 && errno==EAGAIN) { + r = read(fd, &buf[count], (size_t)(n-count)); + if (r==-1 && errno==EAGAIN) { /* non-blocking i/o, no data available */ usleep(100000); tries++; @@ -543,12 +549,12 @@ static int belkin_std_upsread(int fd, unsigned char *buf, int n) { /* blocking write with 1-second timeout (use non-blocking i/o) */ static int belkin_std_upswrite(int fd, unsigned char *buf, int n) { int count = 0; - int r; + ssize_t r; int tries = 0; while (count < n) { - r = write(fd, &buf[count], n-count); - if (r==-1 && errno==EAGAIN) { + r = write(fd, &buf[count], (size_t)(n-count)); + if (r==-1 && errno==EAGAIN) { /* non-blocking i/o, no data available */ usleep(100000); tries++; @@ -613,7 +619,7 @@ static int belkin_std_receive(int fd, unsigned char *buf, int bufsize) { /* read the value of an integer register from UPS. Return -1 on failure. */ -static int belkin_std_read_int(int fd, int reg) { +static int belkin_std_read_int(int fd, unsigned char reg) { unsigned char buf[MAXMSGSIZE]; int len, r; @@ -655,10 +661,10 @@ static int belkin_std_read_int(int fd, int reg) { /* write the value of an integer register to UPS. Return -1 on failure, else 0 */ -static int belkin_std_write_int(int fd, int reg, int val) { +static int belkin_std_write_int(int fd, unsigned char reg, int val) { unsigned char buf[MAXMSGSIZE]; int r; - + /* send the request */ buf[0] = 0x7e; buf[1] = 0x04; @@ -667,12 +673,12 @@ static int belkin_std_write_int(int fd, int reg, int val) { buf[4] = val & 0xff; buf[5] = (val>>8) & 0xff; buf[6] = belkin_checksum(buf, 6); - + r = belkin_std_upswrite(fd, buf, 7); if (r<0) { return -1; } - + /* receive the acknowledgement */ r = belkin_std_receive(fd, buf, MAXMSGSIZE); if (r<0) { @@ -711,7 +717,19 @@ static void updatestatus(int smode, const char *fmt, ...) { /* read formatted argument string */ va_start(ap, fmt); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif vsnprintf(buf, sizeof(buf), fmt, ap); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif buf[sizeof(buf)-1] = 0; va_end(ap); @@ -765,13 +783,13 @@ static int belkin_wait(void) if (dstate_getinfo("driver.flag.flash")) { flash = 1; } - + if (dstate_getinfo("driver.flag.silent")) { smode = 0; } else if (dstate_getinfo("driver.flag.dumbterm")) { smode = 2; } - + updatestatus(smode, "Connecting to UPS..."); failcount = 0; fd = -1; @@ -825,7 +843,7 @@ static int belkin_wait(void) } /* successfully got data from UPS */ failcount = 0; - + if (bs & BS_ONBATTERY) { st = ST_BATTERY; } else if (ov>0) { @@ -908,29 +926,29 @@ void upsdrv_initinfo(void) /* read writable values and declare them writable */ val = belkin_nut_read_int(REG_VOLTSENS); - if (val!=-1) { - dstate_setinfo("input.sensitivity", "%s", (val>=0 && val=0 && val 0) { @@ -1040,13 +1058,13 @@ void upsdrv_updateinfo(void) /* new read everything else */ val = belkin_nut_read_int(REG_XFER_LO); - if (val!=-1) { - dstate_setinfo("input.transfer.low", "%d", val); + if (val!=-1) { + dstate_setinfo("input.transfer.low", "%d", val); } val = belkin_nut_read_int(REG_XFER_HI); - if (val!=-1) { - dstate_setinfo("input.transfer.high", "%d", val); + if (val!=-1) { + dstate_setinfo("input.transfer.high", "%d", val); } val = belkin_nut_read_int(REG_VOLTSENS); @@ -1061,7 +1079,7 @@ void upsdrv_updateinfo(void) val = belkin_nut_read_int(REG_ALARMSTATUS); if (val!=-1) { - dstate_setinfo("ups.beeper.status", "%s", val==1 ? "disabled" : val&1 ? "muted" : "enabled"); + dstate_setinfo("ups.beeper.status", "%s", (val==1) ? "disabled" : (val&1) ? "muted" : "enabled"); } val = belkin_nut_read_int(REG_SHUTDOWNTIMER); @@ -1133,7 +1151,7 @@ void upsdrv_shutdown(void) /* Note: this UPS cannot (apparently) be put into "soft shutdown" mode; thus the -k option should not normally be used; instead, a workaround using the "-x wait" option - should be used; see belkinunv(8) for details. + should be used; see belkinunv(8) for details. In case somebody uses the -k option, the best we can do here is a timed shutdown; this will wake up the attached @@ -1146,7 +1164,7 @@ void upsdrv_shutdown(void) option instead, as suggested on the belkinunv(8) man page. */ - upslogx(LOG_WARNING, "You are using the -k option, which is broken for this driver.\nShutting down for 10 minutes and hoping for the best"); + upslogx(LOG_WARNING, "You are using the -k option, which is broken for this driver.\nShutting down for 10 minutes and hoping for the best"); belkin_nut_write_int(REG_RESTARTTIMER, 10); /* 10 minutes */ belkin_nut_write_int(REG_SHUTDOWNTIMER, 1); /* 1 second */ @@ -1156,8 +1174,8 @@ int instcmd(const char *cmdname, const char *extra) { int r; - /* We use test.failure.start to initiate a "deep battery test". - This does not really simulate a 'power failure', because we + /* We use test.failure.start to initiate a "deep battery test". + This does not really simulate a 'power failure', because we won't start shutdown procedures during a test. We use test.battery.start to initiate a "10-second battery test". */ @@ -1178,35 +1196,43 @@ int instcmd(const char *cmdname, const char *extra) if (!strcasecmp(cmdname, "test.failure.start")) { r = belkin_nut_write_int(REG_TESTSTATUS, 2); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "test.failure.stop")) { r = belkin_nut_write_int(REG_TESTSTATUS, 3); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "test.battery.start")) { r = belkin_nut_write_int(REG_TESTSTATUS, 1); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "test.battery.stop")) { r = belkin_nut_write_int(REG_TESTSTATUS, 3); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "beeper.disable")) { r = belkin_nut_write_int(REG_ALARMSTATUS, 1); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "beeper.enable")) { r = belkin_nut_write_int(REG_ALARMSTATUS, 2); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "beeper.mute")) { r = belkin_nut_write_int(REG_ALARMSTATUS, 3); + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "shutdown.stayoff")) { r = belkin_nut_write_int(REG_RESTARTTIMER, 0); r |= belkin_nut_write_int(REG_SHUTDOWNTIMER, 1); /* 1 second */ + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "shutdown.reboot")) { @@ -1218,11 +1244,13 @@ int instcmd(const char *cmdname, const char *extra) the UPS will stay off between 60 and 120 seconds */ r = belkin_nut_write_int(REG_RESTARTTIMER, 2); /* 2 minutes */ r |= belkin_nut_write_int(REG_SHUTDOWNTIMER, 1); /* 1 second */ + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "shutdown.reboot.graceful")) { r = belkin_nut_write_int(REG_RESTARTTIMER, 2); /* 2 minutes */ r |= belkin_nut_write_int(REG_SHUTDOWNTIMER, 40); /* 40 seconds */ + if (r == -1) upslogx(LOG_WARNING, "Command '%s' failed", cmdname); return STAT_INSTCMD_HANDLED; /* Future: failure if r==-1 */ } if (!strcasecmp(cmdname, "reset.input.minmax")) { @@ -1232,7 +1260,7 @@ int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } diff --git a/drivers/bestfcom.c b/drivers/bestfcom.c index 8a85e6d..c53aa95 100644 --- a/drivers/bestfcom.c +++ b/drivers/bestfcom.c @@ -1,4 +1,4 @@ -/* +/* bestfcom.c - model specific routines for Best Power F-Command ups models This module is yet another rewritten mangle of the bestuferrups @@ -45,7 +45,7 @@ #include "serial.h" #define DRIVER_NAME "Best Ferrups/Fortress driver" -#define DRIVER_VERSION "0.12" +#define DRIVER_VERSION "0.13" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -83,7 +83,7 @@ upsdrv_info_t upsdrv_info = { #include /* Blob of UPS configuration data from the formatconfig string */ -struct { +static struct { int valid; /* set to 1 when this is filled in */ float idealbvolts; /* various interesting battery voltages */ @@ -114,24 +114,24 @@ void upsdrv_initinfo (void) */ dstate_setinfo("ups.mfr", "%s", "Best Power"); switch(fc.model) { - case MExxxx: - dstate_setinfo("ups.model", "%s ME%d", fc.name, fc.va); - break; - case MDxxxx: - dstate_setinfo("ups.model", "%s MD%d", fc.name, fc.va); - break; - case FDxxxx: - dstate_setinfo("ups.model", "%s FD%d", fc.name, fc.va); - break; - case FExxxx: - dstate_setinfo("ups.model", "%s FE%d", fc.name, fc.va); - break; - case LIxxxx: - dstate_setinfo("ups.model", "%s LI%d", fc.name, fc.va); - break; - default: - fatalx(EXIT_FAILURE, "Unknown model - oops!"); /* Will never get here, upsdrv_initups() will catch */ - } + case MExxxx: + dstate_setinfo("ups.model", "%s ME%d", fc.name, fc.va); + break; + case MDxxxx: + dstate_setinfo("ups.model", "%s MD%d", fc.name, fc.va); + break; + case FDxxxx: + dstate_setinfo("ups.model", "%s FD%d", fc.name, fc.va); + break; + case FExxxx: + dstate_setinfo("ups.model", "%s FE%d", fc.name, fc.va); + break; + case LIxxxx: + dstate_setinfo("ups.model", "%s LI%d", fc.name, fc.va); + break; + default: + fatalx(EXIT_FAILURE, "Unknown model - oops!"); /* Will never get here, upsdrv_initups() will catch */ + } dstate_setinfo("ups.power.nominal", "%d", fc.va); dstate_setinfo("ups.realpower.nominal", "%d", fc.watts); @@ -162,7 +162,7 @@ void upsdrv_initinfo (void) /* atoi() without the freebie octal conversion */ -int bcd2i (const char *bcdstring, const int bcdlen) +static int bcd2i (const char *bcdstring, const int bcdlen) { int i, digit, total = 0, factor = 1; for (i = 1; i < bcdlen; i++) @@ -181,7 +181,8 @@ int bcd2i (const char *bcdstring, const int bcdlen) #define POLL_ALERT "{" static void alert_handler(char ch) { - char buf[256]; + char buf[SMALLBUF]; + NUT_UNUSED_VARIABLE(ch); /* Received an Inverter status alarm : * "\r\n{Inverter: On}\r\n=>" @@ -195,10 +196,10 @@ static void alert_handler(char ch) time^M^M^JFeb 20, 22:13:32^M^J^M^J=>id^M^JUnit ID "ME3.1K12345"^M^J^M^J=> ---------------------------------------------------- */ -static int execute(const char *cmd, char *result, int resultsize) +static ssize_t execute(const char *cmd, char *result, size_t resultsize) { - int ret; - char buf[256]; + ssize_t ret; + char buf[SMALLBUF]; unsigned char ch; /* Check for the Inverter status alarm if pending : @@ -250,11 +251,32 @@ void upsdrv_updateinfo(void) if (! fc.valid) { upsdebugx(1, "upsupdate run before ups_ident() read ups config"); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + /* NOTE: This assert() always fails because of "0": + * error: will never be executed [-Werror,-Wunreachable-code] + * ((0) ? (void) (0) : __assert_fail ("0", "bestfcom.c", 254, __PRETTY_FUNCTION__)); + * ^ + */ assert(0); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic pop +#endif } if (execute("f\r", fstring, sizeof(fstring)) >= 80) { - int inverter=0, charger=0, vin=0, vout=0, btimeleft=0, linestat=0, + int inverter=0, charger=0, vin=0, vout=0, btimeleft=0, linestat=0, alstat=0, vaout=0; double ampsout=0.0, vbatt=0.0, battpercent=0.0, loadpercent=0.0, @@ -269,7 +291,7 @@ void upsdrv_updateinfo(void) /* Charger status. 0=off 1=on */ charger = bcd2i(&fstring[18], 2); - + /* Input Voltage. integer number */ vin = bcd2i(&fstring[24], 4); @@ -277,7 +299,7 @@ void upsdrv_updateinfo(void) vout = bcd2i(&fstring[28], 4); /* Battery voltage. int times 10 */ - vbatt = ((double)bcd2i(&fstring[50], 4) / 10.0); + vbatt = ((double)(bcd2i(&fstring[50], 4)) / 10.0); /* Alarm status reg 1. Bitmask */ alstat = bcd2i(&fstring[20], 2); @@ -286,14 +308,14 @@ void upsdrv_updateinfo(void) alstat = alstat | (bcd2i(&fstring[22], 2) << 8); /* AC line frequency */ - acfreq = ((double)bcd2i(&fstring[54], 4) / 100.0); + acfreq = ((double)(bcd2i(&fstring[54], 4)) / 100.0); /* Runtime remaining (UPS reports minutes) */ btimeleft = bcd2i(&fstring[58], 4) * 60; if (fc.model != FDxxxx) { /* Iout. int times 10 */ - ampsout = ((double)bcd2i(&fstring[36], 4) / 10.0); + ampsout = ((double)(bcd2i(&fstring[36], 4)) / 10.0); /* Volt-amps out. int */ vaout = bcd2i(&fstring[40], 6); @@ -304,40 +326,40 @@ void upsdrv_updateinfo(void) } if (fc.model != LIxxxx) { - upstemp = (double) bcd2i(&fstring[62], 4); + upstemp = (double)(bcd2i(&fstring[62], 4)); } /* Percent Load */ switch(fc.model) { - case LIxxxx: - case FDxxxx: - case FExxxx: - case MExxxx: - if (execute("d 16\r", tmp, sizeof(tmp)) > 0) { - int l; - sscanf(tmp, "16 FullLoad%% %d", &l); - loadpercent = (double) l; - } - break; - case MDxxxx: - if (execute("d 22\r", tmp, sizeof(tmp)) > 0) { - int l; - sscanf(tmp, "22 FullLoad%% %d", &l); - loadpercent = (double) l; - } - break; - default: /* Will never happen, caught in upsdrv_initups() */ - fatalx(EXIT_FAILURE, "Unknown model in upsdrv_updateinfo()"); + case LIxxxx: + case FDxxxx: + case FExxxx: + case MExxxx: + if (execute("d 16\r", tmp, sizeof(tmp)) > 0) { + int l; + sscanf(tmp, "16 FullLoad%% %d", &l); + loadpercent = (double) l; + } + break; + case MDxxxx: + if (execute("d 22\r", tmp, sizeof(tmp)) > 0) { + int l; + sscanf(tmp, "22 FullLoad%% %d", &l); + loadpercent = (double) l; + } + break; + default: /* Will never happen, caught in upsdrv_initups() */ + fatalx(EXIT_FAILURE, "Unknown model in upsdrv_updateinfo()"); } /* Compute battery percent left based on battery voltages. */ - battpercent = ((vbatt - fc.emptyvolts) - / (fc.fullvolts - fc.emptyvolts) * 100.0); - if (battpercent < 0.0) + battpercent = ((vbatt - fc.emptyvolts) + / (fc.idealbvolts - fc.emptyvolts) * 100.0); + if (battpercent < 0.0) battpercent = 0.0; else if (battpercent > 100.0) battpercent = 100.0; - + /* Compute status string */ { int lowbatt, lowvolts, overload, replacebatt, boosting, trimming; @@ -353,7 +375,7 @@ void upsdrv_updateinfo(void) lowvolts = (vbatt <= fc.lowvolts); status_init(); - + if (inverter) { if (inverter_status < 1) { upsdebugx(1, "Inverter On, charger: %d battery time left: %d", @@ -444,7 +466,7 @@ void upsdrv_shutdown(void) { /* NB: hard-wired password */ ser_send(upsfd, "pw377\r"); - ser_send(upsfd, "off 1 a\r"); /* power off in 1 second and restart when line power returns */ + ser_send(upsfd, "o 10 a\r"); /* power off in 10 seconds and restart when line power returns, FE7K required a min of 5 seconds for off to function */ } /* list flags and values that you want to receive via -x */ @@ -477,10 +499,10 @@ static void sync_serial(void) { static void setup_serial(void) { struct termios tio; - + if (tcgetattr(upsfd, &tio) == -1) fatal_with_errno(EXIT_FAILURE, "tcgetattr"); - + tio.c_iflag = IXON | IXOFF; tio.c_oflag = 0; tio.c_cflag = (CS8 | CREAD | HUPCL | CLOCAL); @@ -525,11 +547,11 @@ Version: 8.07 Released: 08/01/1995 */ -void upsdrv_init_nofc(void) +static void upsdrv_init_nofc(void) { char tmp[256], rstring[1024]; - /* This is a Best UPS + /* This is a Best UPS * Set initial values for old Fortress??? */ @@ -577,7 +599,8 @@ void upsdrv_init_nofc(void) } } else if (strstr(rstring, "Model: FE") - || strstr(rstring, "Model: FE")){ + || strstr(rstring, "Model: FE")) + { fc.model = FExxxx; fc.type = FERRUPS; snprintf(fc.name, sizeof(fc.name), "%s", "Ferrups"); @@ -586,56 +609,55 @@ void upsdrv_init_nofc(void) /* How does the old Fortress respond to this? */ upsdebugx(2, "Old Best Fortress???"); /* fc.model = FORTRESS; */ - } + } if (fc.model == UNKNOWN) { fatalx(EXIT_FAILURE, "Unknown model %s in upsdrv_init_nofc()", rstring); } switch(fc.model) { - case MExxxx: - case MDxxxx: - case FDxxxx: - /* determine shutdown battery voltage */ - if (execute("d 27\r", tmp, sizeof(tmp)) > 0) { - sscanf(tmp, "27 LowBatt %f", &fc.emptyvolts); - } - /* determine near low battery voltage */ - if (execute("d 30\r", tmp, sizeof(tmp)) > 0) { - sscanf(tmp, "30 NLBatt %f", &fc.lowvolts); - } - /* determine fully charged battery voltage */ - if (execute("d 28\r", tmp, sizeof(tmp)) > 0) { - sscanf(tmp, "28 Hi Batt %f", &fc.fullvolts); - } - fc.fullvolts = 13.70; - /* determine "ideal" voltage by a guess */ - fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; - break; - case FExxxx: - if (execute("d 45\r", tmp, sizeof(tmp)) > 0) { - sscanf(tmp, "45 RatedVA %d", &fc.va); /* 4300 */ - } - if (execute("d 46\r", tmp, sizeof(tmp)) > 0) { - sscanf(tmp, "46 RatedW %d", &fc.watts); /* 3000 */ - } - if (execute("d 65\r", tmp, sizeof(tmp)) > 0) { - sscanf(tmp, "65 LoBatV %f", &fc.emptyvolts); /* 41.00 */ - } - if (execute("d 66\r", tmp, sizeof(tmp)) > 0) { - sscanf(tmp, "66 NLBatV %f", &fc.lowvolts); /* 44.00 */ - } - if (execute("d 67\r", tmp, sizeof(tmp)) > 0) { - sscanf(tmp, "67 HiBatV %f", &fc.fullvolts); /* 59.60 */ - } - fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; - if (fc.va < 1.0) { - fatalx(EXIT_FAILURE, "Error determining Ferrups UPS rating."); - } - break; - default: - fatalx(EXIT_FAILURE, "Unknown model %s in upsdrv_init_nofc()", rstring); - break; + case MExxxx: + case MDxxxx: + case FDxxxx: + /* determine shutdown battery voltage */ + if (execute("d 27\r", tmp, sizeof(tmp)) > 0) { + sscanf(tmp, "27 LowBatt %f", &fc.emptyvolts); + } + /* determine near low battery voltage */ + if (execute("d 30\r", tmp, sizeof(tmp)) > 0) { + sscanf(tmp, "30 NLBatt %f", &fc.lowvolts); + } + /* determine fully charged battery voltage */ + if (execute("d 28\r", tmp, sizeof(tmp)) > 0) { + sscanf(tmp, "28 Hi Batt %f", &fc.fullvolts); + } + fc.fullvolts = 13.70; + /* determine "ideal" voltage by a guess */ + fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; + break; + case FExxxx: + if (execute("d 45\r", tmp, sizeof(tmp)) > 0) { + sscanf(tmp, "45 RatedVA %d", &fc.va); /* 4300 */ + } + if (execute("d 46\r", tmp, sizeof(tmp)) > 0) { + sscanf(tmp, "46 RatedW %d", &fc.watts); /* 3000 */ + } + if (execute("d 65\r", tmp, sizeof(tmp)) > 0) { + sscanf(tmp, "65 LoBatV %f", &fc.emptyvolts); /* 41.00 */ + } + if (execute("d 66\r", tmp, sizeof(tmp)) > 0) { + sscanf(tmp, "66 NLBatV %f", &fc.lowvolts); /* 44.00 */ + } + if (execute("d 67\r", tmp, sizeof(tmp)) > 0) { + sscanf(tmp, "67 HiBatV %f", &fc.fullvolts); /* 59.60 */ + } + fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; + if (fc.va < 1.0) { + fatalx(EXIT_FAILURE, "Error determining Ferrups UPS rating."); + } + break; + default: + fatalx(EXIT_FAILURE, "Unknown model %s in upsdrv_init_nofc()", rstring); } fc.valid = 1; } @@ -657,7 +679,7 @@ Model: [0,1] => 00 = unk, 01 = Patriot/SPS, 02 = FortressII, 03 = Ferrups, 04 = [2,3] => 00 = LI520, 01 = LI720, 02 = LI1020, 03 = LI1420, 07 = ??? */ -void upsdrv_init_fc(const char *fcstring) +static void upsdrv_init_fc(const char *fcstring) { char tmp[256]; @@ -709,24 +731,24 @@ void upsdrv_init_fc(const char *fcstring) } switch(fc.model) { - case LIxxxx: - fc.va = bcd2i(&fcstring[11], 5); - fc.watts = bcd2i(&fcstring[16], 5); + case LIxxxx: + fc.va = bcd2i(&fcstring[11], 5); + fc.watts = bcd2i(&fcstring[16], 5); - /* determine shutdown battery voltage */ - fc.emptyvolts= ((double)bcd2i(&fcstring[57], 4) / 10.0); + /* determine shutdown battery voltage */ + fc.emptyvolts= ((double)(bcd2i(&fcstring[57], 4)) / 10.0); - /* determine fully charged battery voltage */ - fc.lowvolts= ((double)bcd2i(&fcstring[53], 4) / 10.0); + /* determine fully charged battery voltage */ + fc.lowvolts= ((double)(bcd2i(&fcstring[53], 4)) / 10.0); - /* determine fully charged battery voltage */ - fc.fullvolts= ((double)bcd2i(&fcstring[49], 4) / 10.0); + /* determine fully charged battery voltage */ + fc.fullvolts= ((double)(bcd2i(&fcstring[49], 4)) / 10.0); - /* determine "ideal" voltage by a guess */ - fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; - break; - default: - fatalx(EXIT_FAILURE, "Unknown model %s in upsdrv_init_fc()", tmp); + /* determine "ideal" voltage by a guess */ + fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; + break; + default: + fatalx(EXIT_FAILURE, "Unknown model %s in upsdrv_init_fc()", tmp); } fc.valid = 1; } diff --git a/drivers/bestfortress.c b/drivers/bestfortress.c index c3d60c4..97300f6 100644 --- a/drivers/bestfortress.c +++ b/drivers/bestfortress.c @@ -22,6 +22,7 @@ #include "main.h" #include "serial.h" +#include "nut_stdint.h" #define UPSDELAY 50000 /* 50 ms delay required for reliable operation */ #define SER_WAIT_SEC 2 /* allow 2.0 sec for ser_get calls */ @@ -33,8 +34,8 @@ #define inline __inline #endif -#define DRIVER_NAME "Best Fortress UPS driver" -#define DRIVER_VERSION "0.02" +#define DRIVER_NAME "Best Fortress UPS driver" +#define DRIVER_VERSION "0.06" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -66,16 +67,16 @@ void upsdrv_initinfo(void) dstate_setinfo("ups.delay.shutdown", "10"); /* write only */ /* tunable via front panel: (european voltage level) - parameter factory default range - INFO_LOWXFER 196 V p7=nnn 160-210 - INFO_HIGHXFER 254 V p8=nnn 215-274 - INFO_LOBATTIME 2 min p2=n 1-5 + parameter factory default range + INFO_LOWXFER 196 V p7=nnn 160-210 + INFO_HIGHXFER 254 V p8=nnn 215-274 + INFO_LOBATTIME 2 min p2=n 1-5 comm mode p6=0 dumb DONT USE (will lose access to parameter setting!) - p6=1 B1200 - p6=2 B2400 - P6=3 B4800 - p6=4 B9600 + p6=1 B1200 + p6=2 B2400 + P6=3 B4800 + p6=4 B9600 maybe cycle through speeds to autodetect? echo off e0 @@ -126,7 +127,8 @@ static inline int setinfo_int (const char *key, const char * s, size_t len) char buf[10]; int val; - if (len > sizeof(buf)) len = sizeof(buf)-1; + if (len > sizeof(buf)) + len = sizeof(buf)-1; strncpy (buf, s, len); buf[len] = 0; val = atoi(buf); @@ -141,7 +143,8 @@ static inline void setinfo_int_minutes (const char *key, const char * s, size_t { char buf[10]; - if (len > sizeof(buf)) len = sizeof(buf)-1; + if (len > sizeof(buf)) + len = sizeof(buf)-1; strncpy (buf, s, len); buf[len] = 0; dstate_setinfo (key, "%d", 60*atoi (buf)); @@ -151,47 +154,85 @@ static inline void setinfo_int_minutes (const char *key, const char * s, size_t static inline void setinfo_float (const char *key, const char * fmt, const char * s, size_t len, double factor) { char buf[10]; - if (len > sizeof(buf)) len = sizeof(buf)-1; + if (len > sizeof(buf)) + len = sizeof(buf)-1; strncpy (buf, s, len); buf[len] = 0; - dstate_setinfo (key, fmt, factor * (double)atoi (buf)); + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + dstate_setinfo (key, fmt, factor * (double)(atoi (buf))); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif } static int upssend(const char *fmt,...) { - int ret; + int ret; char buf[1024], *p; - va_list ap; + va_list ap; unsigned int sent = 0; - int d_usec = UPSDELAY; + useconds_t d_usec = UPSDELAY; va_start(ap, fmt); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif ret = vsnprintf(buf, sizeof(buf), fmt, ap); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif va_end(ap); if ((ret < 1) || (ret >= (int) sizeof(buf))) upslogx(LOG_WARNING, "ser_send_pace: vsnprintf needed more " "than %d bytes", (int)sizeof(buf)); - for (p = buf; *p; p++) { + + for (p = buf; *p && sent < INT_MAX - 1; p++) { if (write(upsfd, p, 1) != 1) return -1; - if (d_usec) + /* Note: LGTM.com analysis warns that here + * "Comparison is always true because d_usec >= 2" + * since we initialize with UPSDELAY above. + * Do not remove this check just in case that + * initialization changes, or run-time value + * becomes modified, in later iterations. + */ + if (d_usec > 0) usleep(d_usec); sent++; + if (sent >= INT_MAX) { + upslogx(LOG_WARNING, "ser_send_pace: sent more than INT_MAX, aborting"); + } } - return sent; + return (int)sent; } -static int upsrecv(char *buf,size_t bufsize,char ec,const char *ic) +static ssize_t upsrecv(char *buf,size_t bufsize,char ec,const char *ic) { return ser_get_line(upsfd, buf, bufsize - 1, ec, ic, - SER_WAIT_SEC, SER_WAIT_USEC); + SER_WAIT_SEC, SER_WAIT_USEC); } -static int upsflushin(int f,int verbose,const char *ignset) +static ssize_t upsflushin(int f, int verbose, const char *ignset) { + NUT_UNUSED_VARIABLE(f); return ser_flush_in(upsfd, ignset, verbose); } @@ -199,24 +240,33 @@ static int upsflushin(int f,int verbose,const char *ignset) void upsdrv_updateinfo(void) { char temp[256]; - char *p; + char *p = NULL; int loadva; - int len; + size_t len = 0; + ssize_t recv; int retry; + char ch; + int checksum_ok = -1, is_online = 1, is_off, low_batt, trimming, boosting; - int checksum_ok, is_online=1, is_off, low_batt, trimming, boosting; + upsdebugx(1, "upsdrv_updateinfo"); for (retry = 0; retry < 5; ++retry) { upsflushin (0, 0, "\r "); upssend ("f\r"); + while (ser_get_char(upsfd, &ch, 0, UPSDELAY) > 0 && ch != '\n'); /* response starts with \r\n */ + temp[2] = 0; do { - if (upsrecv (temp+2, sizeof temp - 2, ENDCHAR, IGNCHARS) <= 0) { + if ((recv = upsrecv (temp+2, sizeof temp - 2, ENDCHAR, IGNCHARS)) <= 0) { upsflushin (0, 0, "\r "); upssend ("f\r"); + while (ser_get_char(upsfd, &ch, 0, UPSDELAY) > 0 && ch != '\n'); /* response starts with \r\n */ } } while (temp[2] == 0); - /*syslog (LOG_DAEMON | LOG_NOTICE,"ups: got '%s'\n", p);*/ + upsdebugx(1, "upsdrv_updateinfo: received %zi bytes (try %i)", recv, retry); + upsdebug_hex(5, "buffer", temp, (size_t)recv); + + /* syslog (LOG_DAEMON | LOG_NOTICE,"ups: got %d chars '%s'\n", recv, temp + 2); */ /* status example: 000000000001000000000000012201210000001200014500000280600000990025000000000301BE 000000000001000000000000012401230000001200014800000280600000990025000000000301B7 @@ -229,7 +279,7 @@ void upsdrv_updateinfo(void) /* last bytes are a checksum: interpret response as hex string, sum of all bytes must be zero */ - checksum_ok = (checksum (temp+2) & 0xff) == 0; + checksum_ok = ( (checksum (temp+2) & 0xff) == 0 ); /* setinfo (INFO_, ""); */ /* I can't figure out why this is missing the first two chars. @@ -247,10 +297,19 @@ void upsdrv_updateinfo(void) sleep(SER_WAIT_SEC); } - if (!checksum_ok) { + if (!p || len < 1 || checksum_ok < 0) { + upsdebugx(2, "pointer to data not initialized after processing"); dstate_datastale(); return; } + + if (!checksum_ok) { + upsdebugx(2, "checksum corruption"); + upsdebug_hex(3, "buffer", temp, (size_t)len); + dstate_datastale(); + return; + } + /* upslogx(LOG_INFO, "updateinfo: %s", p); */ setinfo_int ("input.voltage", p+24,4); @@ -299,9 +358,11 @@ void upsdrv_updateinfo(void) /* all UPS tunable parameters are set with command 'p%d=%s' */ -int setparam (int parameter, int dlen, const char * data) +static int setparam (int parameter, int dlen, const char * data) { char reply[80]; + /* Note the use of "%*s" - parameter (int)dlen specifies + * the string width reserved for data */ upssend ("p%d=%*s\r", parameter, dlen, data); if (upsrecv (reply, sizeof(reply), ENDCHAR, "") < 0) return 0; return strncmp (reply, "OK", 2) == 0; @@ -328,7 +389,7 @@ static void autorestart (int restart) /* set UPS parameters */ static int upsdrv_setvar (const char *var, const char * data) { int parameter; - int len = strlen(data); + size_t len = strlen(data); upsdebugx(1, "Setvar: %s %s", var, data); if (strcmp("input.transfer.low", var) == 0) { parameter = 7; @@ -344,8 +405,9 @@ static int upsdrv_setvar (const char *var, const char * data) { return STAT_SET_UNKNOWN; } ups_setsuper (1); - if (setparam (parameter, len, data)) { - dstate_setinfo (var, "%*s", len, data); + assert (len < INT_MAX); + if (setparam (parameter, (int)len, data)) { + dstate_setinfo (var, "%*s", (int)len, data); } ups_setsuper (0); return STAT_SET_HANDLED; @@ -386,7 +448,7 @@ static int instcmd (const char *cmdname, const char *extra) upssend ("OFF%s\r", p); return STAT_INSTCMD_HANDLED; } - upslogx(LOG_INFO, "instcmd: unknown command %s", cmdname); + upslogx(LOG_INFO, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -401,7 +463,7 @@ void upsdrv_makevartable(void) addvar (VAR_VALUE, "max_load", "rated VA load VA"); } -struct { +static struct { const char * val; speed_t speed; } speed_table[] = { diff --git a/drivers/bestpower-mib.c b/drivers/bestpower-mib.c index 29c1926..51ba1a6 100644 --- a/drivers/bestpower-mib.c +++ b/drivers/bestpower-mib.c @@ -22,7 +22,7 @@ #include "bestpower-mib.h" -#define BESTPOWER_MIB_VERSION "0.1" +#define BESTPOWER_MIB_VERSION "0.4" #define BESTPOWER_OID_MODEL_NAME ".1.3.6.1.4.1.2947.1.1.2.0" /* @@ -32,30 +32,37 @@ /* TODO: find the right sysOID for this MIB * #define BESTPOWER_SYSOID ".1.3.6.1.4.1.2947???" */ +#define BESTPOWER_SYSOID BESTPOWER_OID_MODEL_NAME static info_lkp_t bestpower_power_status[] = { - { 1, "OL" }, - { 2, "OB" }, - { 0, "NULL" } + { 1, "OL", NULL, NULL }, + { 2, "OB", NULL, NULL }, + { 0, NULL, NULL, NULL } } ; /* Snmp2NUT lookup table for Best Power MIB */ static snmp_info_t bestpower_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device page */ { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ups", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /*.1.3.6.1.4.1.2947.1.1.1.0 = STRING: "Ferrups" .1.3.6.1.4.1.2947.1.1.2.0 = STRING: "FE850VA"*/ { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, BESTPOWER_OID_MODEL_NAME, - "Best Ferrups", SU_FLAG_STATIC, NULL, NULL }, + "Best Ferrups", SU_FLAG_STATIC, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.1.5.0", "", SU_FLAG_STATIC, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.1.7.0", - "", SU_FLAG_STATIC, NULL }, + "", SU_FLAG_STATIC, NULL }, { "ups.power", 0, 1, ".1.3.6.1.4.1.2947.1.1.3.0", "", 0, NULL }, { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2947.1.1.8.0", "", @@ -77,8 +84,8 @@ static snmp_info_t bestpower_mib[] = { 0, NULL }, /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } + { NULL, 0, 0, NULL, NULL, 0, NULL } } ; -mib2nut_info_t bestpower = { "bestpower", BESTPOWER_MIB_VERSION, "", - BESTPOWER_OID_MODEL_NAME, bestpower_mib }; +mib2nut_info_t bestpower = { "bestpower", BESTPOWER_MIB_VERSION, NULL, + BESTPOWER_OID_MODEL_NAME, bestpower_mib, BESTPOWER_SYSOID, NULL }; diff --git a/drivers/bestuferrups.c b/drivers/bestuferrups.c index d60cb02..8bed32b 100644 --- a/drivers/bestuferrups.c +++ b/drivers/bestuferrups.c @@ -1,4 +1,4 @@ -/* +/* bestuferrups.c - model specific routines for Best Power Micro-Ferrups This module is a 40% rewritten mangle of the bestfort module by @@ -33,7 +33,7 @@ #include "serial.h" #define DRIVER_NAME "Best Ferrups Series ME/RE/MD driver" -#define DRIVER_VERSION "0.03" +#define DRIVER_VERSION "0.04" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -61,46 +61,46 @@ upsdrv_info_t upsdrv_info = { #include #include -int debugging = 0; - +static int debugging = 0; /* Blob of UPS configuration data from the formatconfig string */ -struct { - int valid; /* set to 1 when this is filled in */ +static struct { + int valid; /* set to 1 when this is filled in */ - float idealbvolts; /* various interestin battery voltages */ - float fullvolts; - float emptyvolts; - int va; /* capacity of UPS in Volt-Amps */ - int watts; /* capacity of UPS in watts */ - int model; /* enumerated model type */ + float idealbvolts; /* various interestin battery voltages */ + float fullvolts; + float emptyvolts; + int va; /* capacity of UPS in Volt-Amps */ + int watts; /* capacity of UPS in watts */ + int model; /* enumerated model type */ } fc; /* Forward decls */ /* Set up all the funky shared memory stuff used to communicate with upsd */ -void upsdrv_initinfo (void) +void upsdrv_initinfo (void) { /* now set up room for all future variables that are supported */ dstate_setinfo("ups.mfr", "%s", "Best Power"); - switch(fc.model) { - case ME3100: - dstate_setinfo("ups.model", "Micro Ferrups (ME) %d", fc.va); - break; - case MD1KVA: - dstate_setinfo("ups.model", "Micro Ferrups (MD) %d", fc.va); - break; - case RE1800: - dstate_setinfo("ups.model", "Micro Ferrups (RE) %d", fc.va); - break; - default: - fatalx(EXIT_FAILURE, "UPS model not matched!"); /* Will never get here, upsdrv_initups() will catch */ - } - fprintf(stderr, "Best Power %s detected\n", + switch(fc.model) + { + case ME3100: + dstate_setinfo("ups.model", "Micro Ferrups (ME) %d", fc.va); + break; + case MD1KVA: + dstate_setinfo("ups.model", "Micro Ferrups (MD) %d", fc.va); + break; + case RE1800: + dstate_setinfo("ups.model", "Micro Ferrups (RE) %d", fc.va); + break; + default: + fatalx(EXIT_FAILURE, "UPS model not matched!"); /* Will never get here, upsdrv_initups() will catch */ + } + fprintf(stderr, "Best Power %s detected\n", dstate_getinfo("ups.model")); - fprintf(stderr, "Battery voltages %5.1f nominal, %5.1f full, %5.1f empty\n", + fprintf(stderr, "Battery voltages %5.1f nominal, %5.1f full, %5.1f empty\n", fc.idealbvolts, fc.fullvolts, fc.emptyvolts); @@ -114,236 +114,259 @@ time^M^M^JFeb 20, 22:13:32^M^J^M^J=>id^M^JUnit ID "ME3.1K12345"^M^J^M^J=> ---------------------------------------------------- */ -static int execute(const char *cmd, char *result, int resultsize) +static ssize_t execute(const char *cmd, char *result, size_t resultsize) { - int ret; - char buf[256]; - - ser_send(upsfd, "%s", cmd); - ser_get_line(upsfd, buf, sizeof(buf), '\012', "", 3, 0); - ret = ser_get_line(upsfd, result, resultsize, '\015', "\012", 3, 0); - ser_get_line(upsfd, buf, sizeof(buf), '>', "", 3, 0); - return ret; + ssize_t ret; + char buf[256]; + + ser_send(upsfd, "%s", cmd); + ser_get_line(upsfd, buf, sizeof(buf), '\012', "", 3, 0); + ret = ser_get_line(upsfd, result, resultsize, '\015', "\012", 3, 0); + ser_get_line(upsfd, buf, sizeof(buf), '>', "", 3, 0); + return ret; } void upsdrv_updateinfo(void) { - char fstring[512]; + char fstring[512]; - if (! fc.valid) { - fprintf(stderr, - "upsupdate run before ups_ident() read ups config\n"); - assert(0); - } + if (! fc.valid) { + fprintf(stderr, + "upsupdate run before ups_ident() read ups config\n"); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + /* NOTE: This assert() always fails because of "0": + * error: will never be executed [-Werror,-Wunreachable-code] + * ((0) ? (void) (0) : __assert_fail ("0", "bestuferrups.c", 138, __PRETTY_FUNCTION__)); + * ^ + */ + assert(0); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic pop +#endif + } - if (execute("f\r", fstring, sizeof(fstring)) > 0) { - int inverter=0, charger=0, vin=0, vout=0, btimeleft=0, linestat=0, - alstat=0, vaout=0; - double ampsout=0.0, vbatt=0.0, battpercent=0.0, loadpercent=0.0, - hstemp=0.0, acfreq=0.0, ambtemp=0.0; - char tmp[16]; + if (execute("f\r", fstring, sizeof(fstring)) > 0) { + int inverter=0, charger=0, vin=0, vout=0, btimeleft=0, linestat=0, + alstat=0, vaout=0; + double ampsout=0.0, vbatt=0.0, battpercent=0.0, loadpercent=0.0, + hstemp=0.0, acfreq=0.0, ambtemp=0.0; + char tmp[16]; - /* Inverter status. 0=off 1=on */ - memcpy(tmp, fstring+16, 2); - tmp[2] = '\0'; - inverter = atoi(tmp); + /* Inverter status: 0=off 1=on */ + memcpy(tmp, fstring+16, 2); + tmp[2] = '\0'; + inverter = atoi(tmp); - /* Charger status. 0=off 1=on */ - memcpy(tmp, fstring+18, 2); - tmp[2] = '\0'; - charger = atoi(tmp); - - /* Input Voltage. integer number */ - memcpy(tmp, fstring+24, 4); - tmp[4] = '\0'; - vin = atoi(tmp); + /* Charger status: 0=off 1=on */ + memcpy(tmp, fstring+18, 2); + tmp[2] = '\0'; + charger = atoi(tmp); - /* Output Voltage. integer number */ - memcpy(tmp, fstring+28, 4); - tmp[4] = '\0'; - vout = atoi(tmp); + /* Input Voltage. integer number */ + memcpy(tmp, fstring+24, 4); + tmp[4] = '\0'; + vin = atoi(tmp); - /* Iout. int times 10 */ - memcpy(tmp, fstring+36, 4); - tmp[4] = '\0'; - ampsout = ((double)(atoi(tmp)) / 10.0); + /* Output Voltage. integer number */ + memcpy(tmp, fstring+28, 4); + tmp[4] = '\0'; + vout = atoi(tmp); - /* Battery voltage. int times 10 */ - memcpy(tmp, fstring+50, 4); - tmp[4] = '\0'; - vbatt = ((double)(atoi(tmp)) / 10.0); + /* Iout: int times 10 */ + memcpy(tmp, fstring+36, 4); + tmp[4] = '\0'; + ampsout = ((double)(atoi(tmp)) / 10.0); - /* Volt-amps out. int */ - memcpy(tmp, fstring+40, 6); - tmp[6] = '\0'; - vaout = atoi(tmp); + /* Battery voltage: int times 10 */ + memcpy(tmp, fstring+50, 4); + tmp[4] = '\0'; + vbatt = ((double)(atoi(tmp)) / 10.0); - /* Line status. Bitmask */ - memcpy(tmp, fstring+72, 2); - tmp[2] = '\0'; - linestat = atoi(tmp); + /* Volt-amps out: int */ + memcpy(tmp, fstring+40, 6); + tmp[6] = '\0'; + vaout = atoi(tmp); - /* Alarm status reg 1. Bitmask */ - memcpy(tmp, fstring+20, 2); - tmp[2] = '\0'; - alstat = atoi(tmp); + /* Line status. Bitmask */ + memcpy(tmp, fstring+72, 2); + tmp[2] = '\0'; + linestat = atoi(tmp); - /* Alarm status reg 2. Bitmask */ - memcpy(tmp, fstring+22, 2); - tmp[2] = '\0'; - alstat = alstat | (atoi(tmp) << 8); + /* Alarm status reg 1. Bitmask */ + memcpy(tmp, fstring+20, 2); + tmp[2] = '\0'; + alstat = atoi(tmp); - /* AC line frequency */ - memcpy(tmp, fstring+54, 4); - tmp[4]= '\0'; - acfreq = ((double)(atoi(tmp)) / 100.0); + /* Alarm status reg 2. Bitmask */ + memcpy(tmp, fstring+22, 2); + tmp[2] = '\0'; + alstat = alstat | (atoi(tmp) << 8); - /* Runtime remaining */ - memcpy(tmp, fstring+58, 4); - tmp[4]= '\0'; - btimeleft = atoi(tmp); + /* AC line frequency */ + memcpy(tmp, fstring+54, 4); + tmp[4]= '\0'; + acfreq = ((double)(atoi(tmp)) / 100.0); - /* UPS Temperature */ - memcpy(tmp, fstring+62, 4); - tmp[4]= '\0'; - ambtemp = (double)(atoi(tmp)); + /* Runtime remaining */ + memcpy(tmp, fstring+58, 4); + tmp[4]= '\0'; + btimeleft = atoi(tmp); - /* Percent Load */ - switch(fc.model) { - case ME3100: - if (execute("d 16\r", fstring, sizeof(fstring)) > 0) { - int l; - sscanf(fstring, "16 FullLoad%% %d", &l); - loadpercent = (double) l; - } - break; - case RE1800: - if (execute("d 16\r", fstring, sizeof(fstring)) > 0) { - int l; - sscanf(fstring, "16 FullLoad%% %d", &l); - loadpercent = (double) l; - } - if (execute("d 12\r", fstring, sizeof(fstring)) > 0) { - int l; - sscanf(fstring, "12 HS Temp %dC", &l); - hstemp = (double) l; - } - break; - case MD1KVA: - if (execute("d 22\r", fstring, sizeof(fstring)) > 0) { - int l; - sscanf(fstring, "22 FullLoad%% %d", &l); - loadpercent = (double) l; - } - break; - default: /* Will never happen, caught in upsdrv_initups() */ - fatalx(EXIT_FAILURE, "Unknown model in upsdrv_updateinfo()"); - } - /* Compute battery percent left based on battery voltages. */ - battpercent = ((vbatt - fc.emptyvolts) - / (fc.fullvolts - fc.emptyvolts) * 100.0); - if (battpercent < 0.0) - battpercent = 0.0; - else if (battpercent > 100.0) - battpercent = 100.0; - - /* Compute status string */ - { - int lowbatt, overload, replacebatt, boosting, trimming; + /* UPS Temperature */ + memcpy(tmp, fstring+62, 4); + tmp[4]= '\0'; + ambtemp = (double)(atoi(tmp)); - lowbatt = alstat & (1<<1); - overload = alstat & (1<<6); - replacebatt = alstat & (1<<10); - boosting = inverter && (linestat & (1<<2)) && (vin < 115); - trimming = inverter && (linestat & (1<<2)) && (vin > 115); + /* Percent Load */ + switch(fc.model) + { + case ME3100: + if (execute("d 16\r", fstring, sizeof(fstring)) > 0) { + int l; + sscanf(fstring, "16 FullLoad%% %d", &l); + loadpercent = (double) l; + } + break; + case RE1800: + if (execute("d 16\r", fstring, sizeof(fstring)) > 0) { + int l; + sscanf(fstring, "16 FullLoad%% %d", &l); + loadpercent = (double) l; + } + if (execute("d 12\r", fstring, sizeof(fstring)) > 0) { + int l; + sscanf(fstring, "12 HS Temp %dC", &l); + hstemp = (double) l; + } + break; + case MD1KVA: + if (execute("d 22\r", fstring, sizeof(fstring)) > 0) { + int l; + sscanf(fstring, "22 FullLoad%% %d", &l); + loadpercent = (double) l; + } + break; + default: /* Will never happen, caught in upsdrv_initups() */ + fatalx(EXIT_FAILURE, "Unknown model in upsdrv_updateinfo()"); + } + /* Compute battery percent left based on battery voltages. */ + battpercent = ((vbatt - fc.emptyvolts) + / (fc.fullvolts - fc.emptyvolts) * 100.0); + if (battpercent < 0.0) + battpercent = 0.0; + else if (battpercent > 100.0) + battpercent = 100.0; - status_init(); - - if (inverter) - status_set("OB"); - else - status_set("OL"); + /* Compute status string */ + { + int lowbatt, overload, replacebatt, boosting, trimming; - if (lowbatt) - status_set("LB"); + lowbatt = alstat & (1<<1); + overload = alstat & (1<<6); + replacebatt = alstat & (1<<10); + boosting = inverter && (linestat & (1<<2)) && (vin < 115); + trimming = inverter && (linestat & (1<<2)) && (vin > 115); - if (trimming) - status_set("TRIM"); + status_init(); - if (boosting) - status_set("BOOST"); + if (inverter) + status_set("OB"); + else + status_set("OL"); - if (replacebatt) - status_set("RB"); + if (lowbatt) + status_set("LB"); - if (overload) - status_set("OVER"); + if (trimming) + status_set("TRIM"); - status_commit(); - } + if (boosting) + status_set("BOOST"); - if (debugging) { - fprintf(stderr, - "Poll: inverter %d charger %d vin %d vout %d vaout %d btimeleft %d\n", - inverter, charger, vin, vout, vaout, btimeleft); - fprintf(stderr, - " ampsout %5.1f vbatt %5.1f batpcnt %5.1f loadpcnt %5.1f upstemp %5.1f acfreq %5.2f ambtemp %5.1f\n", - ampsout, vbatt, battpercent, loadpercent, hstemp, acfreq, ambtemp); + if (replacebatt) + status_set("RB"); - } + if (overload) + status_set("OVER"); - /* Stuff information into info structures */ + status_commit(); + } - dstate_setinfo("input.voltage", "%05.1f", (double)vin); - dstate_setinfo("output.voltage", "%05.1f", (double)vout); - dstate_setinfo("battery.charge", "%02.1f", battpercent); - dstate_setinfo("ups.load", "%02.1f", loadpercent); - dstate_setinfo("battery.voltage", "%02.1f", vbatt); - dstate_setinfo("input.frequency", "%05.2f", (double)acfreq); - dstate_setinfo("ups.temperature", "%05.1f", (double)hstemp); - dstate_setinfo("battery.runtime", "%d", btimeleft); - dstate_setinfo("ambient.temperature", "%05.1f", (double)ambtemp); + /* FIXME: change to upsdebugx() and friends */ + if (debugging) { + fprintf(stderr, + "Poll: inverter %d charger %d vin %d vout %d vaout %d btimeleft %d\n", + inverter, charger, vin, vout, vaout, btimeleft); + fprintf(stderr, + " ampsout %5.1f vbatt %5.1f batpcnt %5.1f loadpcnt %5.1f upstemp %5.1f acfreq %5.2f ambtemp %5.1f\n", + ampsout, vbatt, battpercent, loadpercent, hstemp, acfreq, ambtemp); - dstate_dataok(); - /* Tim: With out this return, it always falls over to the - datastate() at the end of the function */ - return; - } else { + } - dstate_datastale(); + /* Stuff information into info structures */ - } /* if (execute("f\r", fstring, sizeof(fstring)) > 0) */ + dstate_setinfo("input.voltage", "%05.1f", (double)vin); + dstate_setinfo("output.voltage", "%05.1f", (double)vout); + dstate_setinfo("battery.charge", "%02.1f", battpercent); + dstate_setinfo("ups.load", "%02.1f", loadpercent); + dstate_setinfo("battery.voltage", "%02.1f", vbatt); + dstate_setinfo("input.frequency", "%05.2f", (double)acfreq); + dstate_setinfo("ups.temperature", "%05.1f", (double)hstemp); + dstate_setinfo("battery.runtime", "%d", btimeleft); + dstate_setinfo("ambient.temperature", "%05.1f", (double)ambtemp); - dstate_datastale(); - return; + dstate_dataok(); + /* Tim: With out this return, it always falls over to the + datastate() at the end of the function */ + return; + } else { + + dstate_datastale(); + + } /* if (execute("f\r", fstring, sizeof(fstring)) > 0) */ + + dstate_datastale(); + return; } static void ups_sync(void) { - char buf[256]; + char buf[256]; - printf ("Syncing: "); - fflush (stdout); + printf ("Syncing: "); + fflush (stdout); - /* A bit better sanity might be good here. As is, we expect the - human to observe the time being totally not a time. */ + /* A bit better sanity might be good here. As is, we expect the + human to observe the time being totally not a time. */ - if (execute("time\r", buf, sizeof(buf)) > 0) { - fprintf(stderr, "UPS Time: %s\n", buf); - } else { - fatalx(EXIT_FAILURE, "Error connecting to UPS"); - } + if (execute("time\r", buf, sizeof(buf)) > 0) { + fprintf(stderr, "UPS Time: %s\n", buf); + } else { + fatalx(EXIT_FAILURE, "Error connecting to UPS"); + } } /* power down the attached load immediately */ void upsdrv_shutdown(void) { /* NB: hard-wired password */ - ser_send(upsfd, "pw377\r"); - ser_send(upsfd, "off 1 a\r"); /* power off in 1 second and restart when line power returns */ + ser_send(upsfd, "pw377\r"); + ser_send(upsfd, "off 1 a\r"); /* power off in 1 second and restart when line power returns */ } /* list flags and values that you want to receive via -x */ @@ -369,12 +392,12 @@ static void sync_serial(void) { /* Begin code stolen from bestups.c */ static void setup_serial(void) -{ - struct termios tio; - +{ + struct termios tio; + if (tcgetattr(upsfd, &tio) == -1) fatal_with_errno(EXIT_FAILURE, "tcgetattr"); - + tio.c_iflag = IXON | IXOFF; tio.c_oflag = 0; tio.c_cflag = (CS8 | CREAD | HUPCL | CLOCAL); @@ -399,99 +422,100 @@ static void setup_serial(void) void upsdrv_initups () { - char temp[256], fcstring[512]; + char temp[256], fcstring[512]; - upsfd = ser_open(device_path); - ser_set_speed(upsfd, device_path, B1200); - setup_serial(); - ups_sync(); + upsfd = ser_open(device_path); + ser_set_speed(upsfd, device_path, B1200); + setup_serial(); + ups_sync(); - fc.model = UNKNOWN; - /* Obtain Model */ - if (execute("id\r", fcstring, sizeof(fcstring)) < 1) { - fatalx(EXIT_FAILURE, "Failed execute in ups_ident()"); - } - - /* response is a one-line packed string starting with $ */ - if (memcmp(fcstring, "Unit", 4)) { - fatalx(EXIT_FAILURE, - "Bad response from formatconfig command in ups_ident()\n" - "id: %s\n", fcstring - ); - } + fc.model = UNKNOWN; + /* Obtain Model */ + if (execute("id\r", fcstring, sizeof(fcstring)) < 1) { + fatalx(EXIT_FAILURE, "Failed execute in ups_ident()"); + } - if (debugging) - fprintf(stderr, "id: %s\n", fcstring); - - /* chars 4:2 are a two-digit ascii hex enumerated model code */ - memcpy(temp, fcstring+9, 2); - temp[2] = '\0'; + /* response is a one-line packed string starting with $ */ + if (memcmp(fcstring, "Unit", 4)) { + fatalx(EXIT_FAILURE, + "Bad response from formatconfig command in ups_ident()\n" + "id: %s\n", fcstring + ); + } - if (memcmp(temp, "ME", 2) == 0) { - fc.model = ME3100; - } else if ((memcmp(temp, "RE", 2) == 0)) { - fc.model = RE1800; - } else if (memcmp(temp, "C1", 2) == 0) { - /* Better way to identify unit is using "d 15\r", which results in - "15 M# MD1KVA", "id\r" yields "Unit ID "C1K03588"" */ - fc.model = MD1KVA; - } - - switch(fc.model) { - case ME3100: - fc.va = 3100; - fc.watts = 2200; - /* determine shutdown battery voltage */ - if (execute("d 29\r", fcstring, sizeof(fcstring)) > 0) { - sscanf(fcstring, "29 LowBat %f", &fc.emptyvolts); - } - /* determine fully charged battery voltage */ - if (execute("d 31\r", fcstring, sizeof(fcstring)) > 0) { - sscanf(fcstring, "31 HiBatt %f", &fc.fullvolts); - } - fc.fullvolts = 54.20; - /* determine "ideal" voltage by a guess */ - fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; - break; - case RE1800: - fc.va = 1800; - fc.watts = 1200; - /* determine shutdown battery voltage */ - if (execute("d 29\r", fcstring, sizeof(fcstring)) > 0) { - sscanf(fcstring, "29 LowBat %f", &fc.emptyvolts); - } - /* determine fully charged battery voltage */ - if (execute("d 31\r", fcstring, sizeof(fcstring)) > 0) { - sscanf(fcstring, "31 HiBatt %f", &fc.fullvolts); - } - fc.fullvolts = 54.20; - /* determine "ideal" voltage by a guess */ - fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; - break; - case MD1KVA: - fc.va = 1100; - fc.watts = 770; /* Approximate, based on 0.7 power factor */ - /* determine shutdown battery voltage */ - if (execute("d 27\r", fcstring, sizeof(fcstring)) > 0) { - sscanf(fcstring, "27 LowBatt %f", &fc.emptyvolts); - } - /* determine fully charged battery voltage */ - if (execute("d 28\r", fcstring, sizeof(fcstring)) > 0) { - sscanf(fcstring, "28 Hi Batt %f", &fc.fullvolts); - } - fc.fullvolts = 13.70; - /* determine "ideal" voltage by a guess */ - fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; - break; - default: - fatalx(EXIT_FAILURE, "Uknown model %s in ups_ident()", temp); - } + /* FIXME: upsdebugx() */ + if (debugging) + fprintf(stderr, "id: %s\n", fcstring); - fc.valid = 1; - return; + /* chars 4:2 are a two-digit ascii hex enumerated model code */ + memcpy(temp, fcstring+9, 2); + temp[2] = '\0'; + + if (memcmp(temp, "ME", 2) == 0) { + fc.model = ME3100; + } else if ((memcmp(temp, "RE", 2) == 0)) { + fc.model = RE1800; + } else if (memcmp(temp, "C1", 2) == 0) { + /* Better way to identify unit is using "d 15\r", which results in + "15 M# MD1KVA", "id\r" yields "Unit ID "C1K03588"" */ + fc.model = MD1KVA; + } + + switch(fc.model) { + case ME3100: + fc.va = 3100; + fc.watts = 2200; + /* determine shutdown battery voltage */ + if (execute("d 29\r", fcstring, sizeof(fcstring)) > 0) { + sscanf(fcstring, "29 LowBat %f", &fc.emptyvolts); + } + /* determine fully charged battery voltage */ + if (execute("d 31\r", fcstring, sizeof(fcstring)) > 0) { + sscanf(fcstring, "31 HiBatt %f", &fc.fullvolts); + } + fc.fullvolts = 54.20; + /* determine "ideal" voltage by a guess */ + fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; + break; + case RE1800: + fc.va = 1800; + fc.watts = 1200; + /* determine shutdown battery voltage */ + if (execute("d 29\r", fcstring, sizeof(fcstring)) > 0) { + sscanf(fcstring, "29 LowBat %f", &fc.emptyvolts); + } + /* determine fully charged battery voltage */ + if (execute("d 31\r", fcstring, sizeof(fcstring)) > 0) { + sscanf(fcstring, "31 HiBatt %f", &fc.fullvolts); + } + fc.fullvolts = 54.20; + /* determine "ideal" voltage by a guess */ + fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; + break; + case MD1KVA: + fc.va = 1100; + fc.watts = 770; /* Approximate, based on 0.7 power factor */ + /* determine shutdown battery voltage */ + if (execute("d 27\r", fcstring, sizeof(fcstring)) > 0) { + sscanf(fcstring, "27 LowBatt %f", &fc.emptyvolts); + } + /* determine fully charged battery voltage */ + if (execute("d 28\r", fcstring, sizeof(fcstring)) > 0) { + sscanf(fcstring, "28 Hi Batt %f", &fc.fullvolts); + } + fc.fullvolts = 13.70; + /* determine "ideal" voltage by a guess */ + fc.idealbvolts = ((fc.fullvolts - fc.emptyvolts) * 0.7) + fc.emptyvolts; + break; + default: + fatalx(EXIT_FAILURE, "Unknown model %s in ups_ident()", temp); + } + + fc.valid = 1; + return; } void upsdrv_cleanup(void) { - ser_close(upsfd, device_path); + ser_close(upsfd, device_path); } diff --git a/drivers/bestups.c b/drivers/bestups.c index aa142da..4f63aa5 100644 --- a/drivers/bestups.c +++ b/drivers/bestups.c @@ -1,5 +1,10 @@ /* bestups.c - model specific routines for Best-UPS Fortress models + OBSOLETION WARNING: Please to not base new development on this + codebase, instead create a new subdriver for nutdrv_qx which + generally covers all Megatec/Qx protocol family and aggregates + device support from such legacy drivers over time. + Copyright (C) 1999 Russell Kroll ID config option by Jason White @@ -23,7 +28,7 @@ #include "serial.h" #define DRIVER_NAME "Best UPS driver" -#define DRIVER_VERSION "1.05" +#define DRIVER_VERSION "1.07" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -122,13 +127,14 @@ static int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } static int get_ident(char *buf, size_t bufsize) { - int i, ret; + int i; + ssize_t ret; char *ID; ID = getval("ID"); /* user-supplied override from ups.conf */ @@ -142,7 +148,7 @@ static int get_ident(char *buf, size_t bufsize) for (i = 0; i < MAXTRIES; i++) { ser_send_pace(upsfd, UPSDELAY, "\rID\r"); - ret = ser_get_line(upsfd, buf, bufsize, ENDCHAR, "", + ret = ser_get_line(upsfd, buf, bufsize, ENDCHAR, "", SER_WAIT_SEC, SER_WAIT_USEC); if (ret > 0) @@ -210,7 +216,7 @@ static void ups_ident(void) if ((!model) || (!rating)) { fatalx(EXIT_FAILURE, "Didn't get a valid ident string"); } - + model_set(model, rating); /* Battery voltage multiplier */ @@ -240,12 +246,13 @@ static void ups_ident(void) static void ups_sync(void) { char buf[256]; - int i, ret; + int i; + ssize_t ret; for (i = 0; i < MAXTRIES; i++) { ser_send_pace(upsfd, UPSDELAY, "\rQ1\r"); - ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "", + ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "", SER_WAIT_SEC, SER_WAIT_USEC); /* return once we get something that looks usable */ @@ -263,7 +270,7 @@ void upsdrv_initinfo(void) ups_sync(); ups_ident(); - printf("Detected %s %s on %s\n", dstate_getinfo("ups.mfr"), + printf("Detected %s %s on %s\n", dstate_getinfo("ups.mfr"), dstate_getinfo("ups.model"), device_path); /* paranoia - cancel any shutdown that might already be running */ @@ -277,13 +284,14 @@ void upsdrv_initinfo(void) static int ups_on_line(void) { - int i, ret; + int i; + ssize_t ret; char temp[256], pstat[32]; for (i = 0; i < MAXTRIES; i++) { ser_send_pace(upsfd, UPSDELAY, "\rQ1\r"); - ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, "", + ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, "", SER_WAIT_SEC, SER_WAIT_USEC); /* Q1 must return 46 bytes starting with a ( */ @@ -303,7 +311,7 @@ static int ups_on_line(void) upslogx(LOG_ERR, "Status read failed: assuming on battery"); return 0; /* on battery */ -} +} void upsdrv_shutdown(void) { @@ -319,10 +327,10 @@ void upsdrv_shutdown(void) void upsdrv_updateinfo(void) { - char involt[16], outvolt[16], loadpct[16], acfreq[16], + char involt[16], outvolt[16], loadpct[16], acfreq[16], battvolt[16], upstemp[16], pstat[16], buf[256]; float bvoltp; - int ret; + ssize_t ret; ret = ser_send_pace(upsfd, UPSDELAY, "\rQ1\r"); @@ -335,7 +343,7 @@ void upsdrv_updateinfo(void) /* these things need a long time to respond completely */ usleep(200000); - ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "", + ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "", SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 1) { @@ -345,13 +353,13 @@ void upsdrv_updateinfo(void) } if (ret < 46) { - ser_comm_fail("Poll failed: short read (got %d bytes)", ret); + ser_comm_fail("Poll failed: short read (got %zd bytes)", ret); dstate_datastale(); return; } if (ret > 46) { - ser_comm_fail("Poll failed: response too long (got %d bytes)", + ser_comm_fail("Poll failed: response too long (got %zd bytes)", ret); dstate_datastale(); return; @@ -366,7 +374,7 @@ void upsdrv_updateinfo(void) ser_comm_good(); - sscanf(buf, "%*c%s %*s %s %s %s %s %s %s", involt, outvolt, + sscanf(buf, "%*c%s %*s %s %s %s %s %s %s", involt, outvolt, loadpct, acfreq, battvolt, upstemp, pstat); /* Guesstimation of battery charge left (inaccurate) */ @@ -422,11 +430,21 @@ void upsdrv_help(void) void upsdrv_makevartable(void) { addvar(VAR_VALUE, "nombattvolt", "Override nominal battery voltage"); + addvar(VAR_VALUE, "battvoltmult", "Battery voltage multiplier"); addvar(VAR_VALUE, "ID", "Force UPS ID response string"); } void upsdrv_initups(void) { + upsdebugx(0, + "Please note that this driver is deprecated and will not receive\n" + "new development. If it works for managing your devices - fine,\n" + "but if you are running it to try setting up a new device, please\n" + "consider the newer nutdrv_qx instead, which should handle all 'Qx'\n" + "protocol variants for NUT. (Please also report if your device works\n" + "with this driver, but nutdrv_qx would not actually support it with\n" + "any subdriver!)\n"); + upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); } diff --git a/drivers/blazer.c b/drivers/blazer.c index 0b3cc8f..6e06b42 100644 --- a/drivers/blazer.c +++ b/drivers/blazer.c @@ -1,10 +1,17 @@ /* * blazer.c: driver core for Megatec/Q1 protocol based UPSes * - * A document describing the protocol implemented by this driver can be - * found online at "http://www.networkupstools.org/protocols/megatec.html". + * OBSOLETION WARNING: Please to not base new development on this + * codebase, instead create a new subdriver for nutdrv_qx which + * generally covers all Megatec/Qx protocol family and aggregates + * device support from such legacy drivers over time. * - * Copyright (C) 2008,2009 - Arjen de Korte + * A document describing the protocol implemented by this driver can be + * found online at http://www.networkupstools.org/ups-protocols/megatec.html + * + * Copyright (C) + * 2008,2009 - Arjen de Korte + * 2012 - Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,11 +30,10 @@ #include "main.h" #include "blazer.h" +#include "nut_float.h" -#include - -static int ondelay = 3; /* minutes */ -static int offdelay = 30; /* seconds */ +static long ondelay = 3; /* minutes */ +static long offdelay = 30; /* seconds */ static int proto; static int online = 1; @@ -76,7 +82,7 @@ static const struct { { "mustek", "QS\r", "F\r", "I\r" }, { "megatec/old", "D\r", "F\r", "I\r" }, { "zinto", "Q1\r", "F\r", "FW?\r" }, - { NULL } + { NULL, NULL, NULL, NULL } }; @@ -175,7 +181,7 @@ static int blazer_status(const char *cmd) { "input.frequency", "%.1f", strtod }, { "battery.voltage", "%.2f", blazer_battery }, { "ups.temperature", "%.1f", strtod }, - { NULL } + { NULL, NULL, NULL } }; char buf[SMALLBUF], *val, *last = NULL; @@ -209,7 +215,20 @@ static int blazer_status(const char *cmd) continue; } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif dstate_setinfo(status[i].var, status[i].fmt, status[i].conv(val, NULL)); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + } if (!val) { @@ -223,9 +242,9 @@ static int blazer_status(const char *cmd) } if (val[7] == '1') { /* Beeper On */ - dstate_setinfo("beeper.status", "enabled"); + dstate_setinfo("ups.beeper.status", "enabled"); } else { - dstate_setinfo("beeper.status", "disabled"); + dstate_setinfo("ups.beeper.status", "disabled"); } if (val[4] == '1') { /* UPS Type is Standby (0 is On_line) */ @@ -280,6 +299,7 @@ static int blazer_status(const char *cmd) if (val[6] == '1') { /* Shutdown Active */ alarm_set("Shutdown imminent!"); + status_set("FSD"); } alarm_commit(); @@ -301,7 +321,7 @@ static int blazer_rating(const char *cmd) { "input.current.nominal", "%.1f", strtod }, { "battery.voltage.nominal", "%.1f", blazer_packs }, { "input.frequency.nominal", "%.0f", strtod }, - { NULL } + { NULL, NULL, NULL } }; char buf[SMALLBUF], *val, *last = NULL; @@ -335,7 +355,20 @@ static int blazer_rating(const char *cmd) continue; } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif dstate_setinfo(rating[i].var, rating[i].fmt, rating[i].conv(val, NULL)); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + } return 0; @@ -351,7 +384,7 @@ static int blazer_vendor(const char *cmd) { "ups.mfr", 15 }, { "ups.model", 10 }, { "ups.firmware", 10 }, - { NULL } + { NULL, 0 } }; char buf[SMALLBUF]; @@ -378,7 +411,7 @@ static int blazer_vendor(const char *cmd) snprintf(val, sizeof(val), "%.*s", information[i].len, &buf[index]); - dstate_setinfo(information[i].var, "%s", rtrim(val, ' ')); + dstate_setinfo(information[i].var, "%s", str_rtrim(val, ' ')); } return 0; @@ -398,12 +431,14 @@ static int blazer_instcmd(const char *cmdname, const char *extra) { "test.battery.start.deep", "TL\r" }, { "test.battery.start.quick", "T\r" }, { "test.battery.stop", "CT\r" }, - { NULL } + { NULL, NULL } }; char buf[SMALLBUF] = ""; int i; + upslogx(LOG_INFO, "instcmd(%s, %s)", cmdname, extra ? extra : "[NULL]"); + for (i = 0; instcmd[i].cmd; i++) { if (strcasecmp(cmdname, instcmd[i].cmd)) { @@ -414,10 +449,14 @@ static int blazer_instcmd(const char *cmdname, const char *extra) /* * If a command is invalid, it will be echoed back + * As an exception, Best UPS units will report "ACK" in case of success! + * Other UPSes will reply "(ACK" in case of success. */ if (blazer_command(buf, buf, sizeof(buf)) > 0) { - upslogx(LOG_ERR, "instcmd: command [%s] failed", cmdname); - return STAT_INSTCMD_FAILED; + if (strncmp(buf, "ACK", 3) && strncmp(buf, "(ACK", 4)) { + upslogx(LOG_ERR, "instcmd: command [%s] failed", cmdname); + return STAT_INSTCMD_FAILED; + } } upslogx(LOG_INFO, "instcmd: command [%s] handled", cmdname); @@ -425,36 +464,77 @@ static int blazer_instcmd(const char *cmdname, const char *extra) } if (!strcasecmp(cmdname, "shutdown.return")) { - if (offdelay < 60) { - snprintf(buf, sizeof(buf), "S.%dR%04d\r", offdelay / 6, ondelay); - } else { - snprintf(buf, sizeof(buf), "S%02dR%04d\r", offdelay / 60, ondelay); - } - } else if (!strcasecmp(cmdname, "shutdown.stayoff")) { - if (offdelay < 60) { - snprintf(buf, sizeof(buf), "S.%dR0000\r", offdelay / 6); - } else { - snprintf(buf, sizeof(buf), "S%02dR0000\r", offdelay / 60); - } - } else if (!strcasecmp(cmdname, "test.battery.start")) { - int delay = extra ? strtol(extra, NULL, 10) : 10; - if ((delay < 0) || (delay > 99)) { + /* + * Sn: Shutdown after n minutes and then turn on when mains is back + * SnRm: Shutdown after n minutes and then turn on after m minutes + * Accepted values for n: .2 -> .9 , 01 -> 10 + * Accepted values for m: 0001 -> 9999 + * Note: "S01R0001" and "S01R0002" may not work on early (GE) + * firmware versions. The failure mode is that the UPS turns + * off and never returns. The fix is to push the return value + * up by 2, i.e. S01R0003, and it will return online properly. + * (thus the default of ondelay=3 mins) + */ + + if (ondelay == 0) { + + if (offdelay < 60) { + snprintf(buf, sizeof(buf), "S.%ld\r", offdelay / 6); + } else { + snprintf(buf, sizeof(buf), "S%02ld\r", offdelay / 60); + } + + } else if (offdelay < 60) { + + snprintf(buf, sizeof(buf), "S.%ldR%04ld\r", offdelay / 6, ondelay); + + } else { + + snprintf(buf, sizeof(buf), "S%02ldR%04ld\r", offdelay / 60, ondelay); + + } + + } else if (!strcasecmp(cmdname, "shutdown.stayoff")) { + + /* + * SnR0000 + * Shutdown after n minutes and stay off + * Accepted values for n: .2 -> .9 , 01 -> 10 + */ + + if (offdelay < 60) { + snprintf(buf, sizeof(buf), "S.%ldR0000\r", offdelay / 6); + } else { + snprintf(buf, sizeof(buf), "S%02ldR0000\r", offdelay / 60); + } + + } else if (!strcasecmp(cmdname, "test.battery.start")) { + long delay = extra ? strtol(extra, NULL, 10) : 10; + + if ((delay < 1) || (delay > 99)) { + upslogx(LOG_ERR, + "instcmd: command [%s] failed, delay [%s] out of range", + cmdname, extra); return STAT_INSTCMD_FAILED; } - snprintf(buf, sizeof(buf), "T%02d\r", delay); + snprintf(buf, sizeof(buf), "T%02ld\r", delay); } else { upslogx(LOG_ERR, "instcmd: command [%s] not found", cmdname); return STAT_INSTCMD_UNKNOWN; } /* - * If a command is invalid, it will be echoed back + * If a command is invalid, it will be echoed back. + * As an exception, Best UPS units will report "ACK" in case of success! + * Other UPSes will reply "(ACK" in case of success. */ if (blazer_command(buf, buf, sizeof(buf)) > 0) { - upslogx(LOG_ERR, "instcmd: command [%s] failed", cmdname); - return STAT_INSTCMD_FAILED; + if (strncmp(buf, "ACK", 3) && strncmp(buf, "(ACK", 4)) { + upslogx(LOG_ERR, "instcmd: command [%s] failed", cmdname); + return STAT_INSTCMD_FAILED; + } } upslogx(LOG_INFO, "instcmd: command [%s] handled", cmdname); @@ -488,7 +568,7 @@ void blazer_initups(void) } if ((ondelay < 0) || (ondelay > 9999)) { - fatalx(EXIT_FAILURE, "Start delay '%d' out of range [0..9999]", ondelay); + fatalx(EXIT_FAILURE, "Start delay '%ld' out of range [0..9999]", ondelay); } val = getval("offdelay"); @@ -496,8 +576,8 @@ void blazer_initups(void) offdelay = strtol(val, NULL, 10); } - if ((offdelay < 6) || (offdelay > 600)) { - fatalx(EXIT_FAILURE, "Shutdown delay '%d' out of range [6..600]", offdelay); + if ((offdelay < 12) || (offdelay > 600)) { + fatalx(EXIT_FAILURE, "Shutdown delay '%ld' out of range [12..600]", offdelay); } /* Truncate to nearest setable value */ @@ -523,6 +603,22 @@ static void blazer_initbattery(void) { const char *val; + /* If no values were provided by the user in ups.conf, try to guesstimate + * battery.charge, but announce it! */ + if ( (!d_equal(batt.volt.nom, 1)) && ((d_equal(batt.volt.high, -1)) || (d_equal(batt.volt.low, -1)))) { + upslogx(LOG_INFO, "No values provided for battery high/low voltages in ups.conf\n"); + + /* Basic formula, which should cover most cases */ + batt.volt.low = 104 * batt.volt.nom / 120; + batt.volt.high = 130 * batt.volt.nom / 120; + + /* Publish these data too */ + dstate_setinfo("battery.voltage.low", "%.2f", batt.volt.low); + dstate_setinfo("battery.voltage.high", "%.2f", batt.volt.high); + + upslogx(LOG_INFO, "Using 'guestimation' (low: %f, high: %f)!", batt.volt.low, batt.volt.high); + } + val = getval("runtimecal"); if (val) { double rh, lh, rl, ll; @@ -600,9 +696,18 @@ void blazer_initinfo(void) const char *protocol = getval("protocol"); int retry; + upsdebugx(0, + "Please note that this driver is deprecated and will not receive\n" + "new development. If it works for managing your devices - fine,\n" + "but if you are running it to try setting up a new device, please\n" + "consider the newer nutdrv_qx instead, which should handle all 'Qx'\n" + "protocol variants for NUT. (Please also report if your device works\n" + "with this driver, but nutdrv_qx would not actually support it with\n" + "any subdriver!)\n"); + for (proto = 0; command[proto].status; proto++) { - int ret; + int ret = -1; if (protocol && strcasecmp(protocol, command[proto].name)) { upsdebugx(2, "Skipping %s protocol...", command[proto].name); @@ -634,7 +739,7 @@ void blazer_initinfo(void) } if (command[proto].rating && !testvar("norating")) { - int ret; + int ret = -1; for (retry = 1; retry <= MAXTRIES; retry++) { @@ -654,7 +759,7 @@ void blazer_initinfo(void) } if (command[proto].vendor && !testvar("novendor")) { - int ret; + int ret = -1; for (retry = 1; retry <= MAXTRIES; retry++) { @@ -675,8 +780,8 @@ void blazer_initinfo(void) blazer_initbattery(); - dstate_setinfo("ups.delay.start", "%d", 60 * ondelay); - dstate_setinfo("ups.delay.shutdown", "%d", offdelay); + dstate_setinfo("ups.delay.start", "%ld", 60 * ondelay); + dstate_setinfo("ups.delay.shutdown", "%ld", offdelay); dstate_addcmd("beeper.toggle"); dstate_addcmd("load.off"); @@ -744,22 +849,37 @@ void upsdrv_updateinfo(void) dstate_dataok(); } +void upsdrv_shutdown(void) + __attribute__((noreturn)); void upsdrv_shutdown(void) { int retry; + /* Stop pending shutdowns */ for (retry = 1; retry <= MAXTRIES; retry++) { if (blazer_instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { continue; } + break; + + } + + if (retry > MAXTRIES) { + upslogx(LOG_NOTICE, "No shutdown pending"); + } + + /* Shutdown */ + for (retry = 1; retry <= MAXTRIES; retry++) { + if (blazer_instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) { continue; } - fatalx(EXIT_SUCCESS, "Shutting down in %d seconds", offdelay); + fatalx(EXIT_SUCCESS, "Shutting down in %ld seconds", offdelay); + } fatalx(EXIT_FAILURE, "Shutdown failed!"); diff --git a/drivers/blazer.h b/drivers/blazer.h index 2337858..6a8c71c 100644 --- a/drivers/blazer.h +++ b/drivers/blazer.h @@ -1,6 +1,11 @@ /* * blazer.h: defines/macros for Megatec/Q1 protocol based UPSes * + * OBSOLETION WARNING: Please to not base new development on this + * codebase, instead create a new subdriver for nutdrv_qx which + * generally covers all Megatec/Qx protocol family and aggregates + * device support from such legacy drivers over time. + * * A document describing the protocol implemented by this driver can be * found online at "http://www.networkupstools.org/protocols/megatec.html". * @@ -40,7 +45,7 @@ * Returns < 0 on error, 0 on timeout and the number of bytes send/read on * success. */ -int blazer_command(const char *cmd, char *buf, size_t buflen); +ssize_t blazer_command(const char *cmd, char *buf, size_t buflen); void blazer_makevartable(void); void blazer_initups(void); diff --git a/drivers/blazer_ser.c b/drivers/blazer_ser.c index fbc7e35..a906181 100644 --- a/drivers/blazer_ser.c +++ b/drivers/blazer_ser.c @@ -1,6 +1,11 @@ /* * blazer_ser.c: support for Megatec/Q1 serial protocol based UPSes * + * OBSOLETION WARNING: Please to not base new development on this + * codebase, instead create a new subdriver for nutdrv_qx which + * generally covers all Megatec/Qx protocol family and aggregates + * device support from such legacy drivers over time. + * * A document describing the protocol implemented by this driver can be * found online at "http://www.networkupstools.org/protocols/megatec.html". * @@ -26,7 +31,7 @@ #include "blazer.h" #define DRIVER_NAME "Megatec/Q1 protocol serial driver" -#define DRIVER_VERSION "1.51" +#define DRIVER_VERSION "1.58" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -37,17 +42,17 @@ upsdrv_info_t upsdrv_info = { { NULL } }; -#define SER_WAIT_SEC 1 +#define SER_WAIT_SEC 1 /* 3 seconds for Best UPS */ /* * Generic command processing function. Send a command and read a reply. * Returns < 0 on error, 0 on timeout and the number of bytes read on * success. */ -int blazer_command(const char *cmd, char *buf, size_t buflen) +ssize_t blazer_command(const char *cmd, char *buf, size_t buflen) { #ifndef TESTING - int ret; + ssize_t ret; ser_flush_io(upsfd); @@ -58,7 +63,7 @@ int blazer_command(const char *cmd, char *buf, size_t buflen) return ret; } - upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); + upsdebugx(3, "send: '%.*s'", (int)strcspn(cmd, "\r"), cmd); ret = ser_get_buf(upsfd, buf, buflen, SER_WAIT_SEC, 0); @@ -67,7 +72,7 @@ int blazer_command(const char *cmd, char *buf, size_t buflen) return ret; } - upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); + upsdebugx(3, "read: '%.*s'", (int)strcspn(buf, "\r"), buf); return ret; #else const struct { @@ -90,17 +95,18 @@ int blazer_command(const char *cmd, char *buf, size_t buflen) continue; } - return snprintf(buf, buflen, "%s", testing[i].answer); + /* TODO: Range-check int vs ssize_t values */ + return (ssize_t)snprintf(buf, buflen, "%s", testing[i].answer); } - return snprintf(buf, buflen, "%s", testing[i].cmd); + return (ssize_t)snprintf(buf, buflen, "%s", testing[i].cmd); #endif } void upsdrv_help(void) { - printf("Read The Fine Manual ('man 8 blazer')\n"); + printf("Read The Fine Manual ('man 8 blazer_ser')\n"); } @@ -114,6 +120,7 @@ void upsdrv_makevartable(void) void upsdrv_initups(void) { +#ifndef TESTING const struct { const char *val; const int dtr; @@ -123,13 +130,13 @@ void upsdrv_initups(void) { "reverse", 0, 1 }, { "both", 1, 1 }, { "none", 0, 0 }, - { NULL } + { NULL, 0, 0 } }; int i; const char *val; -#ifndef TESTING + struct termios tio; /* diff --git a/drivers/blazer_usb.c b/drivers/blazer_usb.c index b303596..fc3f78b 100644 --- a/drivers/blazer_usb.c +++ b/drivers/blazer_usb.c @@ -1,11 +1,17 @@ /* * blazer_usb.c: support for Megatec/Q1 USB protocol based UPSes * + * OBSOLETION WARNING: Please to not base new development on this + * codebase, instead create a new subdriver for nutdrv_qx which + * generally covers all Megatec/Qx protocol family and aggregates + * device support from such legacy drivers over time. + * * A document describing the protocol implemented by this driver can be * found online at "http://www.networkupstools.org/protocols/megatec.html". * * Copyright (C) 2003-2009 Arjen de Korte - * Copyright (C) 2011 Arnaud Quette + * Copyright (C) 2011-2012 Arnaud Quette + * Copyright (C) 2016 Eaton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,12 +29,12 @@ */ #include "main.h" -#include "libusb.h" +#include "nut_libusb.h" #include "usb-common.h" #include "blazer.h" #define DRIVER_NAME "Megatec/Q1 protocol USB driver" -#define DRIVER_VERSION "0.04" +#define DRIVER_VERSION "0.14" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -40,6 +46,8 @@ upsdrv_info_t upsdrv_info = { { NULL } }; +#ifndef TESTING + static usb_communication_subdriver_t *usb = &usb_subdriver; static usb_dev_handle *udev = NULL; static USBDevice_t usbdevice; @@ -59,15 +67,15 @@ static int cypress_command(const char *cmd, char *buf, size_t buflen) memset(tmp, 0, sizeof(tmp)); snprintf(tmp, sizeof(tmp), "%s", cmd); - for (i = 0; i < strlen(tmp); i += ret) { + for (i = 0; i < strlen(tmp); i += (size_t)ret) { /* Write data in 8-byte chunks */ /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); */ ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, - 0x09, 0x200, 0, &tmp[i], 8, 1000); + 0x09, 0x200, 0, (usb_ctrl_charbuf)&tmp[i], 8, 5000); if (ret <= 0) { - upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout"); + upsdebugx(3, "send: %s", ret ? nut_usb_strerror(ret) : "timeout"); return ret; } } @@ -76,24 +84,27 @@ static int cypress_command(const char *cmd, char *buf, size_t buflen) memset(buf, 0, buflen); - for (i = 0; (i <= buflen-8) && (strchr(buf, '\r') == NULL); i += ret) { + for (i = 0; (i <= buflen-8) && (strchr(buf, '\r') == NULL); i += (size_t)ret) { /* Read data in 8-byte chunks */ /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */ - ret = usb_interrupt_read(udev, 0x81, &buf[i], 8, 1000); + ret = usb_interrupt_read(udev, + 0x81, + (usb_ctrl_charbuf)&buf[i], 8, 1000); /* * Any errors here mean that we are unable to read a reply (which * will happen after successfully writing a command to the UPS) */ if (ret <= 0) { - upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout"); + upsdebugx(3, "read: %s", ret ? nut_usb_strerror(ret) : "timeout"); return ret; } } upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); - return i; + /* TODO: Range-check before cast */ + return (int)i; } @@ -107,7 +118,9 @@ static int phoenix_command(const char *cmd, char *buf, size_t buflen) /* Read data in 8-byte chunks */ /* ret = usb->get_interrupt(udev, (unsigned char *)tmp, 8, 1000); */ - ret = usb_interrupt_read(udev, 0x81, tmp, 8, 1000); + ret = usb_interrupt_read(udev, + 0x81, + (usb_ctrl_charbuf)&tmp, 8, 1000); /* * This USB to serial implementation is crappy. In order to read correct @@ -116,32 +129,34 @@ static int phoenix_command(const char *cmd, char *buf, size_t buflen) */ switch (ret) { - case -EPIPE: /* Broken pipe */ + case ERROR_PIPE: /** Pipe error or Broken pipe */ usb_clear_halt(udev, 0x81); - case -ETIMEDOUT: /* Connection timed out */ + break; + + case ERROR_TIMEOUT: /** Operation or Connection timed out */ break; } if (ret < 0) { - upsdebugx(3, "flush: %s", usb_strerror()); + upsdebugx(3, "flush: %s", nut_usb_strerror(ret)); break; } - upsdebug_hex(4, "dump", tmp, ret); + upsdebug_hex(4, "dump", tmp, (size_t)ret); } memset(tmp, 0, sizeof(tmp)); snprintf(tmp, sizeof(tmp), "%s", cmd); - for (i = 0; i < strlen(tmp); i += ret) { + for (i = 0; i < strlen(tmp); i += (size_t)ret) { /* Write data in 8-byte chunks */ /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); */ ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, - 0x09, 0x200, 0, &tmp[i], 8, 1000); + 0x09, 0x200, 0, (usb_ctrl_charbuf)&tmp[i], 8, 1000); if (ret <= 0) { - upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout"); + upsdebugx(3, "send: %s", ret ? nut_usb_strerror(ret) : "timeout"); return ret; } } @@ -150,43 +165,46 @@ static int phoenix_command(const char *cmd, char *buf, size_t buflen) memset(buf, 0, buflen); - for (i = 0; (i <= buflen-8) && (strchr(buf, '\r') == NULL); i += ret) { + for (i = 0; (i <= buflen-8) && (strchr(buf, '\r') == NULL); i += (size_t)ret) { /* Read data in 8-byte chunks */ /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */ - ret = usb_interrupt_read(udev, 0x81, &buf[i], 8, 1000); + ret = usb_interrupt_read(udev, + 0x81, + (usb_ctrl_charbuf)&buf[i], 8, 1000); /* * Any errors here mean that we are unable to read a reply (which * will happen after successfully writing a command to the UPS) */ if (ret <= 0) { - upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout"); + upsdebugx(3, "read: %s", ret ? nut_usb_strerror(ret) : "timeout"); return ret; } } upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); - return i; + /* TODO: Range-check before cast */ + return (int)i; } static int ippon_command(const char *cmd, char *buf, size_t buflen) { char tmp[64]; - int ret; + int ret, len; size_t i; snprintf(tmp, sizeof(tmp), "%s", cmd); - for (i = 0; i < strlen(tmp); i += ret) { + for (i = 0; i < strlen(tmp); i += (size_t)ret) { /* Write data in 8-byte chunks */ ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, - 0x09, 0x2, 0, &tmp[i], 8, 1000); + 0x09, 0x2, 0, (usb_ctrl_charbuf)&tmp[i], 8, 1000); if (ret <= 0) { - upsdebugx(3, "send: %s", (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out"); + upsdebugx(3, "send: %s", (ret != ERROR_TIMEOUT) ? nut_usb_strerror(ret) : "Connection timed out"); return ret; } } @@ -194,21 +212,32 @@ static int ippon_command(const char *cmd, char *buf, size_t buflen) upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp); /* Read all 64 bytes of the reply in one large chunk */ - ret = usb_interrupt_read(udev, 0x81, tmp, sizeof(tmp), 1000); + ret = usb_interrupt_read(udev, + 0x81, + (usb_ctrl_charbuf)&tmp, sizeof(tmp), 1000); /* * Any errors here mean that we are unable to read a reply (which * will happen after successfully writing a command to the UPS) */ if (ret <= 0) { - upsdebugx(3, "read: %s", (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out"); + upsdebugx(3, "read: %s", (ret != ERROR_TIMEOUT) ? nut_usb_strerror(ret) : "Connection timed out"); return ret; } - snprintf(buf, buflen, "%.*s", ret, tmp); - - upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); - return ret; + /* + * As Ippon will always return 64 bytes in response, we have to + * calculate and return length of actual response data here. + * Empty response will look like 0x00 0x0D, otherwise it will be + * data string terminated by 0x0D. + */ + len = (int)strcspn(tmp, "\r"); + upsdebugx(3, "read: %.*s", len, tmp); + if (len > 0) { + len ++; + } + snprintf(buf, buflen, "%.*s", len, tmp); + return len; } @@ -232,7 +261,7 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) { "Q\r", 0x07, '\r' }, { "C\r", 0x0b, '\r' }, { "CT\r", 0x0b, '\r' }, - { NULL } + { NULL, 0, '\0' } }; int i; @@ -251,14 +280,16 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) if (langid_fix != -1) { /* Apply langid_fix value */ - ret = usb_get_string(udev, command[i].index, langid_fix, buf, buflen); + ret = usb_get_string(udev, command[i].index, langid_fix, + (usb_ctrl_charbuf)buf, buflen); } else { - ret = usb_get_string_simple(udev, command[i].index, buf, buflen); + ret = usb_get_string_simple(udev, command[i].index, + (usb_ctrl_charbuf)buf, buflen); } if (ret <= 0) { - upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout"); + upsdebugx(3, "read: %s", ret ? nut_usb_strerror(ret) : "timeout"); return ret; } @@ -268,7 +299,7 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) if (langid_fix != -1) { /* Limit this check, at least for now */ /* Invalid receive size - message corrupted */ - if (ret != buf[0]) + if (ret != buf[0]) { upsdebugx(1, "size mismatch: %d / %d", ret, buf[0]); continue; @@ -277,7 +308,7 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) /* Simple unicode -> ASCII inplace conversion * FIXME: this code is at least shared with mge-shut/libshut * Create a common function? */ - unsigned int di, si, size = buf[0]; + size_t di, si, size = (size_t)buf[0]; for (di = 0, si = 2; si < size; si += 2) { if (di >= (buflen - 1)) break; @@ -288,7 +319,9 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) buf[di++] = buf[si]; } buf[di] = 0; - ret = di; + /* with buf a char* array, practical "size" limit and + * so "di" are small enough to cast to int */ + ret = (int)di; } /* "UPS No Ack" has a special meaning */ @@ -313,29 +346,37 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) } -static void *cypress_subdriver(void) +static void *cypress_subdriver(USBDevice_t *device) { + NUT_UNUSED_VARIABLE(device); + subdriver_command = &cypress_command; return NULL; } -static void *ippon_subdriver(void) +static void *ippon_subdriver(USBDevice_t *device) { + NUT_UNUSED_VARIABLE(device); + subdriver_command = &ippon_command; return NULL; } -static void *krauler_subdriver(void) +static void *krauler_subdriver(USBDevice_t *device) { + NUT_UNUSED_VARIABLE(device); + subdriver_command = &krauler_command; return NULL; } -static void *phoenix_subdriver(void) +static void *phoenix_subdriver(USBDevice_t *device) { + NUT_UNUSED_VARIABLE(device); + subdriver_command = &phoenix_command; return NULL; } @@ -346,21 +387,29 @@ static usb_device_id_t blazer_usb_id[] = { { USB_DEVICE(0x0001, 0x0000), &krauler_subdriver }, /* Krauler UP-M500VA */ { USB_DEVICE(0xffff, 0x0000), &krauler_subdriver }, /* Ablerex 625L USB */ { USB_DEVICE(0x0665, 0x5161), &cypress_subdriver }, /* Belkin F6C1200-UNV */ + { USB_DEVICE(0x06da, 0x0002), &cypress_subdriver }, /* Online Yunto YQ450 */ { USB_DEVICE(0x06da, 0x0003), &ippon_subdriver }, /* Mustek Powermust */ + { USB_DEVICE(0x06da, 0x0004), &cypress_subdriver }, /* Phoenixtec Innova 3/1 T */ + { USB_DEVICE(0x06da, 0x0005), &cypress_subdriver }, /* Phoenixtec Innova RT */ + { USB_DEVICE(0x06da, 0x0201), &cypress_subdriver }, /* Phoenixtec Innova T */ + { USB_DEVICE(0x06da, 0x0601), &phoenix_subdriver }, /* Online Zinto A */ { USB_DEVICE(0x0f03, 0x0001), &cypress_subdriver }, /* Unitek Alpha 1200Sx */ { USB_DEVICE(0x14f0, 0x00c9), &phoenix_subdriver }, /* GE EP series */ - /* end of list */ - {-1, -1, NULL} + + /* Terminating entry */ + { 0, 0, NULL } }; static int device_match_func(USBDevice_t *hd, void *privdata) { + NUT_UNUSED_VARIABLE(privdata); + if (subdriver_command) { return 1; } - switch (is_usb_device_supported(blazer_usb_id, hd->VendorID, hd->ProductID)) + switch (is_usb_device_supported(blazer_usb_id, hd)) { case SUPPORTED: return 1; @@ -379,16 +428,18 @@ static USBDeviceMatcher_t device_matcher = { NULL }; +#endif /* TESTING */ + /* * Generic command processing function. Send a command and read a reply. * Returns < 0 on error, 0 on timeout and the number of bytes read on * success. */ -int blazer_command(const char *cmd, char *buf, size_t buflen) +ssize_t blazer_command(const char *cmd, char *buf, size_t buflen) { #ifndef TESTING - int ret; + ssize_t ret; if (udev == NULL) { ret = usb->open(&udev, &usbdevice, reopen_matcher, NULL); @@ -405,42 +456,58 @@ int blazer_command(const char *cmd, char *buf, size_t buflen) switch (ret) { - case -EBUSY: /* Device or resource busy */ + case ERROR_BUSY: /* Device or resource busy */ fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver"); +#ifndef HAVE___ATTRIBUTE__NORETURN + exit(EXIT_FAILURE); /* Should not get here in practice, but compiler is afraid we can fall through */ +#endif +#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ case -EPERM: /* Operation not permitted */ fatal_with_errno(EXIT_FAILURE, "Permissions problem"); +# ifndef HAVE___ATTRIBUTE__NORETURN + exit(EXIT_FAILURE); /* Should not get here in practice, but compiler is afraid we can fall through */ +# endif +#endif /* WITH_LIBUSB_0_1 */ - case -EPIPE: /* Broken pipe */ + case ERROR_PIPE: /* Broken pipe */ if (usb_clear_halt(udev, 0x81) == 0) { upsdebugx(1, "Stall condition cleared"); break; } -#ifdef ETIME +#if (defined ETIME) && ETIME && WITH_LIBUSB_0_1 + goto fallthrough_case_etime; case -ETIME: /* Timer expired */ + fallthrough_case_etime: #endif if (usb_reset(udev) == 0) { upsdebugx(1, "Device reset handled"); } - case -ENODEV: /* No such device */ - case -EACCES: /* Permission denied */ - case -EIO: /* I/O error */ + goto fallthrough_case_reconnect; + case ERROR_NO_DEVICE: /* No such device */ + case ERROR_ACCESS: /* Permission denied */ + case ERROR_IO: /* I/O error */ +#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ case -ENXIO: /* No such device or address */ - case -ENOENT: /* No such file or directory */ +#endif + case ERROR_NOT_FOUND: /* No such file or directory */ + fallthrough_case_reconnect: /* Uh oh, got to reconnect! */ usb->close(udev); udev = NULL; break; - case -ETIMEDOUT: /* Connection timed out */ - case -EOVERFLOW: /* Value too large for defined data type */ + case ERROR_TIMEOUT: /* Connection timed out */ + case ERROR_OVERFLOW: /* Value too large for defined data type */ +#if EPROTO && WITH_LIBUSB_0_1 case -EPROTO: /* Protocol error */ +#endif default: break; } return ret; -#else +#else /* if TESTING: */ const struct { const char *command; const char *answer; @@ -461,31 +528,49 @@ int blazer_command(const char *cmd, char *buf, size_t buflen) continue; } - return snprintf(buf, buflen, "%s", testing[i].answer); + /* TODO: Range-check int vs ssize_t values */ + return (ssize_t)snprintf(buf, buflen, "%s", testing[i].answer); } - return snprintf(buf, buflen, "%s", testing[i].command); -#endif + return (ssize_t)snprintf(buf, buflen, "%s", testing[i].command); +#endif /* TESTING */ } +#ifndef TESTING +static const struct subdriver_t { + const char *name; + int (*command)(const char *cmd, char *buf, size_t buflen); +} subdriver[] = { + { "cypress", &cypress_command }, + { "phoenix", &phoenix_command }, + { "ippon", &ippon_command }, + { "krauler", &krauler_command }, + { NULL, NULL } +}; +#endif /* TESTING */ void upsdrv_help(void) { - printf("Read The Fine Manual ('man 8 blazer')\n"); +#ifndef TESTING + printf("\nAcceptable values for 'subdriver' via -x or ups.conf in this driver: "); + size_t i; + + for (i = 0; subdriver[i].name != NULL; i++) { + if (i>0) + printf(", "); + printf("%s", subdriver[i].name); + } + printf("\n\n"); +#endif /* TESTING */ + + printf("Read The Fine Manual ('man 8 blazer_usb')\n"); } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "subdriver", "Serial-over-USB subdriver selection"); - addvar(VAR_VALUE, "vendorid", "Regular expression to match UPS Manufacturer numerical ID (4 digits hexadecimal)"); - addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)"); - - addvar(VAR_VALUE, "vendor", "Regular expression to match UPS Manufacturer string"); - addvar(VAR_VALUE, "product", "Regular expression to match UPS Product string"); - addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial number"); - - addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name"); + nut_usb_addvars(); addvar(VAR_VALUE, "langid_fix", "Apply the language ID workaround to the krauler subdriver (0x409 or 0x4095)"); @@ -496,37 +581,30 @@ void upsdrv_makevartable(void) void upsdrv_initups(void) { #ifndef TESTING - const struct { - const char *name; - int (*command)(const char *cmd, char *buf, size_t buflen); - } subdriver[] = { - { "cypress", &cypress_command }, - { "phoenix", &phoenix_command }, - { "ippon", &ippon_command }, - { "krauler", &krauler_command }, - { NULL } - }; - int ret, langid; char tbuf[255]; /* Some devices choke on size > 255 */ - char *regex_array[6]; - + char *regex_array[7]; char *subdrv = getval("subdriver"); + warn_if_bad_usb_port_filename(device_path); + regex_array[0] = getval("vendorid"); regex_array[1] = getval("productid"); regex_array[2] = getval("vendor"); regex_array[3] = getval("product"); regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); + regex_array[6] = getval("device"); /* check for language ID workaround (#1) */ if (getval("langid_fix")) { /* skip "0x" prefix and set back to hexadecimal */ - if (sscanf(getval("langid_fix") + 2, "%x", &langid_fix) != 1) { + unsigned int u_langid_fix; + if ( (sscanf(getval("langid_fix") + 2, "%x", &u_langid_fix) != 1) || (u_langid_fix > INT_MAX) ) { upslogx(LOG_NOTICE, "Error enabling language ID workaround"); } else { + langid_fix = (int)u_langid_fix; upsdebugx(2, "language ID workaround enabled (using '0x%x')", langid_fix); } } @@ -576,7 +654,7 @@ void upsdrv_initups(void) "and make sure you have an up-to-date version of NUT. If this does not help,\n" "try running the driver with at least 'subdriver', 'vendorid' and 'productid'\n" "options specified. Please refer to the man page for details about these options\n" - "(man 8 blazer).\n"); + "(man 8 blazer_usb).\n"); } if (!subdriver_command) { @@ -605,13 +683,13 @@ void upsdrv_initups(void) * in the descriptor. See USB 2.0 specification, section 9.6.7, for * more information on this. * This should allow automatic application of the workaround */ - ret = usb_get_string(udev, 0, 0, tbuf, sizeof(tbuf)); + ret = usb_get_string(udev, 0, 0, (usb_ctrl_charbuf)tbuf, sizeof(tbuf)); if (ret >= 4) { - langid = tbuf[2] | (tbuf[3] << 8); + langid = (unsigned char)tbuf[2] | ((unsigned char)tbuf[3] << 8); upsdebugx(1, "First supported language ID: 0x%x (please report to the NUT maintainer!)", langid); } } -#endif +#endif /* TESTING */ blazer_initups(); } @@ -632,5 +710,6 @@ void upsdrv_cleanup(void) free(usbdevice.Product); free(usbdevice.Serial); free(usbdevice.Bus); -#endif + free(usbdevice.Device); +#endif /* TESTING */ } diff --git a/drivers/clone-outlet.c b/drivers/clone-outlet.c index 99d150c..127909c 100644 --- a/drivers/clone-outlet.c +++ b/drivers/clone-outlet.c @@ -20,13 +20,14 @@ #include "main.h" #include "parseconf.h" +#include "nut_stdint.h" #include #include #include #define DRIVER_NAME "clone outlet UPS Driver" -#define DRIVER_VERSION "0.01" +#define DRIVER_VERSION "0.02" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -66,7 +67,7 @@ static int dumpdone = 0; static PCONF_CTX_t sock_ctx; static time_t last_heard = 0, last_ping = 0, last_connfail = 0; -static int parse_args(int numargs, char **arg) +static int parse_args(size_t numargs, char **arg) { if (numargs < 1) { return 0; @@ -142,13 +143,28 @@ static int parse_args(int numargs, char **arg) static int sstate_connect(void) { - int ret, fd; + ssize_t ret; + int fd, len; const char *dumpcmd = "DUMPALL\n"; struct sockaddr_un sa; memset(&sa, '\0', sizeof(sa)); sa.sun_family = AF_UNIX; - snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/%s", dflt_statepath(), device_path); + len = snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/%s", dflt_statepath(), device_path); + + if (len < 0) { + fatalx(EXIT_FAILURE, "Can't create a unix domain socket: " + "failed to prepare the pathname"); + } + if ((uintmax_t)len >= (uintmax_t)sizeof(sa.sun_path)) { + fatalx(EXIT_FAILURE, + "Can't create a unix domain socket: pathname '%s/%s' " + "is too long (%zu) for 'struct sockaddr_un->sun_path' " + "on this system (%zu)", + dflt_statepath(), device_path, + strlen(dflt_statepath()) + 1 + strlen(device_path), + sizeof(sa.sun_path)); + } fd = socket(AF_UNIX, SOCK_STREAM, 0); @@ -231,7 +247,7 @@ static void sstate_disconnect(void) static int sstate_sendline(const char *buf) { - int ret; + ssize_t ret; if (upsfd < 0) { return -1; /* failed */ @@ -250,7 +266,8 @@ static int sstate_sendline(const char *buf) static int sstate_readline(void) { - int i, ret; + int i; + ssize_t ret; char buf[SMALLBUF]; if (upsfd < 0) { diff --git a/drivers/clone.c b/drivers/clone.c index c8beb83..00a95b4 100644 --- a/drivers/clone.c +++ b/drivers/clone.c @@ -18,15 +18,18 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" #include "main.h" #include "parseconf.h" +#include "attribute.h" +#include "nut_stdint.h" #include #include #include #define DRIVER_NAME "Clone UPS driver" -#define DRIVER_VERSION "0.02" +#define DRIVER_VERSION "0.03" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -39,8 +42,8 @@ upsdrv_info_t upsdrv_info = { static struct { struct { - int start; - int shutdown; + long start; + long shutdown; } timer; char status[ST_MAX_VALUE_LEN]; } ups = { { -1, -1 }, "WAIT" }; @@ -57,7 +60,7 @@ static struct { } battery = { { 0, 0 }, { 0, 0 } }; static int dumpdone = 0, online = 1, outlet = 1; -static int offdelay = 120, ondelay = 30; +static long offdelay = 120, ondelay = 30; static PCONF_CTX_t sock_ctx; static time_t last_poll = 0, last_heard = 0, @@ -66,7 +69,7 @@ static time_t last_poll = 0, last_heard = 0, static int instcmd(const char *cmdname, const char *extra); -static int parse_args(int numargs, char **arg) +static int parse_args(size_t numargs, char **arg) { if (numargs < 1) { return 0; @@ -156,13 +159,28 @@ static int parse_args(int numargs, char **arg) static int sstate_connect(void) { - int ret, fd; + ssize_t ret; + int fd, len; const char *dumpcmd = "DUMPALL\n"; struct sockaddr_un sa; memset(&sa, '\0', sizeof(sa)); sa.sun_family = AF_UNIX; - snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/%s", dflt_statepath(), device_path); + len = snprintf(sa.sun_path, sizeof(sa.sun_path), "%s/%s", dflt_statepath(), device_path); + + if (len < 0) { + fatalx(EXIT_FAILURE, "Can't create a unix domain socket: " + "failed to prepare the pathname"); + } + if ((uintmax_t)len >= (uintmax_t)sizeof(sa.sun_path)) { + fatalx(EXIT_FAILURE, + "Can't create a unix domain socket: pathname '%s/%s' " + "is too long (%zu) for 'struct sockaddr_un->sun_path' " + "on this system (%zu)", + dflt_statepath(), device_path, + strlen(dflt_statepath()) + 1 + strlen(device_path), + sizeof(sa.sun_path)); + } fd = socket(AF_UNIX, SOCK_STREAM, 0); @@ -245,7 +263,7 @@ static void sstate_disconnect(void) static int sstate_sendline(const char *buf) { - int ret; + ssize_t ret; if (upsfd < 0) { return -1; /* failed */ @@ -264,7 +282,8 @@ static int sstate_sendline(const char *buf) static int sstate_readline(void) { - int i, ret; + int i; + ssize_t ret; char buf[SMALLBUF]; if (upsfd < 0) { @@ -381,7 +400,7 @@ static int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -434,11 +453,11 @@ void upsdrv_initinfo(void) battery.runtime.low = strtod(val, NULL); } - dstate_setinfo("ups.delay.shutdown", "%d", offdelay); - dstate_setinfo("ups.delay.start", "%d", ondelay); + dstate_setinfo("ups.delay.shutdown", "%ld", offdelay); + dstate_setinfo("ups.delay.start", "%ld", ondelay); - dstate_setinfo("ups.timer.shutdown", "%d", ups.timer.shutdown); - dstate_setinfo("ups.timer.start", "%d", ups.timer.start); + dstate_setinfo("ups.timer.shutdown", "%ld", ups.timer.shutdown); + dstate_setinfo("ups.timer.start", "%ld", ups.timer.start); upsh.instcmd = instcmd; upsh.setvar = setvar; @@ -513,13 +532,16 @@ void upsdrv_updateinfo(void) } } - dstate_setinfo("ups.timer.shutdown", "%d", ups.timer.shutdown); - dstate_setinfo("ups.timer.start", "%d", ups.timer.start); + dstate_setinfo("ups.timer.shutdown", "%ld", ups.timer.shutdown); + dstate_setinfo("ups.timer.start", "%ld", ups.timer.start); last_poll = now; } +void upsdrv_shutdown(void) + __attribute__((noreturn)); + void upsdrv_shutdown(void) { fatalx(EXIT_FAILURE, "shutdown not supported"); diff --git a/drivers/compaq-mib.c b/drivers/compaq-mib.c index 206b04b..114299a 100644 --- a/drivers/compaq-mib.c +++ b/drivers/compaq-mib.c @@ -1,14 +1,15 @@ /* compaq-mib.c - data to monitor SNMP UPS with NUT * - * Copyright (C) 2002-2006 - * Arnaud Quette - * Niels Baggesen - * Philip Ward + * Copyright (C) + * 2002-2012 Arnaud Quette + * 2002-2006 Niels Baggesen + * 2002-2006 Philip Ward * - * Sponsored by MGE UPS SYSTEMS + * This program was sponsored by MGE UPS SYSTEMS, and now Eaton * - * This version has been tested using an HP R5500XR UPS with AF401A - * management card and a single phase input. + * This version has been tested using: + * HP R5500XR UPS with management card AF401A and a single phase input + * HP R/T3000 UPS with management card AF465A and a single phase input * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,152 +30,227 @@ #include "compaq-mib.h" -#define CPQPOWER_MIB_VERSION "1.0" +#define CPQPOWER_MIB_VERSION "1.66" + +#define DEFAULT_ONDELAY "30" +#define DEFAULT_OFFDELAY "20" + +/* Note: RFC-1628 (UPS MIB) is also supported on these devices! */ /* SNMP OIDs set */ -#define CPQPOWER_OID_UPS_MIB "1.3.6.1.4.1.232.165.3" +#define CPQPOWER_OID_UPS_MIB ".1.3.6.1.4.1.232.165.3" +/* FIXME: to be verified */ +#define CPQPOWER_SYSOID CPQPOWER_OID_UPS_MIB -#define CPQPOWER_OID_MFR_NAME CPQPOWER_OID_UPS_MIB ".1.1.0" /* UPS-MIB::upsIdentManufacturer */ -#define CPQPOWER_OID_MODEL_NAME CPQPOWER_OID_UPS_MIB ".1.2.0" /* UPS-MIB::upsIdentModel */ -#define CPQPOWER_OID_FIRMREV CPQPOWER_OID_UPS_MIB ".1.3.0" /* UPS-MIB::upsIdentUPSSoftwareVersion */ -#define CPQPOWER_OID_OEMCODE CPQPOWER_OID_UPS_MIB ".1.4.0" /* UPS-MIB::upsIdentAgentSoftwareVersion */ +#define CPQPOWER_OID_MFR_NAME ".1.3.6.1.4.1.232.165.3.1.1.0" /* UPS-MIB::upsIdentManufacturer */ +#define CPQPOWER_OID_MODEL_NAME ".1.3.6.1.4.1.232.165.3.1.2.0" /* UPS-MIB::upsIdentModel */ +#define CPQPOWER_OID_FIRMREV ".1.3.6.1.4.1.232.165.3.1.3.0" /* UPS-MIB::upsIdentUPSSoftwareVersion */ +#define CPQPOWER_OID_OEMCODE ".1.3.6.1.4.1.232.165.3.1.4.0" /* UPS-MIB::upsIdentAgentSoftwareVersion */ -#define CPQPOWER_OID_BATT_RUNTIME CPQPOWER_OID_UPS_MIB ".2.1.0" /* UPS-MIB::upsEstimatedMinutesRemaining */ -#define CPQPOWER_OID_BATT_VOLTAGE CPQPOWER_OID_UPS_MIB ".2.2.0" /* UPS-MIB::upsBatteryVoltage */ -#define CPQPOWER_OID_BATT_CURRENT CPQPOWER_OID_UPS_MIB ".2.3.0" /* UPS-MIB::upsBatteryCurrent */ -#define CPQPOWER_OID_BATT_CHARGE CPQPOWER_OID_UPS_MIB ".2.4.0" /* UPS-MIB::upsBattCapacity */ -#define CPQPOWER_OID_BATT_STATUS CPQPOWER_OID_UPS_MIB ".2.5.0" /* UPS-MIB::upsBatteryAbmStatus */ +#define CPQPOWER_OID_BATT_RUNTIME ".1.3.6.1.4.1.232.165.3.2.1.0" /* UPS-MIB::upsEstimatedMinutesRemaining */ +#define CPQPOWER_OID_BATT_VOLTAGE ".1.3.6.1.4.1.232.165.3.2.2.0" /* UPS-MIB::upsBatteryVoltage */ +#define CPQPOWER_OID_BATT_CURRENT ".1.3.6.1.4.1.232.165.3.2.3.0" /* UPS-MIB::upsBatteryCurrent */ +#define CPQPOWER_OID_BATT_CHARGE ".1.3.6.1.4.1.232.165.3.2.4.0" /* UPS-MIB::upsBattCapacity */ +#define CPQPOWER_OID_BATT_STATUS ".1.3.6.1.4.1.232.165.3.2.5.0" /* UPS-MIB::upsBatteryAbmStatus */ -#define CPQPOWER_OID_IN_FREQ CPQPOWER_OID_UPS_MIB ".3.1.0" /* UPS-MIB::upsInputFrequency */ -#define CPQPOWER_OID_IN_LINEBADS CPQPOWER_OID_UPS_MIB ".3.2.0" /* UPS-MIB::upsInputLineBads */ -#define CPQPOWER_OID_IN_LINES CPQPOWER_OID_UPS_MIB ".3.3.0" /* UPS-MIB::upsInputNumPhases */ +#define CPQPOWER_OID_IN_FREQ ".1.3.6.1.4.1.232.165.3.3.1.0" /* UPS-MIB::upsInputFrequency */ +#define CPQPOWER_OID_IN_LINEBADS ".1.3.6.1.4.1.232.165.3.3.2.0" /* UPS-MIB::upsInputLineBads */ +#define CPQPOWER_OID_IN_LINES ".1.3.6.1.4.1.232.165.3.3.3.0" /* UPS-MIB::upsInputNumPhases */ -#define CPQPOWER_OID_IN_PHASE CPQPOWER_OID_UPS_MIB ".3.4.1.1" /* UPS-MIB::upsInputPhase */ -#define CPQPOWER_OID_IN_VOLTAGE CPQPOWER_OID_UPS_MIB ".3.4.1.2" /* UPS-MIB::upsInputVoltage */ -#define CPQPOWER_OID_IN_CURRENT CPQPOWER_OID_UPS_MIB ".3.4.1.3" /* UPS-MIB::upsInputCurrent */ -#define CPQPOWER_OID_IN_POWER CPQPOWER_OID_UPS_MIB ".3.4.1.4" /* UPS-MIB::upsInputWatts */ +#define CPQPOWER_OID_IN_PHASE ".1.3.6.1.4.1.232.165.3.3.4.1.1" /* UPS-MIB::upsInputPhase */ +#define CPQPOWER_OID_IN_VOLTAGE ".1.3.6.1.4.1.232.165.3.3.4.1.2" /* UPS-MIB::upsInputVoltage */ +#define CPQPOWER_OID_IN_CURRENT ".1.3.6.1.4.1.232.165.3.3.4.1.3" /* UPS-MIB::upsInputCurrent */ +#define CPQPOWER_OID_IN_POWER ".1.3.6.1.4.1.232.165.3.3.4.1.4" /* UPS-MIB::upsInputWatts */ -#define CPQPOWER_OID_LOAD_LEVEL CPQPOWER_OID_UPS_MIB ".4.1.0" /* UPS-MIB::upsOutputLoad */ -#define CPQPOWER_OID_OUT_FREQUENCY CPQPOWER_OID_UPS_MIB ".4.2.0" /* UPS-MIB::upsOutputFrequency */ -#define CPQPOWER_OID_OUT_LINES CPQPOWER_OID_UPS_MIB ".4.3.0" /* UPS-MIB::upsOutputNumPhases */ +#define CPQPOWER_OID_LOAD_LEVEL ".1.3.6.1.4.1.232.165.3.4.1.0" /* UPS-MIB::upsOutputLoad */ +#define CPQPOWER_OID_OUT_FREQUENCY ".1.3.6.1.4.1.232.165.3.4.2.0" /* UPS-MIB::upsOutputFrequency */ +#define CPQPOWER_OID_OUT_LINES ".1.3.6.1.4.1.232.165.3.4.3.0" /* UPS-MIB::upsOutputNumPhases */ -#define CPQPOWER_OID_OUT_PHASE CPQPOWER_OID_UPS_MIB ".4.4.1.1" /* UPS-MIB::upsOutputPhase */ -#define CPQPOWER_OID_OUT_VOLTAGE CPQPOWER_OID_UPS_MIB ".4.4.1.2" /* UPS-MIB::upsOutputVoltage */ -#define CPQPOWER_OID_OUT_CURRENT CPQPOWER_OID_UPS_MIB ".4.4.1.3" /* UPS-MIB::upsOutputCurrent */ -#define CPQPOWER_OID_OUT_POWER CPQPOWER_OID_UPS_MIB ".4.4.1.4" /* UPS-MIB::upsOutputWatts */ +#define CPQPOWER_OID_OUT_PHASE ".1.3.6.1.4.1.232.165.3.4.4.1.1" /* UPS-MIB::upsOutputPhase */ +#define CPQPOWER_OID_OUT_VOLTAGE ".1.3.6.1.4.1.232.165.3.4.4.1.2" /* UPS-MIB::upsOutputVoltage */ +#define CPQPOWER_OID_OUT_CURRENT ".1.3.6.1.4.1.232.165.3.4.4.1.3" /* UPS-MIB::upsOutputCurrent */ +#define CPQPOWER_OID_OUT_POWER ".1.3.6.1.4.1.232.165.3.4.4.1.4" /* UPS-MIB::upsOutputWatts */ -#define CPQPOWER_OID_POWER_STATUS CPQPOWER_OID_UPS_MIB ".4.5.0" /* UPS-MIB::upsOutputSource */ +#define CPQPOWER_OID_POWER_STATUS ".1.3.6.1.4.1.232.165.3.4.5.0" /* UPS-MIB::upsOutputSource */ -#define CPQPOWER_OID_AMBIENT_TEMP CPQPOWER_OID_UPS_MIB ".6.1.0" /* UPS-MIB::upsEnvAmbientTemp */ +#define CPQPOWER_OID_AMBIENT_TEMP ".1.3.6.1.4.1.232.165.3.6.1.0" /* UPS-MIB::upsEnvAmbientTemp */ -#define CPQPOWER_OID_UPS_TEST_BATT CPQPOWER_OID_UPS_MIB ".7.1.0" /* UPS-MIB::upsTestBattery */ -#define CPQPOWER_OID_UPS_TEST_RES CPQPOWER_OID_UPS_MIB ".7.2.0" /* UPS-MIB::upsTestBatteryStatus */ -#define CPQPOWER_OID_ALARM_OB CPQPOWER_OID_UPS_MIB ".7.3.0" /* UPS-MIB::upsOnBattery */ -#define CPQPOWER_OID_ALARM_LB CPQPOWER_OID_UPS_MIB ".7.4.0" /* UPS-MIB::upsLowBattery */ +#define CPQPOWER_OID_UPS_TEST_BATT ".1.3.6.1.4.1.232.165.3.7.1.0" /* UPS-MIB::upsTestBattery */ +#define CPQPOWER_OID_UPS_TEST_RES ".1.3.6.1.4.1.232.165.3.7.2.0" /* UPS-MIB::upsTestBatteryStatus */ +#define CPQPOWER_OID_ALARM_OB ".1.3.6.1.4.1.232.165.3.7.3.0" /* UPS-MIB::upsOnBattery */ +#define CPQPOWER_OID_ALARM_LB ".1.3.6.1.4.1.232.165.3.7.4.0" /* UPS-MIB::upsLowBattery */ +#define IETF_OID_AGENTREV ".1.3.6.1.2.1.33.1.1.4.0" /* UPS-MIB::upsIdentAgentSoftwareVersion.0 */ + +/* Not used, as no longer supported by MIB ver. 1.76 (Github issue 118) static info_lkp_t cpqpower_alarm_ob[] = { - { 1, "OB" }, - { 0, "NULL" } -} ; + { 1, "OB", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; +*/ +/* Not used, as no longer supported by MIB ver. 1.76 (Github issue 118) static info_lkp_t cpqpower_alarm_lb[] = { - { 1, "LB" }, - { 0, "NULL" } -} ; + { 1, "LB", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; +*/ /* Defines for CPQPOWER_OID_POWER_STATUS (1) */ static info_lkp_t cpqpower_pwr_info[] = { - { 1, "" /* other */ }, - { 2, "OFF" /* none */ }, - { 3, "OL" /* normal */ }, - { 4, "OL BYPASS" /* bypass */ }, - { 5, "OB" /* battery */ }, - { 6, "OL BOOST" /* booster */ }, - { 7, "OL TRIM" /* reducer */ }, - { 8, "OL" /* parallelCapacity */ }, - { 9, "OL" /* parallelRedundant */ }, - { 10, "OL" /* HighEfficiencyMode */ }, - { 0, "NULL" } + { 1, "" /* other */, NULL, NULL }, + { 2, "OFF" /* none */, NULL, NULL }, + { 3, "OL" /* normal */, NULL, NULL }, + { 4, "OL BYPASS" /* bypass */, NULL, NULL }, + { 5, "OB" /* battery */, NULL, NULL }, + { 6, "OL BOOST" /* booster */, NULL, NULL }, + { 7, "OL TRIM" /* reducer */, NULL, NULL }, + { 8, "OL" /* parallelCapacity */, NULL, NULL }, + { 9, "OL" /* parallelRedundant */, NULL, NULL }, + { 10, "OL" /* HighEfficiencyMode */, NULL, NULL }, + { 0, NULL, NULL, NULL } } ; static info_lkp_t cpqpower_mode_info[] = { - { 1, "" }, - { 2, "" }, - { 3, "normal" }, - { 4, "" }, - { 5, "" }, - { 6, "" }, - { 7, "" }, - { 8, "parallel capacity" }, - { 9, "parallel redundancy" }, - {10, "high efficiency" }, - { 0, "NULL" } + { 1, "", NULL, NULL }, + { 2, "", NULL, NULL }, + { 3, "normal", NULL, NULL }, + { 4, "", NULL, NULL }, + { 5, "", NULL, NULL }, + { 6, "", NULL, NULL }, + { 7, "", NULL, NULL }, + { 8, "parallel capacity", NULL, NULL }, + { 9, "parallel redundancy", NULL, NULL }, + {10, "high efficiency", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t cpqpower_battery_abm_status[] = { - { 1, "CHRG" }, - { 2, "DISCHRG" }, -/* { 3, "Floating" }, */ -/* { 4, "Resting" }, */ -/* { 5, "Unknown" }, */ - { 0, "NULL" } + { 1, "CHRG", NULL, NULL }, + { 2, "DISCHRG", NULL, NULL }, +/* { 3, "Floating", NULL, NULL }, */ +/* { 4, "Resting", NULL, NULL }, */ +/* { 5, "Unknown", NULL, NULL }, */ + { 0, NULL, NULL, NULL } } ; /* Defines for CPQPOWER_OID_UPS_TEST_RES */ static info_lkp_t cpqpower_test_res_info[] = { - { 1, "Unknown" }, - { 2, "Done and passed" }, - { 3, "Done and error" }, - { 4, "In progress" }, - { 5, "Not supported" }, - { 6, "Inhibited" }, - { 7, "Scheduled" }, - { 0, "NULL" } + { 1, "Unknown", NULL, NULL }, + { 2, "Done and passed", NULL, NULL }, + { 3, "Done and error", NULL, NULL }, + { 4, "In progress", NULL, NULL }, + { 5, "Not supported", NULL, NULL }, + { 6, "Inhibited", NULL, NULL }, + { 7, "Scheduled", NULL, NULL }, + { 0, NULL, NULL, NULL } } ; -#define CPQPOWER_OID_SD_AFTER_DELAY CPQPOWER_OID_UPS_MIB ".8.1.0" /* UPS-MIB::upsShutdownAfterDelay */ +#define CPQPOWER_START_TEST "1" + +static info_lkp_t cpqpower_outlet_status_info[] = { + { 1, "on", NULL, NULL }, + { 2, "off", NULL, NULL }, + { 3, "pendingOff", NULL, NULL }, /* transitional status */ + { 4, "pendingOn", NULL, NULL }, /* transitional status */ + { 5, "unknown", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* Ugly hack: having the matching OID present means that the outlet is + * switchable. So, it should not require this value lookup */ +static info_lkp_t cpqpower_outlet_switchability_info[] = { + { 1, "yes", NULL, NULL }, + { 2, "yes", NULL, NULL }, + { 3, "yes", NULL, NULL }, + { 4, "yes", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +#define CPQPOWER_OID_SD_AFTER_DELAY ".1.3.6.1.4.1.232.165.3.8.1.0" /* UPS-MIB::upsControlOutputOffDelay */ #define CPQPOWER_OFF_DO 0 /* Snmp2NUT lookup table */ static snmp_info_t cpqpower_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* UPS page */ /* info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_MFR_NAME, "HP/Compaq", SU_FLAG_STATIC, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_MODEL_NAME, "SNMP UPS", SU_FLAG_STATIC, NULL }, - { "ups.model.aux", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_OEMCODE, "", SU_FLAG_STATIC, NULL }, - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_FIRMREV, "", SU_FLAG_STATIC, NULL }, + /* { "ups.model.aux", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_OEMCODE, "", SU_FLAG_STATIC, NULL },*/ + { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.1.2.7.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* FIXME: split between firmware and firmware.aux ("00.01.0019;00.01.0004") + * UPS Firmware Revision : 00.01.0004 + * Communication Board Firmware Revision : 00.01.0019 */ + /* FIXME: the 2 "firmware" entries below should be SU_FLAG_SEMI_STATIC */ + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_FIRMREV, "", 0, NULL }, + { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_AGENTREV, "", 0, NULL }, { "ups.load", 0, 1.0, CPQPOWER_OID_LOAD_LEVEL, "", 0, NULL }, { "ups.realpower", 0, 1.0, CPQPOWER_OID_OUT_POWER, "", SU_OUTPUT_1, NULL }, + { "ups.realpower", 0, 1.0, ".1.3.6.1.4.1.232.165.3.9.3.0", "", SU_OUTPUT_1, NULL }, { "ups.L1.realpower", 0, 0.1, CPQPOWER_OID_OUT_POWER ".1", "", SU_OUTPUT_3, NULL }, { "ups.L2.realpower", 0, 0.1, CPQPOWER_OID_OUT_POWER ".2", "", SU_OUTPUT_3, NULL }, { "ups.L3.realpower", 0, 0.1, CPQPOWER_OID_OUT_POWER ".3", "", SU_OUTPUT_3, NULL }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_POWER_STATUS, "OFF", SU_STATUS_PWR, cpqpower_pwr_info }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_BATT_STATUS, "", SU_STATUS_PWR, cpqpower_battery_abm_status }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_ALARM_OB, "", SU_STATUS_BATT, cpqpower_alarm_ob }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_ALARM_LB, "", SU_STATUS_BATT, cpqpower_alarm_lb }, -/* { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_BATT_STATUS, "", SU_STATUS_BATT, ietf_batt_info }, */ + /* The next two lines are no longer supported by MIB ver. 1.76 (Github issue 118) + * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_ALARM_OB, "", SU_STATUS_BATT, cpqpower_alarm_ob }, + * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_ALARM_LB, "", SU_STATUS_BATT, cpqpower_alarm_lb }, */ + /* { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_BATT_STATUS, "", SU_STATUS_BATT, ietf_batt_info }, */ + /* FIXME: this should use either .1.3.6.1.4.1.232.165.3.11.1.0 (upsTopologyType) + * or .1.3.6.1.4.1.232.165.3.11.2.0 (upsTopoMachineCode) */ { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_POWER_STATUS, "", SU_STATUS_PWR, cpqpower_mode_info }, { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, CPQPOWER_OID_UPS_TEST_RES, "", 0, cpqpower_test_res_info }, + /* FIXME: handle ups.date and ups.time + * - OID: .1.3.6.1.4.1.232.165.3.9.5.0 + * - format MM/DD/YYYY HH:MM:SS */ + /* FIXME: handle upsInputSource.0 (".1.3.6.1.4.1.232.165.3.3.5.0") + * other(1) + * none(2) + * primaryUtility(3) + * bypassFeed(4) + * secondaryUtility(5) + * generator(6) + * flywheel(7) + * fuelcell(8) */ + + { "ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 6, ".1.3.6.1.4.1.232.165.3.8.1.0", DEFAULT_OFFDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.delay.start", ST_FLAG_STRING | ST_FLAG_RW, 6, ".1.3.6.1.4.1.232.165.3.8.2.0", DEFAULT_ONDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.timer.shutdown", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", "", SU_FLAG_OK, NULL }, + { "ups.timer.start", 0, 1, ".1.3.6.1.4.1.232.165.3.8.2.0", "", SU_FLAG_OK, NULL }, /* Ambient page */ { "ambient.temperature", 0, 1.0, CPQPOWER_OID_AMBIENT_TEMP, "", 0, NULL }, + { "ambient.temperature.low", 0, 1.0, ".1.3.6.1.4.1.232.165.3.6.2.0", "", 0, NULL }, + { "ambient.temperature.high", 0, 1.0, ".1.3.6.1.4.1.232.165.3.6.3.0", "", 0, NULL }, /* Battery page */ { "battery.charge", 0, 1.0, CPQPOWER_OID_BATT_CHARGE, "", 0, NULL }, - { "battery.runtime", 0, 60.0, CPQPOWER_OID_BATT_RUNTIME, "", 0, NULL }, + { "battery.runtime", 0, 1.0, CPQPOWER_OID_BATT_RUNTIME, "", 0, NULL }, { "battery.voltage", 0, 0.1, CPQPOWER_OID_BATT_VOLTAGE, "", 0, NULL }, { "battery.current", 0, 0.1, CPQPOWER_OID_BATT_CURRENT, "", 0, NULL }, + /* FIXME: need the new variable (for ABM) + { "battery.status", 0, 0.1, ".1.3.6.1.4.1.232.165.3.2.5.0", "", 0, NULL }, */ /* Input page */ - { "input.phases", 0, 1.0, CPQPOWER_OID_IN_LINES, "", SU_FLAG_SETINT, NULL, &input_phases }, + { "input.phases", 0, 1.0, CPQPOWER_OID_IN_LINES, "", 0, NULL }, /* { "input.phase", 0, 1.0, CPQPOWER_OID_IN_PHASE, "", SU_OUTPUT_1, NULL }, */ { "input.frequency", 0, 0.1, CPQPOWER_OID_IN_FREQ , "", 0, NULL }, { "input.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE, "", SU_OUTPUT_1, NULL }, + { "input.voltage", 0, 1.0, ".1.3.6.1.4.1.232.165.3.3.4.1.2.1", "", SU_OUTPUT_1, NULL }, + { "input.voltage.nominal", ST_FLAG_RW | ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.9.2.0", "", SU_OUTPUT_1, NULL }, { "input.L1-N.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".1", "", SU_INPUT_3, NULL }, { "input.L2-N.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".2", "", SU_INPUT_3, NULL }, { "input.L3-N.voltage", 0, 1.0, CPQPOWER_OID_IN_VOLTAGE ".3", "", SU_INPUT_3, NULL }, { "input.current", 0, 0.1, CPQPOWER_OID_IN_CURRENT, "", SU_OUTPUT_1, NULL }, + { "input.current", 0, 0.1, ".1.3.6.1.4.1.232.165.3.3.4.1.3.1", "", SU_OUTPUT_1, NULL }, + { "input.L1.current", 0, 0.1, CPQPOWER_OID_IN_CURRENT ".1", "", SU_INPUT_3, NULL }, { "input.L2.current", 0, 0.1, CPQPOWER_OID_IN_CURRENT ".2", "", SU_INPUT_3, NULL }, { "input.L3.current", 0, 0.1, CPQPOWER_OID_IN_CURRENT ".3", "", SU_INPUT_3, NULL }, @@ -185,24 +261,79 @@ static snmp_info_t cpqpower_mib[] = { { "input.quality", 0, 1.0, CPQPOWER_OID_IN_LINEBADS, "", 0, NULL }, /* Output page */ - { "output.phases", 0, 1.0, CPQPOWER_OID_OUT_LINES, "", SU_FLAG_SETINT, NULL, &output_phases }, + { "output.phases", 0, 1.0, CPQPOWER_OID_OUT_LINES, "", 0, NULL }, /* { "output.phase", 0, 1.0, CPQPOWER_OID_OUT_PHASE, "", SU_OUTPUT_1, NULL }, */ { "output.frequency", 0, 0.1, CPQPOWER_OID_OUT_FREQUENCY, "", 0, NULL }, + /* FIXME: handle multiplier (0.1 there) */ + { "output.frequency.nominal", ST_FLAG_RW | ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.9.4.0", "", SU_OUTPUT_1, NULL }, { "output.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE, "", SU_OUTPUT_1, NULL }, + { "output.voltage", 0, 1.0, ".1.3.6.1.4.1.232.165.3.4.4.1.2.1", "", SU_OUTPUT_1, NULL }, + { "output.voltage.nominal", ST_FLAG_RW | ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.9.1.0", "", SU_OUTPUT_1, NULL }, { "output.L1-N.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".1", "", SU_OUTPUT_3, NULL }, { "output.L2-N.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".2", "", SU_OUTPUT_3, NULL }, { "output.L3-N.voltage", 0, 1.0, CPQPOWER_OID_OUT_VOLTAGE ".3", "", SU_OUTPUT_3, NULL }, { "output.current", 0, 0.1, CPQPOWER_OID_OUT_CURRENT, "", SU_OUTPUT_1, NULL }, + { "output.current", 0, 0.1, ".1.3.6.1.4.1.232.165.3.4.4.1.3.1", "", SU_OUTPUT_1, NULL }, + /* { "output.realpower", 0, 1.0, ".1.3.6.1.4.1.232.165.3.4.4.1.4", "", SU_OUTPUT_1, NULL }, */ { "output.L1.current", 0, 0.1, CPQPOWER_OID_OUT_CURRENT ".1", "", SU_OUTPUT_3, NULL }, { "output.L2.current", 0, 0.1, CPQPOWER_OID_OUT_CURRENT ".2", "", SU_OUTPUT_3, NULL }, { "output.L3.current", 0, 0.1, CPQPOWER_OID_OUT_CURRENT ".3", "", SU_OUTPUT_3, NULL }, + /* FIXME: what to map with these? + * Name/OID: upsConfigLowOutputVoltageLimit.0; Value (Integer): 160 + * => input.transfer.low? + * Name/OID: upsConfigHighOutputVoltageLimit.0; Value (Integer): 288 + * => input.transfer.high? */ + + /* Outlet page */ + { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "outlet.count", 0, 1, ".1.3.6.1.4.1.232.165.3.10.1.0", "0", 0, NULL }, /* upsNumReceptacles */ + +/* { "outlet.current", 0, 0.001, AR_OID_UNIT_CURRENT ".0", NULL, 0, NULL, NULL }, + { "outlet.voltage", 0, 0.001, AR_OID_UNIT_VOLTAGE ".0", NULL, 0, NULL, NULL }, + { "outlet.realpower", 0, 1.0, AR_OID_UNIT_ACTIVEPOWER ".0", NULL, 0, NULL, NULL }, + { "outlet.power", 0, 1.0, AR_OID_UNIT_APPARENTPOWER ".0", NULL, 0, NULL, NULL }, */ + + /* outlet template definition */ + /* FIXME always true? */ + { "outlet.%i.switchable", ST_FLAG_STRING, 3, ".1.3.6.1.4.1.232.165.3.10.2.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, &cpqpower_outlet_switchability_info[0] }, + { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.1.%i", "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL }, + /* { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_NAME ".%i", NULL, SU_OUTLET, NULL }, */ + { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.3.10.2.1.2.%i", NULL, SU_FLAG_OK | SU_OUTLET, &cpqpower_outlet_status_info[0] }, + /* FIXME: come up with a suitable varname! + * - The delay after going On Battery until the Receptacle is automatically turned Off. + * A value of -1 means that this Output should never be turned Off automatically, but must be turned Off only by command. + * { "outlet.%i.autoswitch.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 6, ".1.3.6.1.4.1.232.165.3.10.2.1.5.%i", DEFAULT_OFFDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, // upsRecepAutoOffDelay + * - Seconds delay after the Outlet is signaled to turn On before the Output is Automatically turned ON. + * A value of -1 means that this Output should never be turned On automatically, but only when specifically commanded to do so. + * { "outlet.%i.autoswitch.delay.start", ST_FLAG_STRING | ST_FLAG_RW, 6, ".1.3.6.1.4.1.232.165.3.10.2.1.5.%i", DEFAULT_OFFDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, // upsRecepAutoOnDelay + */ + /* FIXME: also define .stop (as for 'shutdown.reboot') + * and .delay */ + { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.3.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.4.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + /* FIXME: also define a .delay or map to "outlet.%i.delay.shutdown" */ + { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.232.165.3.10.2.1.7.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + /* instant commands. */ - { "load.off", 0, CPQPOWER_OFF_DO, CPQPOWER_OID_SD_AFTER_DELAY, "", SU_TYPE_CMD, NULL }, -/* { CMD_SHUTDOWN, 0, CPQPOWER_OFF_GRACEFUL, CPQPOWER_OID_OFF, "", 0, NULL }, */ + /* We need to duplicate load.{on,off} Vs load.{on,off}.delay, since + * "0" cancels the shutdown, so we put "1" (second) for immediate off! */ + { "load.off", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", "1", SU_TYPE_CMD, NULL }, + { "load.on", 0, 1, ".1.3.6.1.4.1.232.165.3.8.2.0", "1", SU_TYPE_CMD, NULL }, + { "shutdown.stop", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", "0", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* FIXME: need ups.{timer,delay}.{start,shutdown} param counterparts! */ + + { "load.off.delay", 0, 1, ".1.3.6.1.4.1.232.165.3.8.1.0", DEFAULT_OFFDELAY, SU_TYPE_CMD, NULL }, + { "load.on.delay", 0, 1, ".1.3.6.1.4.1.232.165.3.8.2.0", DEFAULT_ONDELAY, SU_TYPE_CMD, NULL }, + /* { CMD_SHUTDOWN, 0, CPQPOWER_OFF_GRACEFUL, CPQPOWER_OID_OFF, "", 0, NULL }, */ + { "shutdown.reboot", 0, 1, ".1.3.6.1.4.1.232.165.3.8.6.0", "0", SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "test.battery.start", 0, 1, ".1.3.6.1.4.1.232.165.3.7.1.0", CPQPOWER_START_TEST, SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; -mib2nut_info_t compaq = { "cpqpower", CPQPOWER_MIB_VERSION, "", CPQPOWER_OID_MFR_NAME, cpqpower_mib }; +mib2nut_info_t compaq = { "cpqpower", CPQPOWER_MIB_VERSION, NULL, CPQPOWER_OID_MFR_NAME, cpqpower_mib, CPQPOWER_SYSOID, NULL }; + diff --git a/drivers/cps-hid.c b/drivers/cps-hid.c index b098df3..71f9a5b 100644 --- a/drivers/cps-hid.c +++ b/drivers/cps-hid.c @@ -5,7 +5,7 @@ * 2005 - 2006 Peter Selinger * * Note: this subdriver was initially generated as a "stub" by the - * path-to-subdriver script. It must be customized. + * gen-usbhid-subdriver script. It must be customized. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,54 +24,107 @@ */ #include "main.h" /* for getval() */ +#include "nut_float.h" +#include "hidparser.h" /* for FindObject_with_ID_Node() */ #include "usbhid-ups.h" #include "cps-hid.h" #include "usb-common.h" -#define CPS_HID_VERSION "CyberPower HID 0.3" +#define CPS_HID_VERSION "CyberPower HID 0.6" /* Cyber Power Systems */ #define CPS_VENDORID 0x0764 -/* +/* Values for correcting the HID on some models + * where LogMin and LogMax are set incorrectly in the HID. + */ +#define CPS_VOLTAGE_LOGMIN 0 +#define CPS_VOLTAGE_LOGMAX 511 /* Includes safety margin. */ + +/*! Battery voltage scale factor. * For some devices, the reported battery voltage is off by factor * of 1.5 so we need to apply a scale factor to it to get the real * battery voltage. By default, the factor is 1 (no scaling). */ static double battery_scale = 1; +static int might_need_battery_scale = 0; +static int battery_scale_checked = 0; -static void *cps_battery_scale(void) +/*! If the ratio of the battery voltage to the nominal battery voltage exceeds + * this factor, we assume that the battery voltage needs to be scaled by 2/3. + */ +static const double battery_voltage_sanity_check = 1.4; + +static void *cps_battery_scale(USBDevice_t *device) { - battery_scale = 0.667; + NUT_UNUSED_VARIABLE(device); + + might_need_battery_scale = 1; return NULL; } /* USB IDs device table */ static usb_device_id_t cps_usb_device_table[] = { - /* 900AVR/BC900D, CP1200AVR/BC1200D */ + /* 900AVR/BC900D */ { USB_DEVICE(CPS_VENDORID, 0x0005), NULL }, - /* Dynex DX-800U? */ + /* Dynex DX-800U?, CP1200AVR/BC1200D, CP825AVR-G, CP1000AVRLCD, CP1000PFCLCD, CP1500C, CP550HG, etc. */ { USB_DEVICE(CPS_VENDORID, 0x0501), &cps_battery_scale }, - /* OR2200LCDRM2U */ + /* OR2200LCDRM2U, OR700LCDRM1U, PR6000LCDRTXL5U */ { USB_DEVICE(CPS_VENDORID, 0x0601), NULL }, /* Terminating entry */ - { -1, -1, NULL } + { 0, 0, NULL } }; +/*! Adjusts @a battery_scale if voltage is well above nominal. + */ +static void cps_adjust_battery_scale(double batt_volt) +{ + const char *batt_volt_nom_str; + double batt_volt_nom; + + if(battery_scale_checked) { + return; + } + + batt_volt_nom_str = dstate_getinfo("battery.voltage.nominal"); + if(!batt_volt_nom_str) { + upsdebugx(2, "%s: 'battery.voltage.nominal' not available yet; skipping scale determination", __func__); + return; + } + + batt_volt_nom = strtod(batt_volt_nom_str, NULL); + if(d_equal(batt_volt_nom, 0)) { + upsdebugx(3, "%s: 'battery.voltage.nominal' is %s", __func__, batt_volt_nom_str); + return; + } + + if( (batt_volt / batt_volt_nom) > battery_voltage_sanity_check ) { + upslogx(LOG_INFO, "%s: battery readings will be scaled by 2/3", __func__); + battery_scale = 2.0/3; + } + + battery_scale_checked = 1; +} + /* returns statically allocated string - must not use it again before done with result! */ static const char *cps_battvolt_fun(double value) { static char buf[8]; + if(might_need_battery_scale) { + cps_adjust_battery_scale(value); + } + + upsdebugx(5, "%s: battery_scale = %.3f", __func__, battery_scale); snprintf(buf, sizeof(buf), "%.1f", battery_scale * value); return buf; } static info_lkp_t cps_battvolt[] = { - { 0, NULL, &cps_battvolt_fun } + { 0, NULL, &cps_battvolt_fun, NULL } }; /* returns statically allocated string - must not use it again before @@ -87,7 +140,7 @@ static const char *cps_battcharge_fun(double value) } static info_lkp_t cps_battcharge[] = { - { 0, NULL, &cps_battcharge_fun } + { 0, NULL, &cps_battcharge_fun, NULL } }; /* --------------------------------------------------------------- */ @@ -150,12 +203,14 @@ static hid_info_t cps_hid2nut[] = { { "BOOL", 0, 0, "UPS.Output.Overload", NULL, NULL, 0, overload_info }, /* Input page */ + { "input.frequency", 0, 0, "UPS.Input.Frequency", NULL, "%.1f", 0, NULL }, { "input.voltage.nominal", 0, 0, "UPS.Input.ConfigVoltage", NULL, "%.0f", 0, NULL }, { "input.voltage", 0, 0, "UPS.Input.Voltage", NULL, "%.1f", 0, NULL }, { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Input.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.high", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Input.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, /* Output page */ + { "output.frequency", 0, 0, "UPS.Output.Frequency", NULL, "%.1f", 0, NULL }, { "output.voltage", 0, 0, "UPS.Output.Voltage", NULL, "%.1f", 0, NULL }, { "output.voltage.nominal", 0, 0, "UPS.Output.ConfigVoltage", NULL, "%.0f", 0, NULL }, @@ -193,8 +248,7 @@ static const char *cps_format_serial(HIDDevice_t *hd) { * the device is supported by this subdriver, else 0. */ static int cps_claim(HIDDevice_t *hd) { - int status = is_usb_device_supported(cps_usb_device_table, hd->VendorID, - hd->ProductID); + int status = is_usb_device_supported(cps_usb_device_table, hd); switch (status) { @@ -215,6 +269,62 @@ static int cps_claim(HIDDevice_t *hd) { } } +/* CPS Models like CP900EPFCLCD return a syntactically legal but incorrect + * Report Descriptor whereby the Input High Transfer Max/Min values + * are used for the Output Voltage Usage Item limits. + * Additionally the Input Voltage LogMax is set incorrectly for EU models. + * This corrects them by finding and applying fixed + * voltage limits as being more appropriate. + */ + +static int cps_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) { + HIDData_t *pData; + + int vendorID = pDev->VendorID; + int productID = pDev->ProductID; + if (vendorID != CPS_VENDORID || productID != 0x0501) { + return 0; + } + + upsdebugx(3, "Attempting Report Descriptor fix for UPS: Vendor: %04x, Product: %04x", vendorID, productID); + + /* Apply the fix cautiously by looking for input voltage, high voltage transfer and output voltage report usages. + * If the output voltage log min/max equals high voltage transfer log min/max then the bug is present. + * To fix it Set both the input and output voltages to pre-defined settings. + */ + + if ((pData=FindObject_with_ID_Node(pDesc_arg, 16, USAGE_POW_HIGH_VOLTAGE_TRANSFER))) { + long hvt_logmin = pData->LogMin; + long hvt_logmax = pData->LogMax; + upsdebugx(4, "Report Descriptor: hvt input LogMin: %ld LogMax: %ld", hvt_logmin, hvt_logmax); + + if ((pData=FindObject_with_ID_Node(pDesc_arg, 18, USAGE_POW_VOLTAGE))) { + long output_logmin = pData->LogMin; + long output_logmax = pData->LogMax; + upsdebugx(4, "Report Descriptor: output LogMin: %ld LogMax: %ld", + output_logmin, output_logmax); + + if (hvt_logmin == output_logmin && hvt_logmax == output_logmax) { + pData->LogMin = CPS_VOLTAGE_LOGMIN; + pData->LogMax = CPS_VOLTAGE_LOGMAX; + upsdebugx(3, "Fixing Report Descriptor. Set Output Voltage LogMin = %d, LogMax = %d", + CPS_VOLTAGE_LOGMIN , CPS_VOLTAGE_LOGMAX); + if ((pData=FindObject_with_ID_Node(pDesc_arg, 15, USAGE_POW_VOLTAGE))) { + long input_logmin = pData->LogMin; + long input_logmax = pData->LogMax; + upsdebugx(4, "Report Descriptor: input LogMin: %ld LogMax: %ld", + input_logmin, input_logmax); + upsdebugx(3, "Fixing Report Descriptor. Set Input Voltage LogMin = %d, LogMax = %d", + CPS_VOLTAGE_LOGMIN , CPS_VOLTAGE_LOGMAX); + } + + return 1; + } + } + } + return 0; +} + subdriver_t cps_subdriver = { CPS_HID_VERSION, cps_claim, @@ -223,4 +333,5 @@ subdriver_t cps_subdriver = { cps_format_model, cps_format_mfr, cps_format_serial, + cps_fix_report_desc, }; diff --git a/drivers/cyberpower-mib.c b/drivers/cyberpower-mib.c index e2d81cd..aa43295 100644 --- a/drivers/cyberpower-mib.c +++ b/drivers/cyberpower-mib.c @@ -1,7 +1,7 @@ /* cyberpower-mib.c - data to monitor Cyberpower RMCARD * * Copyright (C) 2010 - Eric Schultz - * + * * derived (i.e. basically copied and modified) of bestpower by: * Copyright (C) 2010 - Arnaud Quette * @@ -24,58 +24,153 @@ #include "cyberpower-mib.h" -#define CYBERPOWER_MIB_VERSION "0.1" +#define CYBERPOWER_MIB_VERSION "0.51" #define CYBERPOWER_OID_MODEL_NAME ".1.3.6.1.4.1.3808.1.1.1.1.1.1.0" -#define CYBERPOWER_SYSOID ".1.3.6.1.4.1.3808" +/* CPS-MIB::ups */ +#define CYBERPOWER_SYSOID ".1.3.6.1.4.1.3808.1.1.1" +/* https://www.cyberpowersystems.com/products/software/mib-files/ */ +/* Per CPS MIB 2.9 upsBaseOutputStatus OBJECT-TYPE: */ static info_lkp_t cyberpower_power_status[] = { - { 2, "OL" }, - { 3, "OB" }, - { 4, "OL" }, - { 5, "OL" }, - { 7, "OL" }, - { 1, "NULL" }, - { 6, "NULL" }, - { 0, "NULL" } + { 1, "NULL", NULL, NULL }, /* unknown */ + { 2, "OL", NULL, NULL }, /* onLine */ + { 3, "OB", NULL, NULL }, /* onBattery */ + { 4, "OL BOOST", NULL, NULL }, /* onBoost */ + { 5, "OFF", NULL, NULL }, /* onSleep */ + { 6, "OFF", NULL, NULL }, /* off */ + { 7, "OL", NULL, NULL }, /* rebooting */ + { 8, "OL", NULL, NULL }, /* onECO */ + { 9, "OL BYPASS", NULL, NULL }, /* onBypass */ + { 10, "OL TRIM", NULL, NULL }, /* onBuck */ + { 11, "OL OVER", NULL, NULL }, /* onOverload */ + { 0, NULL, NULL, NULL } } ; +static info_lkp_t cyberpower_battery_status[] = { + { 1, "", NULL, NULL }, /* unknown */ + { 2, "", NULL, NULL }, /* batteryNormal */ + { 3, "LB", NULL, NULL }, /* batteryLow */ + { 0, NULL, NULL, NULL } +} ; + +static info_lkp_t cyberpower_cal_status[] = { + { 1, "", NULL, NULL }, /* Calibration Successful */ + { 2, "", NULL, NULL }, /* Calibration Invalid */ + { 3, "CAL", NULL, NULL }, /* Calibration in progress */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t cyberpower_battrepl_status[] = { + { 1, "", NULL, NULL }, /* No battery needs replacing */ + { 2, "RB", NULL, NULL }, /* Batteries need to be replaced */ + { 0, NULL, NULL, NULL } +}; + /* Snmp2NUT lookup table for CyberPower MIB */ static snmp_info_t cyberpower_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device page */ { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ups", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "CYBERPOWER", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, CYBERPOWER_OID_MODEL_NAME, - "CyberPower", SU_FLAG_STATIC, NULL, NULL }, + "CyberPower", SU_FLAG_STATIC, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.1.2.3.0", "", SU_FLAG_STATIC, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.1.2.1.0", - "", SU_FLAG_STATIC, NULL }, + "", SU_FLAG_STATIC, NULL }, { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.1.2.2.0", "", 0, NULL }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.4.1.1.0", "", - 0 /*SU_STATUS_PWR*/, &cyberpower_power_status[0] }, + SU_FLAG_OK | SU_STATUS_PWR, &cyberpower_power_status[0] }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.2.1.1.0", "", + SU_FLAG_OK | SU_STATUS_BATT, &cyberpower_battery_status[0] }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.7.2.7.0", "", + SU_FLAG_OK | SU_STATUS_CAL, &cyberpower_cal_status[0] }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.3808.1.1.1.2.2.5.0", "", + SU_FLAG_OK | SU_STATUS_RB, &cyberpower_battrepl_status[0] }, + { "ups.load", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.4.2.3.0", "", + 0, NULL }, - /* Battery runtime is expressed in minutes */ - { "battery.runtime", 0, 60.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.4.0", "", + /* Battery runtime is expressed in seconds */ + { "battery.runtime", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.4.0", "", 0, NULL }, /* The elapsed time in seconds since the * UPS has switched to battery power */ { "battery.runtime.elapsed", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.1.2.0", "", 0, NULL }, + /* Different generations/models reported "battery.voltage" by different OIDs: */ + { "battery.voltage", 0, 0.1, ".1.3.6.1.2.1.33.1.2.5.0", "", + 0, NULL }, { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.2.2.2.0", "", 0, NULL }, + { "battery.voltage.nominal", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.8.0", "", + 0, NULL }, + /* Different generations/models reported "battery.current" by different OIDs: */ + { "battery.current", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.4.2.4.0", "", + 0, NULL }, { "battery.current", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.2.2.7.0", "", 0, NULL }, + { "battery.charge", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.1.0", "", + 0, NULL }, + { "battery.temperature", 0, 1.0, ".1.3.6.1.4.1.3808.1.1.1.2.2.3.0", "", + 0, NULL }, + + { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.3.2.1.0", "", + 0, NULL }, + { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.3.2.4.0", "", + 0, NULL }, + + { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.4.2.1.0", "", + 0, NULL }, + { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.4.2.2.0", "", + 0, NULL }, + { "output.current", 0, 0.1, ".1.3.6.1.4.1.3808.1.1.1.4.2.4.0", "", + 0, NULL }, + + /* Delays affecting instant commands */ + + /* upsAdvanceConfigReturnDelay */ + { "ups.delay.start", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.3808.1.1.1.5.2.9.0", "0", + SU_FLAG_OK | SU_TYPE_TIME, NULL }, + /* Not provided by CPS-MIB */ + { "ups.delay.reboot", 0, 1.0, NULL, "0", + SU_FLAG_OK | SU_FLAG_ABSENT, NULL }, + /* upsAdvanceConfigSleepDelay */ + { "ups.delay.shutdown", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.3808.1.1.1.5.2.11.0", "60", + SU_FLAG_OK | SU_TYPE_TIME, NULL }, + /* instant commands. */ + /* upsAdvanceControlUpsOff */ + { "load.off", 0, 2, ".1.3.6.1.4.1.3808.1.1.1.6.2.1.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* upsAdvanceControlTurnOnUPS */ + { "load.on", 0, 2, ".1.3.6.1.4.1.3808.1.1.1.6.2.6.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* upsAdvanceControlUpsOff */ + { "shutdown.stayoff", 0, 3, ".1.3.6.1.4.1.3808.1.1.1.6.2.6.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* upsAdvanceControlUpsSleep */ + { "shutdown.return", 0, 3, ".1.3.6.1.4.1.3808.1.1.1.6.2.3.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* upsAdvanceControlSimulatePowerFail */ + { "test.failure.start", 0, 2, ".1.3.6.1.4.1.3808.1.1.1.6.2.4.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* upsAdvanceTestIndicators */ + { "test.panel.start", 0, 2, ".1.3.6.1.4.1.3808.1.1.1.7.2.5.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* upsAdvanceTestDiagnostics */ + { "test.battery.start", 0, 2, ".1.3.6.1.4.1.3808.1.1.1.7.2.2.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* upsAdvanceTestRuntimeCalibration */ + { "calibrate.start", 0, 2, ".1.3.6.1.4.1.3808.1.1.1.7.2.6.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + { "calibrate.stop", 0, 3, ".1.3.6.1.4.1.3808.1.1.1.7.2.6.0", NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } + { NULL, 0, 0, NULL, NULL, 0, NULL } } ; -mib2nut_info_t cyberpower = { "cyberpower", CYBERPOWER_MIB_VERSION, "", - CYBERPOWER_OID_MODEL_NAME, cyberpower_mib, CYBERPOWER_SYSOID }; +mib2nut_info_t cyberpower = { "cyberpower", CYBERPOWER_MIB_VERSION, NULL, + CYBERPOWER_OID_MODEL_NAME, cyberpower_mib, CYBERPOWER_SYSOID, NULL }; diff --git a/drivers/delta_ups-hid.c b/drivers/delta_ups-hid.c new file mode 100644 index 0000000..49d5af9 --- /dev/null +++ b/drivers/delta_ups-hid.c @@ -0,0 +1,323 @@ +/* delta_ups-hid.c - data mapping subdriver to monitor Delta UPS USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2012 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2013 Charles Lepple + * 2020 Luka Kovacic + * 2021 Jungeon Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" /* must be first */ + +#include "usbhid-ups.h" +#include "delta_ups-hid.h" +#include "main.h" /* for getval() */ +#include "usb-common.h" + +#define DELTA_UPS_HID_VERSION "Delta UPS HID 0.6" + +/* Delta UPS */ +#define DELTA_UPS_VENDORID 0x05dd + +/* USB IDs device table */ +static usb_device_id_t delta_ups_usb_device_table[] = { + /* Delta RT Series, Single Phase, 1/2/3 kVA */ + /* Delta UPS Amplon R Series, Single Phase UPS, 1/2/3 kVA */ + { USB_DEVICE(DELTA_UPS_VENDORID, 0x041b), NULL }, + + /* Terminating entry */ + { 0, 0, NULL } +}; + +/* --------------------------------------------------------------- */ +/* Vendor-specific usage table */ +/* --------------------------------------------------------------- */ + +/* DELTA usage table */ +static usage_lkp_t delta_ups_usage_lkp[] = { + { "DELTA1", 0x00000000 }, + { "DELTA2", 0xff000055 }, +/* { "DELTA3", 0xffff0010 }, */ + { "DeltaCustom", 0xffff0010 }, + { "DELTA4", 0xffff0056 }, +/* { "DELTA5", 0xffff0057 }, */ + { "DeltaConfigTransferLowMax", 0xffff0057 }, +/* { "DELTA6", 0xffff0058 }, */ + { "DeltaConfigTransferLowMin", 0xffff0058 }, +/* { "DELTA7", 0xffff0059 }, */ + { "DeltaConfigTransferHighMax", 0xffff0059 }, +/* { "DELTA8", 0xffff005a }, */ + { "DeltaConfigTransferHighMin", 0xffff005a }, + { "DELTA9", 0xffff0060 }, +/* { "DELTA10", 0xffff0061 }, */ + { "DeltaConfigExternalBatteryPack", 0xffff0061 }, + { "DELTA11", 0xffff0062 }, + { "DELTA12", 0xffff0063 }, + { "DELTA13", 0xffff0064 }, + { "DELTA14", 0xffff0065 }, + { "DELTA15", 0xffff0066 }, + { "DELTA16", 0xffff0067 }, + { "DELTA17", 0xffff0068 }, +/* { "DELTA18", 0xffff0075 }, */ + { "DeltaModelName", 0xffff0075 }, + { "DELTA19", 0xffff0076 }, +/* { "DELTA20", 0xffff007c }, */ + { "DeltaUPSType", 0xffff007c }, + { "DELTA21", 0xffff007d }, +/* { "DELTA22", 0xffff0081 }, */ + { "DeltaConfigStartPowerRestoreDelay", 0xffff0081 }, +/* { "DELTA23", 0xffff0091 }, */ + { "DeltaOutputSource", 0xffff0091 }, + { "DELTA24", 0xffff0092 }, + { "DELTA25", 0xffff0093 }, + { "DELTA26", 0xffff0094 }, + { "DELTA27", 0xffff0095 }, + { "DELTA28", 0xffff0096 }, + { "DELTA29", 0xffff0097 }, + { "DELTA30", 0xffff0098 }, + { "DELTA31", 0xffff0099 }, + { "DELTA32", 0xffff009a }, +/* { "DELTA33", 0xffff009b }, */ + { "DeltaConfigSensitivity", 0xffff009b }, +/* { "DELTA34", 0xffff009c }, */ + { "DeltaConfigStartPowerRestore", 0xffff009c }, + + /* Terminating entry */ + { NULL, 0 } +}; + +static usage_tables_t delta_ups_utab[] = { + delta_ups_usage_lkp, + hid_usage_lkp, + NULL, +}; + +/* --------------------------------------------------------------- */ +/* Helper lookup tables and mapping functions */ +/* --------------------------------------------------------------- */ + +static info_lkp_t delta_ups_sensitivity_info[] = { + { 0, "normal", NULL, NULL }, + { 1, "reduced", NULL, NULL }, + { 2, "low", NULL, NULL }, + + /* Terminating entry */ + { 0, NULL, NULL, NULL } +}; + +static const char *delta_ups_type_fun(double value) +{ + static const char* upstypes[] = { + "online", + "offline", + "line-interactive", + "3-phase", + "split-phase" + }; + + int type = (int)value & 0xf; + if (type == 6) { + type = 4; + } else if (2 < type && type <= 5) { + type -= 2; + } + + if (type < 0 || type > 4) { + return NULL; + } + + return upstypes[type]; +} + +static info_lkp_t delta_ups_type_info[] = { + { 0, NULL, delta_ups_type_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t delta_ups_output_source_info[] = { + { 0, "normal", NULL, NULL }, + { 1, "battery", NULL, NULL }, + { 2, "bypass/reserve", NULL, NULL }, + { 3, "reducing", NULL, NULL }, + { 4, "boosting", NULL, NULL }, + { 5, "manual bypass", NULL, NULL }, + { 6, "other", NULL, NULL }, + { 7, "no output", NULL, NULL }, + { 8, "on eco", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* --------------------------------------------------------------- */ +/* HID2NUT lookup table */ +/* --------------------------------------------------------------- */ + +static hid_info_t delta_ups_hid2nut[] = { + { "input.sensitivity", ST_FLAG_RW, 0, "UPS.DeltaCustom.[1].DeltaConfigSensitivity", NULL, "%s", 0, delta_ups_sensitivity_info }, + { "input.voltage.nominal", 0, 0, "UPS.PowerSummary.Input.ConfigVoltage", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, + { "input.voltage", 0, 0, "UPS.PowerSummary.Input.Voltage", NULL, "%.1f", HU_FLAG_QUICK_POLL, NULL }, + { "input.voltage", 0, 0, "UPS.PowerConverter.Input.Voltage", NULL, "%.1f", HU_FLAG_QUICK_POLL, NULL }, + { "input.transfer.low", ST_FLAG_RW, 0, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.1f", 0, NULL }, + { "input.transfer.high", ST_FLAG_RW, 0, "UPS.PowerConverter.Output.HighVoltageTransfer", NULL, "%.1f", 0, NULL }, + { "input.transfer.low.min", 0, 0, "UPS.PowerConverter.Output.DeltaConfigTransferLowMin", NULL, "%.1f", HU_FLAG_STATIC, NULL }, + { "input.transfer.low.max", 0, 0, "UPS.PowerConverter.Output.DeltaConfigTransferLowMax", NULL, "%.1f", HU_FLAG_STATIC, NULL }, + { "input.transfer.high.min", 0, 0, "UPS.PowerConverter.Output.DeltaConfigTransferHighMin", NULL, "%.1f", HU_FLAG_STATIC, NULL }, + { "input.transfer.high.max", 0, 0, "UPS.PowerConverter.Output.DeltaConfigTransferHighMax", NULL, "%.1f", HU_FLAG_STATIC, NULL }, + /* FIXME: Check vs hardware, is this an "input" or "outlet/outpu" value after all? */ + { "input.source", 0, 0, "UPS.OutletSystem.Outlet.DeltaOutputSource", NULL, "%s", 0, delta_ups_output_source_info }, + { "input.frequency", 0, 0, "UPS.PowerConverter.Input.Frequency", NULL, "%.1f", HU_FLAG_QUICK_POLL, NULL }, + + { "battery.voltage.nominal", 0, 0, "UPS.BatterySystem.Battery.ConfigVoltage", NULL, "%.1f", HU_FLAG_STATIC, NULL }, + { "battery.voltage", 0, 0, "UPS.BatterySystem.Battery.Voltage", NULL, "%.1f", HU_FLAG_QUICK_POLL, NULL }, + { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, + { "battery.charge", 0, 0, "UPS.BatterySystem.Battery.RemainingCapacity", NULL, "%.0f", 0, NULL }, + { "battery.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, +/* { "battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, */ + { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "battery.temperature", 0, 0, "UPS.BatterySystem.Temperature", NULL, "%s", HU_FLAG_QUICK_POLL, kelvin_celsius_conversion }, + { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, + { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, + { "battery.capacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "battery.capacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + + { "output.voltage.nominal", 0, 0, "UPS.Flow.ConfigVoltage", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, + { "output.frequency.nominal", 0, 0, "UPS.Flow.ConfigFrequency", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, + { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.1f", HU_FLAG_QUICK_POLL, NULL }, + { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", NULL, "%.1f", HU_FLAG_QUICK_POLL, NULL }, + { "output.current", 0, 0, "UPS.PowerConverter.Output.Current", NULL, "%.1f", HU_FLAG_QUICK_POLL, NULL }, + + { "ups.beeper.status", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "%s", HU_FLAG_QUICK_POLL, beeper_info }, + { "ups.test.result", 0, 0, "UPS.BatterySystem.Test", NULL, "%s", 0, test_read_info }, + { "ups.type", 0, 0, "UPS.DeltaCustom.[1].DeltaUPSType", NULL, "%s", HU_FLAG_STATIC, delta_ups_type_info }, + { "ups.start.auto", ST_FLAG_RW, 0, "UPS.DeltaCustom.[1].DeltaConfigStartPowerRestore", NULL, "%s", 0, yes_no_info }, + { "ups.power.nominal", 0, 0, "UPS.Flow.ConfigApparentPower", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "ups.realpower", 0, 0, "UPS.OutletSystem.Outlet.ActivePower", NULL, "%.1f", HU_FLAG_QUICK_POLL, NULL }, + { "ups.realpower", 0, 0, "UPS.PowerConverter.Output.ActivePower", NULL, "%.1f", HU_FLAG_QUICK_POLL, NULL }, + { "ups.load", 0, 0, "UPS.OutletSystem.Outlet.PercentLoad", NULL, "%.1f", HU_FLAG_QUICK_POLL, NULL }, + /* Per comments to PR #807 these 3 declarations are populated elsewhere, + * by delta_ups_format_*() functions hooks; see: + * https://github.com/networkupstools/nut/pull/807#discussion_r501496383 + */ +/* { "ups.mfr", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, + { "ups.model", 0, 0, "UPS.PowerSummary.iProduct", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, + { "ups.serial", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, +*/ + { "ups.delay.start", ST_FLAG_RW, 0, "UPS.OutletSystem.Outlet.DeltaConfigStartPowerRestoreDelay", NULL, "%.0f", 0, NULL }, +/* mge-hid.c simlar configurable settings: + { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL }, + { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL }, +... + { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, +*/ + { "ups.timer.start", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, + { "ups.timer.shutdown", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, + { "ups.timer.reboot", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeReboot", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, + + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Good", NULL, NULL, HU_FLAG_QUICK_POLL, off_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.InternalFailure", NULL, NULL, HU_FLAG_QUICK_POLL, commfault_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, HU_FLAG_QUICK_POLL, shutdownimm_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FullyCharged", NULL, NULL, HU_FLAG_QUICK_POLL, fullycharged_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FullyDischarged", NULL, NULL, HU_FLAG_QUICK_POLL, depleted_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, HU_FLAG_QUICK_POLL, replacebatt_info }, + { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.VoltageOutOfRange", NULL, NULL, HU_FLAG_QUICK_POLL, vrange_info }, + { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.Buck", NULL, NULL, HU_FLAG_QUICK_POLL, trim_info }, + { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.Boost", NULL, NULL, HU_FLAG_QUICK_POLL, boost_info }, + { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.Overload", NULL, NULL, HU_FLAG_QUICK_POLL, overload_info }, + { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.Used", NULL, NULL, HU_FLAG_QUICK_POLL, nobattery_info }, + { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.OverTemperature", NULL, NULL, HU_FLAG_QUICK_POLL, overheat_info }, + { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.InternalFailure", NULL, NULL, HU_FLAG_QUICK_POLL, commfault_info }, + { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.AwaitingPower", NULL, NULL, HU_FLAG_QUICK_POLL, awaitingpower_info }, + + { "beeper.on", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, + { "beeper.off", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, + { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, + { "beeper.disable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, + { "beeper.mute", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, + + /* 10 seconds battery test */ + { "test.battery.start.quick", 0, 0, "UPS.BatterySystem.Test", NULL, "1", HU_TYPE_CMD, NULL }, + /* test until battery low */ + { "test.battery.start.deep", 0, 0, "UPS.BatterySystem.Test", NULL, "2", HU_TYPE_CMD, NULL }, + { "test.battery.stop", 0, 0, "UPS.BatterySystem.Test", NULL, "3", HU_TYPE_CMD, NULL }, + + { "load.on.delay", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, + { "load.off.delay", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, + + { "shutdown.stop", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, + { "shutdown.reboot", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL }, + + /* Terminating entry */ + { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } +}; + +static const char *delta_ups_format_mfr(HIDDevice_t *hd) { + return hd->Vendor ? hd->Vendor : "Delta"; +} + +static const char *delta_ups_format_model(HIDDevice_t *hd) { + static char model[SMALLBUF]; + HIDGetItemString(udev, "UPS.DeltaCustom.[1].DeltaModelName", model, sizeof(model), delta_ups_utab); + + if (strlen(model) < 1) { + return hd->Product; + } + + return model; +} + +static const char *delta_ups_format_serial(HIDDevice_t *hd) { + return hd->Serial; +} + +/* this function allows the subdriver to "claim" a device: return 1 if + * the device is supported by this subdriver, else 0. */ +static int delta_ups_claim(HIDDevice_t *hd) { + int status = is_usb_device_supported(delta_ups_usb_device_table, hd); + + switch (status) { + case SUPPORTED: + return 1; + + case POSSIBLY_SUPPORTED: + /* by default, reject, unless the productid option is given */ + if (getval("productid")) { + return 1; + } + possibly_supported("Delta", hd); + return 0; + + case NOT_SUPPORTED: + default: + return 0; + } +} + +subdriver_t delta_ups_subdriver = { + DELTA_UPS_HID_VERSION, + delta_ups_claim, + delta_ups_utab, + delta_ups_hid2nut, + delta_ups_format_model, + delta_ups_format_mfr, + delta_ups_format_serial, + fix_report_desc, +}; diff --git a/drivers/delta_ups-hid.h b/drivers/delta_ups-hid.h new file mode 100644 index 0000000..d8c039f --- /dev/null +++ b/drivers/delta_ups-hid.h @@ -0,0 +1,31 @@ +/* delta_ups-hid.h - data mapping subdriver to monitor Delta UPS USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2009 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2021 Jungeon Kim + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DELTA_UPS_HID_H +#define DELTA_UPS_HID_H + +#include "usbhid-ups.h" + +extern subdriver_t delta_ups_subdriver; + +#endif /* DELTA_UPS_HID_H */ diff --git a/drivers/delta_ups-mib.c b/drivers/delta_ups-mib.c new file mode 100644 index 0000000..770b0d7 --- /dev/null +++ b/drivers/delta_ups-mib.c @@ -0,0 +1,365 @@ +/* delta_ups-mib.c - subdriver to monitor delta_ups SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2012 Arnaud Quette + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-snmp-subdriver.sh script. It must be customized! + * + * MIB reference: http://www.networkupstools.org/ups-protocols/snmp/DeltaUPSv4.mib + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "delta_ups-mib.h" + +#define DELTA_UPS_MIB_VERSION "0.5" + +#define DELTA_UPS_SYSOID ".1.3.6.1.4.1.2254.2.4" + +/* To create a value lookup structure (as needed on the 2nd line of the example + * below), use the following kind of declaration, outside of the present snmp_info_t[]: + * static info_lkp_t delta_ups_onbatt_info[] = { + * { 1, "OB", NULL, NULL }, + * { 2, "OL", NULL, NULL }, + * { 0, NULL, NULL, NULL } + * }; + */ + +static info_lkp_t delta_ups_upstype_info[] = { + { 1, "on-line", NULL, NULL }, + { 2, "off-line", NULL, NULL }, + { 3, "line-interactive", NULL, NULL }, + { 4, "3phase", NULL, NULL }, + { 5, "splite-phase", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t delta_ups_pwr_info[] = { + { 0, "OL", NULL, NULL }, /* normal */ + { 1, "OB", NULL, NULL }, /* battery */ + { 2, "BYPASS", NULL, NULL }, /* bypass */ + { 3, "TRIM", NULL, NULL }, /* reducing */ + { 4, "BOOST", NULL, NULL }, /* boosting */ + { 5, "BYPASS", NULL, NULL }, /* manualBypass */ + /*{ 6, "NULL", NULL, NULL },*/ /* other */ + { 7, "OFF", NULL, NULL }, /* none */ + { 0, NULL, NULL, NULL } +} ; + +/* DELTA_UPS Snmp2NUT lookup table */ +static snmp_info_t delta_ups_mib[] = { + + /* Data format: + * { info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar }, + * + * info_type: NUT INFO_ or CMD_ element name + * info_flags: flags to set in addinfo + * info_len: length of strings if STR + * cmd value if CMD, multiplier otherwise + * OID: SNMP OID or NULL + * dfl: default value + * flags: snmp-ups internal flags (FIXME: ...) + * oid2info: lookup table between OID and NUT values + * + * Example: + * { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, + * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, delta_ups_onbatt_info }, + * + * To create a value lookup structure (as needed on the 2nd line), use the + * following kind of declaration, outside of the present snmp_info_t[]: + * static info_lkp_t delta_ups_onbatt_info[] = { + * { 1, "OB" }, + * { 2, "OL" }, + * { 0, NULL } + * }; + */ + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* dupsIdentManufacturer.0 = STRING: "Socomec" */ + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* dupsIdentModel.0 = STRING: "NETYS RT 1/1 UPS" */ + { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.2.0", NULL, SU_FLAG_OK, NULL }, + /* dupsIdentAgentSoftwareVersion.0 = STRING: "2.0h " */ + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.4.0", NULL, SU_FLAG_OK, NULL }, + /* dupsIdentUPSSoftwareVersion.0 = STRING: "1.1" */ + { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.1.3.0", NULL, SU_FLAG_OK, NULL }, + /* dupsType.0 = INTEGER: on-line(1) */ + { "ups.type", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.19.0", NULL, SU_FLAG_OK, delta_ups_upstype_info }, + /* dupsOutputLoad1.0 = INTEGER: 29 */ + { "ups.load", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.7.0", NULL, SU_FLAG_OK, NULL }, + /* dupsRatingOutputVA.0 = INTEGER: 2200 */ + { "ups.power", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.7.0", NULL, SU_FLAG_OK, NULL }, + /* dupsRatingOutputVoltage.0 = INTEGER: 230 */ + { "output.voltage.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.8.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputVoltage1.0 = INTEGER: 2300 */ + { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.5.4.0", NULL, SU_FLAG_OK, NULL }, + /* dupsRatingOutputFrequency.0 = INTEGER: 50 */ + { "output.frequency.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.9.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputCurrent1.0 = INTEGER: 23 */ + { "output.current", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.5.5.0", NULL, SU_FLAG_OK, NULL }, + /* dupsRatingInputVoltage.0 = INTEGER: 230 */ + { "input.voltage.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.10.0", NULL, SU_FLAG_OK, NULL }, + /* dupsInputVoltage1.0 = INTEGER: 2280 */ + { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.4.3.0", NULL, SU_FLAG_OK, NULL }, + /* dupsRatingInputFrequency.0 = INTEGER: 50 */ + { "input.frequency.nominal", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.11.0", NULL, SU_FLAG_OK, NULL }, + /* dupsInputFrequency1.0 = INTEGER: 499 */ + { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.2254.2.4.4.2.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputSource.0 = INTEGER: normal(0) */ + { "ups.status", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.1.0", NULL, SU_FLAG_OK, delta_ups_pwr_info }, + + /* Remaining unmapped variables. + * Mostly the first field (string) is to be changed + * Check docs/nut-names.txt for the right variable names + */ +#if 0 + /* dupsIdentName.0 = "" */ + { "unmapped.dupsIdentName", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.5.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAttachedDevices.0 = "" */ + { "unmapped.dupsAttachedDevices", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* dupsRatingBatteryVoltage.0 = INTEGER: 0 */ + { "unmapped.dupsRatingBatteryVoltage", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.12.0", NULL, SU_FLAG_OK, NULL }, + /* dupsLowTransferVoltUpBound.0 = INTEGER: 0 Volt */ + { "unmapped.dupsLowTransferVoltUpBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.13.0", NULL, SU_FLAG_OK, NULL }, + /* dupsLowTransferVoltLowBound.0 = INTEGER: 0 Volt */ + { "unmapped.dupsLowTransferVoltLowBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.14.0", NULL, SU_FLAG_OK, NULL }, + /* dupsHighTransferVoltUpBound.0 = INTEGER: 0 Volt */ + { "unmapped.dupsHighTransferVoltUpBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.15.0", NULL, SU_FLAG_OK, NULL }, + /* dupsHighTransferVoltLowBound.0 = INTEGER: 0 Volt */ + { "unmapped.dupsHighTransferVoltLowBound", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.16.0", NULL, SU_FLAG_OK, NULL }, + /* dupsLowBattTime.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsLowBattTime", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.17.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutletRelays.0 = INTEGER: 2 */ + { "unmapped.dupsOutletRelays", 0, 1, ".1.3.6.1.4.1.2254.2.4.1.18.0", NULL, SU_FLAG_OK, NULL }, + /* dupsShutdownType.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsShutdownType", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.1.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAutoReboot.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsAutoReboot", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.2.0", NULL, SU_FLAG_OK, NULL }, + /* dupsShutdownAction.0 = INTEGER: 0 */ + { "unmapped.dupsShutdownAction", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.3.0", NULL, SU_FLAG_OK, NULL }, + /* dupsRestartAction.0 = INTEGER: 0 */ + { "unmapped.dupsRestartAction", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.4.0", NULL, SU_FLAG_OK, NULL }, + /* dupsSetOutletRelay.0 = INTEGER: 1 */ + { "unmapped.dupsSetOutletRelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.5.0", NULL, SU_FLAG_OK, NULL }, + /* dupsRelayOffDelay.0 = INTEGER: 0 */ + { "unmapped.dupsRelayOffDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.6.0", NULL, SU_FLAG_OK, NULL }, + /* dupsRelayOnDelay.0 = INTEGER: 0 */ + { "unmapped.dupsRelayOnDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.2.7.0", NULL, SU_FLAG_OK, NULL }, + /* dupsConfigBuzzerAlarm.0 = INTEGER: alarm(1) */ + { "unmapped.dupsConfigBuzzerAlarm", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.1.0", NULL, SU_FLAG_OK, NULL }, + /* dupsConfigBuzzerState.0 = INTEGER: disable(2) */ + { "unmapped.dupsConfigBuzzerState", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.2.0", NULL, SU_FLAG_OK, NULL }, + /* dupsConfigSensitivity.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsConfigSensitivity", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.3.0", NULL, SU_FLAG_OK, NULL }, + /* dupsConfigLowVoltageTransferPoint.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsConfigLowVoltageTransferPoint", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.4.0", NULL, SU_FLAG_OK, NULL }, + /* dupsConfigHighVoltageTransferPoint.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsConfigHighVoltageTransferPoint", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.5.0", NULL, SU_FLAG_OK, NULL }, + /* dupsConfigShutdownOSDelay.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsConfigShutdownOSDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.6.0", NULL, SU_FLAG_OK, NULL }, + /* dupsConfigUPSBootDelay.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsConfigUPSBootDelay", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.7.0", NULL, SU_FLAG_OK, NULL }, + /* dupsConfigExternalBatteryPack.0 = INTEGER: 0 */ + { "unmapped.dupsConfigExternalBatteryPack", 0, 1, ".1.3.6.1.4.1.2254.2.4.3.8.0", NULL, SU_FLAG_OK, NULL }, + /* dupsInputNumLines.0 = INTEGER: 1 */ + { "unmapped.dupsInputNumLines", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.1.0", NULL, SU_FLAG_OK, NULL }, + /* dupsInputCurrent1.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsInputCurrent1", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.4.0", NULL, SU_FLAG_OK, NULL }, + /* dupsInputFrequency2.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsInputFrequency2", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.5.0", NULL, SU_FLAG_OK, NULL }, + /* dupsInputVoltage2.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsInputVoltage2", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.6.0", NULL, SU_FLAG_OK, NULL }, + /* dupsInputCurrent2.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsInputCurrent2", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.7.0", NULL, SU_FLAG_OK, NULL }, + /* dupsInputFrequency3.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsInputFrequency3", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.8.0", NULL, SU_FLAG_OK, NULL }, + /* dupsInputVoltage3.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsInputVoltage3", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.9.0", NULL, SU_FLAG_OK, NULL }, + /* dupsInputCurrent3.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsInputCurrent3", 0, 1, ".1.3.6.1.4.1.2254.2.4.4.10.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputFrequency.0 = INTEGER: 499 0.1 Hertz */ + { "unmapped.dupsOutputFrequency", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.2.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputNumLines.0 = INTEGER: 1 */ + { "unmapped.dupsOutputNumLines", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.3.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputPower1.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsOutputPower1", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.6.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputVoltage2.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsOutputVoltage2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.8.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputCurrent2.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsOutputCurrent2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.9.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputPower2.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsOutputPower2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.10.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputLoad2.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsOutputLoad2", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.11.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputVoltage3.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsOutputVoltage3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.12.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputCurrent3.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsOutputCurrent3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.13.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputPower3.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsOutputPower3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.14.0", NULL, SU_FLAG_OK, NULL }, + /* dupsOutputLoad3.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsOutputLoad3", 0, 1, ".1.3.6.1.4.1.2254.2.4.5.15.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypassFrequency.0 = INTEGER: 499 0.1 Hertz */ + { "unmapped.dupsBypassFrequency", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.1.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypassNumLines.0 = INTEGER: 1 */ + { "unmapped.dupsBypassNumLines", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.2.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypassVoltage1.0 = INTEGER: 2280 */ + { "unmapped.dupsBypassVoltage1", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.3.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypassCurrent1.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsBypassCurrent1", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.4.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypassPower1.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsBypassPower1", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.5.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypassVoltage2.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsBypassVoltage2", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.6.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypassCurrent2.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsBypassCurrent2", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.7.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypassPower2.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsBypassPower2", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.8.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypassVoltage3.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsBypassVoltage3", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.9.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypassCurrent3.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsBypassCurrent3", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.10.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypassPower3.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsBypassPower3", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.11.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypass.12.0 = NULL */ + { "unmapped.dupsBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.12.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypass.13.0 = NULL */ + { "unmapped.dupsBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.13.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBypass.14.0 = NULL */ + { "unmapped.dupsBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.6.14.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBatteryCondiction.0 = INTEGER: good(0) */ + { "unmapped.dupsBatteryCondiction", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.1.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBatteryStatus.0 = INTEGER: ok(0) */ + { "unmapped.dupsBatteryStatus", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.2.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBatteryCharge.0 = INTEGER: charging(1) */ + { "unmapped.dupsBatteryCharge", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.3.0", NULL, SU_FLAG_OK, NULL }, + /* dupsSecondsOnBattery.0 = INTEGER: 0 seconds */ + { "unmapped.dupsSecondsOnBattery", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.4.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBatteryEstimatedTime.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsBatteryEstimatedTime", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.5.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBatteryVoltage.0 = INTEGER: 550 0.1 Volt DC */ + { "unmapped.dupsBatteryVoltage", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.6.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBatteryCurrent.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsBatteryCurrent", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.7.0", NULL, SU_FLAG_OK, NULL }, + /* dupsBatteryCapacity.0 = INTEGER: 100 percent */ + { "unmapped.dupsBatteryCapacity", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.8.0", NULL, SU_FLAG_OK, NULL }, + /* dupsTemperature.0 = INTEGER: 32 degrees Centigrade */ + { "unmapped.dupsTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.7.9.0", NULL, SU_FLAG_OK, NULL }, + /* dupsLastReplaceDate.0 = Wrong Type (should be OCTET STRING): NULL */ + { "unmapped.dupsLastReplaceDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.7.10.0", NULL, SU_FLAG_OK, NULL }, + /* dupsNextReplaceDate.0 = Wrong Type (should be OCTET STRING): NULL */ + { "unmapped.dupsNextReplaceDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.7.11.0", NULL, SU_FLAG_OK, NULL }, + /* dupsTestType.0 = INTEGER: abort(0) */ + { "unmapped.dupsTestType", 0, 1, ".1.3.6.1.4.1.2254.2.4.8.1.0", NULL, SU_FLAG_OK, NULL }, + /* dupsTestResultsSummary.0 = INTEGER: noTestsInitiated(0) */ + { "unmapped.dupsTestResultsSummary", 0, 1, ".1.3.6.1.4.1.2254.2.4.8.2.0", NULL, SU_FLAG_OK, NULL }, + /* dupsTestResultsDetail.0 = Wrong Type (should be OCTET STRING): NULL */ + { "unmapped.dupsTestResultsDetail", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2254.2.4.8.3.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmDisconnect.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmDisconnect", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.1.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmPowerFail.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmPowerFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.2.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmBatteryLow.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmBatteryLow", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.3.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmLoadWarning.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsAlarmLoadWarning", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.4.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmLoadSeverity.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsAlarmLoadSeverity", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.5.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmLoadOnBypass.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmLoadOnBypass", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.6.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmUPSFault.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmUPSFault", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.7.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmBatteryGroundFault.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsAlarmBatteryGroundFault", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.8.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmTestInProgress.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmTestInProgress", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.9.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmBatteryTestFail.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmBatteryTestFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.10.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmFuseFailure.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmFuseFailure", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.11.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmOutputOverload.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmOutputOverload", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.12.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmOutputOverCurrent.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsAlarmOutputOverCurrent", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.13.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmInverterAbnormal.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmInverterAbnormal", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.14.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmRectifierAbnormal.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsAlarmRectifierAbnormal", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.15.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmReserveAbnormal.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsAlarmReserveAbnormal", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.16.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmLoadOnReserve.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsAlarmLoadOnReserve", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.17.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmOverTemperature.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmOverTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.18.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmOutputBad.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmOutputBad", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.19.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmBypassBad.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmBypassBad", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.20.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmUPSOff.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmUPSOff", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.21.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmChargerFail.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmChargerFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.22.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmFanFail.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmFanFail", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.23.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmEconomicMode.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmEconomicMode", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.24.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmOutputOff.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmOutputOff", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.25.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmSmartShutdown.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmSmartShutdown", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.26.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmEmergencyPowerOff.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmEmergencyPowerOff", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.27.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmUPSShutdown.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmUPSShutdown", 0, 1, ".1.3.6.1.4.1.2254.2.4.9.28.0", NULL, SU_FLAG_OK, NULL }, + /* dupsEnvTemperature.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsEnvTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.1.0", NULL, SU_FLAG_OK, NULL }, + /* dupsEnvHumidity.0 = Wrong Type (should be INTEGER): NULL */ + { "unmapped.dupsEnvHumidity", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.2.0", NULL, SU_FLAG_OK, NULL }, + /* dupsEnvSetTemperatureLimit.0 = INTEGER: 40 degrees Centigrade */ + { "unmapped.dupsEnvSetTemperatureLimit", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.3.0", NULL, SU_FLAG_OK, NULL }, + /* dupsEnvSetHumidityLimit.0 = INTEGER: 90 percentage */ + { "unmapped.dupsEnvSetHumidityLimit", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.4.0", NULL, SU_FLAG_OK, NULL }, + /* dupsEnvSetEnvRelay1.0 = INTEGER: normalOpen(0) */ + { "unmapped.dupsEnvSetEnvRelay1", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.5.0", NULL, SU_FLAG_OK, NULL }, + /* dupsEnvSetEnvRelay2.0 = INTEGER: normalOpen(0) */ + { "unmapped.dupsEnvSetEnvRelay2", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.6.0", NULL, SU_FLAG_OK, NULL }, + /* dupsEnvSetEnvRelay3.0 = INTEGER: normalOpen(0) */ + { "unmapped.dupsEnvSetEnvRelay3", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.7.0", NULL, SU_FLAG_OK, NULL }, + /* dupsEnvSetEnvRelay4.0 = INTEGER: normalOpen(0) */ + { "unmapped.dupsEnvSetEnvRelay4", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.8.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmOverEnvTemperature.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmOverEnvTemperature", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.9.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmOverEnvHumidity.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmOverEnvHumidity", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.10.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmEnvRelay1.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmEnvRelay1", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.11.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmEnvRelay2.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmEnvRelay2", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.12.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmEnvRelay3.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmEnvRelay3", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.13.0", NULL, SU_FLAG_OK, NULL }, + /* dupsAlarmEnvRelay4.0 = INTEGER: off(0) */ + { "unmapped.dupsAlarmEnvRelay4", 0, 1, ".1.3.6.1.4.1.2254.2.4.10.14.0", NULL, SU_FLAG_OK, NULL }, +#endif /* #if 0 */ + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + +mib2nut_info_t delta_ups = { "delta_ups", DELTA_UPS_MIB_VERSION, NULL, NULL, delta_ups_mib, DELTA_UPS_SYSOID, NULL }; diff --git a/drivers/delta_ups-mib.h b/drivers/delta_ups-mib.h new file mode 100644 index 0000000..2dfc367 --- /dev/null +++ b/drivers/delta_ups-mib.h @@ -0,0 +1,29 @@ +/* delta_ups-mib.h - subdriver to monitor delta_ups SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2012 Arnaud Quette + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DELTA_UPS_MIB_H +#define DELTA_UPS_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t delta_ups; + +#endif /* DELTA_UPS_MIB_H */ diff --git a/drivers/dstate-hal.c b/drivers/dstate-hal.c deleted file mode 100644 index 5fcf68f..0000000 --- a/drivers/dstate-hal.c +++ /dev/null @@ -1,700 +0,0 @@ -/* dstate-hal.c - Network UPS Tools driver-side state management - This is a compatibility interface that encapsulate the HAL bridge - into the NUT dstate API for NUT drivers - - Copyright (C) 2006-2007 Arnaud Quette - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "config.h" -#include "dstate-hal.h" -#include "extstate.h" -/* #include "state.h" -#include "parseconf.h" */ - -#include - -/* FIXME: export command and RW variables (using the HAL example: addon-cpufreq and macbook addon) */ -/* beeper.enable, beeper.disable => SetBeeper(bool) - beeper.toggle => ToggleBeeper(void) - -org.freedesktop.Hal.Device.UPS.SetSounder (bool) - - -Shutdown() or ShutOff() - shutdown.return - shutdown.stayoff - shutdown.reboot - shutdown.reboot.graceful - -#define UPS_ERROR_GENERAL "GeneralError" -#define UPS_ERROR_UNSUPPORTED_FEATURE "FeatureNotSupported" -#define UPS_ERROR_PERMISSION_DENIED "PermissionDenied" - -****** implementation ******* - -#define DBUS_INTERFACE "org.freedesktop.Hal.Device.UPS" - -if (!libhal_device_claim_interface(halctx, udi, DBUS_INTERFACE, - - " \n" - " \n" - " \n" - " \n" - - &dbus_dbus_error)) { - fprintf(stderr, "Cannot claim interface: %s", dbus_dbus_error.message); - goto Error; - } - - -*/ - -/* - * static int sockfd = -1, stale = 1, alarm_active = 0; - * static struct st_tree_t *dtree_root = NULL; - * static struct conn_t *connhead = NULL; - * static struct cmdlist_t *cmdhead = NULL; - * static char *sockfn = NULL; - * static char status_buf[ST_MAX_VALUE_LEN], - * alarm_buf[ST_MAX_VALUE_LEN]; - */ - -struct ups_handler upsh; - -LibHalContext *halctx = NULL; -char *udi; -int ac_present = 0; /* 0 = false ; 1 = true */ -extern char *dbus_methods_introspection; - -static void* runtime_handler(LibHalChangeSet *cs, char* runtime); -static void* level_handler(LibHalChangeSet *cs, char* critical_level); -static void* battery_type_handler(LibHalChangeSet *cs, char* battery_type); - -/* Structure to lookup between NUT and HAL data */ -typedef struct { - const char *nut_name; /* NUT variable name */ - const char *hal_name; /* HAL variable name */ - int hal_type; /* HAL variable type */ - void *(*fun)(LibHalChangeSet *cs, - char *value); /* conversion function. */ -} info_lkp_t; - -enum hal_type_t -{ - NONE = 0, - HAL_TYPE_INT, - HAL_TYPE_BOOL, - HAL_TYPE_STRING -}; - -/* Structure to lookup between NUT commands and HAL/DBus methods */ -typedef struct { - char *nut_name; /* NUT command name */ - char *hal_name; /* HAL/DBus method name */ - char *xml_introspection; /* HAL/DBus method introspection */ -/* FIXME: how to lookup param values between HAL and NUT?? */ -/* void *(*fun)(LibHalChangeSet *cs, - char *value);*/ /* NUT function */ -} method_lkp_t; - -#if 0 -/* Data to lookup between NUT commands and HAL/DBus methods - * for dstate_addcmd() */ -static method_lkp_t nut2hal_cmd[] = -{ - /* ups.status is handled by status_set() calls */ - { - "beeper.enable", - "SetBeeper", - " \n" - " \n" - " \n" - " \n" - }, - - /* Terminating element */ - { NULL, NULL, NULL } -}; -#endif - -/* Data to lookup between NUT and HAL for dstate_setinfo() */ -static info_lkp_t nut2hal_info[] = -{ - /* ups.status is handled by status_set() calls */ - { "battery.charge.low", "battery.charge_level.low", HAL_TYPE_INT, *level_handler }, - /* { "battery.charge.low", "battery.reporting.low", HAL_TYPE_INT, NULL }, */ - { "battery.charge.low", "battery.alarm.design", HAL_TYPE_INT, NULL }, - - { "battery.charge", "battery.charge_level.current", HAL_TYPE_INT, NULL }, - { "battery.charge", "battery.charge_level.percentage", HAL_TYPE_INT, NULL }, - { "battery.charge", "battery.reporting.current", HAL_TYPE_INT, NULL }, - { "battery.charge", "battery.reporting.percentage", HAL_TYPE_INT, NULL }, - { "battery.runtime", "battery.remaining_time", HAL_TYPE_INT, *runtime_handler }, - /* raw version (PbAc) */ - { "battery.type", "battery.reporting.technology", HAL_TYPE_STRING, NULL }, - /* Human readable version */ - { "battery.type", "battery.technology", HAL_TYPE_STRING, *battery_type_handler }, - - /* AQ note: Not sure it fits! */ - /* HAL marked as mandatory! */ - { "battery.voltage", "battery.voltage.current", HAL_TYPE_INT, NULL }, - { "battery.voltage.nominal", "battery.voltage.design", HAL_TYPE_INT, NULL }, - - { "ups.mfr", "battery.vendor", HAL_TYPE_STRING, NULL }, - { "ups.model", "battery.model", HAL_TYPE_STRING, NULL }, - { "ups.serial", "battery.serial", HAL_TYPE_STRING, NULL }, - - /* Terminating element */ - { NULL, NULL, NONE, NULL } -}; - -/* Functions to lookup between NUT and HAL */ -static info_lkp_t *find_nut_info(const char *nut_varname, info_lkp_t *prev_info_item); - -/* HAL accessors wrappers */ -void hal_set_string(LibHalChangeSet *cs, const char *key, const char *value); -void hal_set_int(LibHalChangeSet *cs, const char *key, const int value); -void hal_set_bool(LibHalChangeSet *cs, const char *key, const dbus_bool_t value); -int hal_get_int(const char *key); -char *hal_get_string(const char *key); - -/* Handle warning charge level according to the critical level */ -static void* level_handler(LibHalChangeSet *cs, char* critical_level) -{ - /* Magic formula to generate the warning level */ - int int_critical_level = atoi(critical_level); - /* warning level = critical + 1/3 of (100 % - critical level), approx at the leat mod 10 */ - int int_warning_level = int_critical_level + ((100 - int_critical_level) / 3); - int_warning_level -= (int_warning_level % 10); - - /* Set the critical level value */ - hal_set_int (cs, "battery.charge_level.low", int_critical_level); - hal_set_int (cs, "battery.reporting.low", int_critical_level); - - /* Set the warning level value (FIXME: set to 50 % for now) */ - hal_set_int (cs, "battery.charge_level.warning", int_warning_level); - hal_set_int (cs, "battery.reporting.warning", int_warning_level); - - return NULL; /* Nothing to return */ -} - -/* Handle runtime exposition according to the AC status */ -static void* runtime_handler(LibHalChangeSet *cs, char* runtime) -{ - if (ac_present == 0) { - /* unSet the runtime auto computation and rely upon NUT.battery.runtime*/ - hal_set_bool (cs, "battery.remaining_time.calculate_per_time", FALSE); - - /* Set the runtime value */ - hal_set_int (cs, "battery.remaining_time", atoi(runtime)); - } - else { - /* Set the runtime auto computation */ - hal_set_bool (cs, "battery.remaining_time.calculate_per_time", TRUE); - - /* Set the runtime value */ - hal_set_int (cs, "battery.remaining_time", 0); - } - return NULL; /* Nothing to return */ -} - -/* Handle the battery technology reporting */ -static void* battery_type_handler(LibHalChangeSet *cs, char* battery_type) -{ - if (!strncmp (battery_type, "PbAc", 4)) { - hal_set_string(cs, "battery.technology", "lead-acid"); - } - /* FIXME: manage other types (lithium-ion, lithium-polymer, - * nickel-metal-hydride, unknown */ - - return NULL; /* Nothing to return */ -} - -/******************************************************************** - * dstate compatibility interface - *******************************************************************/ -void dstate_init(const char *prog, const char *port) -{ - DBusError dbus_error; - LibHalChangeSet *cs; - - dbus_error_init (&dbus_error); - - cs = libhal_device_new_changeset (udi); - if (cs == NULL) { - fatalx (EXIT_FAILURE, "Cannot initialize changeset"); - } - - /* UPS always report charge as percent */ - hal_set_string (cs, "battery.charge_level.unit", "percent"); - hal_set_string (cs, "battery.reporting.unit", "percent"); - hal_set_string (cs, "battery.alarm.unit", "percent"); - - /* Various UPSs assumptions */ - /****************************/ - /* UPS are always rechargeable! */ - /* FIXME: Check for NUT extension however: ie HID->UPS.PowerSummary.Rechargeable - * into battery.rechargeable - * or always expose it? - */ - hal_set_bool (cs, "battery.is_rechargeable", TRUE); - - /* UPS always has a max battery charge of 100 % */ - hal_set_int (cs, "battery.charge_level.design", 100); - hal_set_int (cs, "battery.charge_level.last_full", 100); - hal_set_int (cs, "battery.reporting.design", 100); - hal_set_int (cs, "battery.reporting.last_full", 100); - - /* NUT always express Voltage in Volts "V" */ - /* But not all UPSs provide the data - * battery.voltage.{design,current} */ - hal_set_string (cs, "battery.voltage.unit", "V"); - - /* UPS always have a battery! */ - /* Note(AQU): wrong with some solar panel usage, where the UPS */ - /* is just an energy source switch! */ - /* FIXME: to be processed (need possible NUT extension) */ - hal_set_bool (cs, "battery.present", TRUE); - - /* FIXME: can be improved?! (implies "info.recall.vendor") */ - hal_set_bool (cs, "info.is_recalled", FALSE); - - /* Set generic properties */ - hal_set_string (cs, "battery.type", "ups"); - libhal_device_add_capability (halctx, udi, "battery", &dbus_error); - libhal_device_add_capability (halctx, udi, "ac_adaptor", &dbus_error); - - /* FIXME: can be improved?! Set granularity (1 %)*/ - hal_set_int (cs, "battery.charge_level.granularity_1", 1); - hal_set_int (cs, "battery.charge_level.granularity_2", 1); - hal_set_int (cs, "battery.reporting.granularity_1", 1); - hal_set_int (cs, "battery.reporting.granularity_2", 1); - - dbus_error_init (&dbus_error); - /* NOTE: commit_changeset won't do IPC if set is empty */ - libhal_device_commit_changeset (halctx, cs, &dbus_error); - libhal_device_free_changeset (cs); - - if (dbus_error_is_set (&dbus_error)) - dbus_error_free (&dbus_error); -} - - -const char *dstate_getinfo(const char *var) -{ - info_lkp_t *nut2hal_info = find_nut_info(var, NULL); - -/* FIXME: use the data exposed by NUT on the DBus when available */ - - if (nut2hal_info != NULL) { - - switch (nut2hal_info->hal_type) - { -/* case HAL_TYPE_INT: - static char value[8]; - snprintf(value, sizeof(value), "%i", hal_get_int(nut2hal_info->hal_name)); - return value; - case HAL_TYPE_BOOL: - hal_set_bool(cs, nut2hal_info->hal_name, TRUE); - break; -*/ - case HAL_TYPE_STRING: - return hal_get_string(nut2hal_info->hal_name); - } - } - return NULL; -} - -int dstate_setinfo(const char *var, const char *fmt, ...) -{ - va_list ap; - int ret = 1; - LibHalChangeSet *cs; - DBusError dbus_error; - char value[ST_MAX_VALUE_LEN]; - info_lkp_t *nut2hal_info = NULL, *prev_nut2hal_info = NULL; - - va_start(ap, fmt); - vsnprintf(value, sizeof(value), fmt, ap); - va_end(ap); - - cs = libhal_device_new_changeset (udi); - if (cs == NULL) { - fatalx (EXIT_FAILURE, "Cannot initialize changeset"); - } - - /* Loop on getting HAL variable(s) matching this NUT variable */ - while ( (nut2hal_info = find_nut_info(var, prev_nut2hal_info)) != NULL) - { - upsdebugx(2, "dstate_setinfo: %s => %s (%s)\n", var, nut2hal_info->hal_name, value); - - if (nut2hal_info->fun != NULL) - nut2hal_info->fun(cs, value); - else { - switch (nut2hal_info->hal_type) - { - case HAL_TYPE_INT: - hal_set_int(cs, nut2hal_info->hal_name, atoi(value)); - break; - case HAL_TYPE_BOOL: - /* FIXME: howto lookup TRUE/FALSE? */ - hal_set_bool(cs, nut2hal_info->hal_name, TRUE); - break; - case HAL_TYPE_STRING: - hal_set_string(cs, nut2hal_info->hal_name, value); - break; - } - } - prev_nut2hal_info = nut2hal_info; - } - - dbus_error_init (&dbus_error); - /* NOTE: commit_changeset won't do IPC if set is empty */ - libhal_device_commit_changeset(halctx,cs,&dbus_error); - libhal_device_free_changeset (cs); - - if (dbus_error_is_set (&dbus_error)) - dbus_error_free (&dbus_error); - - return ret; -} - - -int dstate_addenum(const char *var, const char *fmt, ...) -{ - return 0; -} - -int dstate_delinfo(const char *var) -{ - return 0; -} - -int dstate_delcmd(const char *var) -{ - return 0; -} - -void dstate_setflags(const char *var, int flags) -{ - return; -} - -void dstate_setaux(const char *var, int aux) -{ - return; -} - -void dstate_dataok(void) -{ - return; -} - -void dstate_datastale(void) -{ - return; -} - -void dstate_free(void) -{ - return; -} - -/* extrafd: provided for waking up based on the driver's UPS fd */ -int dstate_poll_fds(struct timeval timeout, int extrafd) -{ - /* drivers expect us to limit the polling rate here */ - sleep(timeout.tv_sec); - - /* the timeout expired */ - return 1; -} - -/* clean out the temp space for a new pass */ -void status_init(void) -{ - /* Nothing to do */ - return; -} - -/* ups.status element conversion */ -void status_set(const char *buf) -{ - upsdebugx(2, "status_set: %s", buf); - - /* Note: only usbhid-ups supported devices expose [DIS]CHRG status */ - /* along with the standard OL (online) / OB (on battery) status! */ - if ( (strcmp(buf, "DISCHRG") == 0) || (strcmp(buf, "OB") == 0) ) - { - ac_present = 0; - } - else if ( (strcmp(buf, "CHRG") == 0) || (strcmp(buf, "OL") == 0) ) - { - ac_present = 1; - } - else - upsdebugx(2, "status_set: dropping status %s (not managed)\n", buf); - - return; -} - -/* write the status_buf into the externally visible dstate storage */ -void status_commit(void) -{ - LibHalChangeSet *cs; - DBusError dbus_error; - int curlevel, warnlevel, lowlevel; - - upsdebugx(2, "status_commit"); - - cs = libhal_device_new_changeset (udi); - if (cs == NULL) { - fatalx (EXIT_FAILURE, "Cannot initialize changeset"); - } - - /* Retrieve the levels */ - curlevel = hal_get_int ("battery.charge_level.current"); - warnlevel = hal_get_int ("battery.charge_level.warning"); - lowlevel = hal_get_int ("battery.charge_level.low"); - - /* Set AC present status */ - /* Note: UPSs are also AC adaptors! */ - hal_set_bool (cs, "ac_adaptor.present", (ac_present == 0)?FALSE:TRUE); - - /* Set discharging status */ - hal_set_bool (cs, "battery.rechargeable.is_discharging", (ac_present == 0)?TRUE:FALSE); - - /* Set charging status */ - if (curlevel != 100) - hal_set_bool (cs, "battery.rechargeable.is_charging", (ac_present == 0)?FALSE:TRUE); - - /* Set the battery status (FIXME: are these values valid?) */ - if (curlevel <= lowlevel) - hal_set_string(cs, "battery.charge_level.capacity_state", "critical"); - else if (curlevel <= warnlevel) - hal_set_string(cs, "battery.charge_level.capacity_state", "warning"); /*low?*/ - else - hal_set_string(cs, "battery.charge_level.capacity_state", "ok"); - - dbus_error_init (&dbus_error); - /* NOTE: commit_changeset won't do IPC if set is empty */ - libhal_device_commit_changeset (halctx, cs, &dbus_error); - libhal_device_free_changeset (cs); - - if (dbus_error_is_set (&dbus_error)) - dbus_error_free (&dbus_error); - - return; -} - -/* similar functions for ups.alarm */ -void alarm_init(void) -{ - return; -} - -void alarm_set(const char *buf) -{ - return; -} - -void alarm_commit(void) -{ - return; -} - -/* FIXME: complete and use nut2hal_cmd */ -/* Register DBus methods, by feeling the methods buffer */ -void dstate_addcmd(const char *cmdname) -{ - DBusError dbus_error; - dbus_error_init (&dbus_error); - - upsdebugx(2, "dstate_addcmd: %s\n", cmdname); - - /* beeper.{on,off} */ - /* FIXME: how to deal with beeper.toggle */ - if (!strncasecmp(cmdname, "beeper.disable", 14)) - { - strcat(dbus_methods_introspection, - " \n" - " \n" - " \n" - " \n"); - } -} - - - -/******************************************************************* - * internal functions - *******************************************************************/ - -/**************** - * HAL wrappers * - ****************/ -/* Only update HAL string values if there are real changes */ -void hal_set_string(LibHalChangeSet *cs, const char *key, const char *value) -{ - DBusError dbus_error; - char *new_value = NULL; - - upsdebugx(2, "hal_set_string: %s => %s", key, value); - - dbus_error_init(&dbus_error); - - /* Check if the property already exists */ - if (libhal_device_property_exists (halctx, udi, key, &dbus_error) == TRUE) { - - new_value = libhal_device_get_property_string (halctx, udi, - key, &dbus_error); - - /* Check if the value has really changed */ - if (strcmp(value, new_value)) - libhal_changeset_set_property_string (cs, key, value); - - /* Free the new_value string */ - if (new_value != NULL) - libhal_free_string (new_value); - } - else { - libhal_changeset_set_property_string (cs, key, value); - } -} - -/* Only update HAL int values if there are real changes */ -void hal_set_int(LibHalChangeSet *cs, const char *key, const int value) -{ - DBusError dbus_error; - int new_value; - - upsdebugx(2, "hal_set_int: %s => %i", key, value); - - dbus_error_init(&dbus_error); - - /* Check if the property already exists */ - if (libhal_device_property_exists (halctx, udi, key, &dbus_error) == TRUE) { - - new_value = libhal_device_get_property_int (halctx, udi, - key, &dbus_error); - - /* Check if the value has really changed */ - if (value != new_value) - libhal_changeset_set_property_int (cs, key, value); - } - else { - libhal_changeset_set_property_int (cs, key, value); - } -} - -char *hal_get_string(const char *key) -{ - DBusError dbus_error; - char *value = NULL; - - upsdebugx(2, "hal_get_string: %s", key); - - dbus_error_init(&dbus_error); - - /* Check if the property already exists */ - if (libhal_device_property_exists (halctx, udi, key, &dbus_error) == TRUE) { - - value = libhal_device_get_property_string (halctx, udi, - key, &dbus_error); - } - return value; -} - -int hal_get_int(const char *key) -{ - DBusError dbus_error; - int value = -1; - - upsdebugx(2, "hal_get_int: %s", key); - - dbus_error_init(&dbus_error); - - /* Check if the property already exists */ - if (libhal_device_property_exists (halctx, udi, key, &dbus_error) == TRUE) { - - value = libhal_device_get_property_int (halctx, udi, - key, &dbus_error); - } - return value; -} - -/* Only update HAL int values if there are real changes */ -void hal_set_bool(LibHalChangeSet *cs, const char *key, const dbus_bool_t value) -{ - DBusError dbus_error; - dbus_bool_t new_value; - - upsdebugx(2, "hal_set_bool: %s => %s", key, (value==TRUE)?"true":"false"); - - dbus_error_init(&dbus_error); - - /* Check if the property already exists */ - if (libhal_device_property_exists (halctx, udi, key, &dbus_error) == TRUE) { - - new_value = libhal_device_get_property_bool (halctx, udi, - key, &dbus_error); - - /* Check if the value has really changed */ - if (value != new_value) - libhal_changeset_set_property_bool (cs, key, value); - } - else { - libhal_changeset_set_property_bool (cs, key, value); - } -} - -/* find the next info element definition in info array - * that matches nut_varname, and that is after prev_info_item - * if specified. - * Note that 1 nut item can matches several HAL items - */ -static info_lkp_t *find_nut_info(const char *nut_varname, info_lkp_t *prev_info_item) -{ - info_lkp_t *info_item; - - upsdebugx(2, "find_nut_info: looking up => %s\n", nut_varname); - - if (prev_info_item != NULL) { - /* Start from the item following prev_info_item */ - info_item = ++prev_info_item; - } - else { - info_item = nut2hal_info; - } - - for ( ; info_item != NULL && info_item->nut_name != NULL ; info_item++) { - - if (!strcasecmp(info_item->nut_name, nut_varname)) - return info_item; - } - - return NULL; -} diff --git a/drivers/dstate-hal.h b/drivers/dstate-hal.h deleted file mode 100644 index 06750ed..0000000 --- a/drivers/dstate-hal.h +++ /dev/null @@ -1,93 +0,0 @@ -/* dstate-hal.h - Network UPS Tools driver-side state management - - Copyright (C) 2006 Arnaud Quette - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef DSTATE_HAL_H_SEEN -#define DSTATE_HAL_H_SEEN 1 - -#include "state.h" -#include "attribute.h" - -/*#include "parseconf.h"*/ -#include "upshandler.h" - -#include -#include -#include -#include - -#define DS_LISTEN_BACKLOG 16 -#define DS_MAX_READ 256 /* don't read forever from upsd */ - -/* HAL specific */ -#define DBUS_INTERFACE "org.freedesktop.Hal.Device.UPS" - -DBusHandlerResult dbus_filter_function(DBusConnection *connection, - DBusMessage *message, - void *user_data); - -gboolean dbus_init_local (void); - -#define HAL_WARNING - -/* track client connections */ -/* typedef struct conn_s { - * int fd; - * PCONF_CTX_t ctx; - * struct conn_s *next; - *} conn_t; - */ - extern struct ups_handler upsh; - -void dstate_init(const char *prog, const char *port); -int dstate_poll_fds(struct timeval timeout, int extrafd); -int dstate_setinfo(const char *var, const char *fmt, ...) - __attribute__ ((__format__ (__printf__, 2, 3))); -int dstate_addenum(const char *var, const char *fmt, ...) - __attribute__ ((__format__ (__printf__, 2, 3))); -void dstate_setflags(const char *var, int flags); -void dstate_setaux(const char *var, int aux); -const char *dstate_getinfo(const char *var); -void dstate_addcmd(const char *cmdname); -int dstate_delinfo(const char *var); -int dstate_delenum(const char *var, const char *val); -int dstate_delcmd(const char *cmd); -void dstate_free(void); -const st_tree_t *dstate_getroot(void); -const cmdlist_t *dstate_getcmdlist(void); - -void dstate_dataok(void); -void dstate_datastale(void); - -int dstate_is_stale(void); - -/* clean out the temp space for a new pass */ -void status_init(void); - -/* add a status element */ -void status_set(const char *buf); - -/* write the temporary status_buf into ups.status */ -void status_commit(void); - -/* similar functions for ups.alarm */ -void alarm_init(void); -void alarm_set(const char *buf); -void alarm_commit(void); - -#endif /* DSTATE_HAL_H_SEEN */ diff --git a/drivers/dstate.c b/drivers/dstate.c index 316c54f..b37e889 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -1,8 +1,9 @@ /* dstate.c - Network UPS Tools driver-side state management Copyright (C) - 2003 Russell Kroll - 2008 Arjen de Korte + 2003 Russell Kroll + 2008 Arjen de Korte + 2012 - 2017 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,6 +20,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" /* must be the first header */ + #include #include #include @@ -31,6 +34,8 @@ #include "dstate.h" #include "state.h" #include "parseconf.h" +#include "attribute.h" +#include "nut_stdint.h" static int sockfd = -1, stale = 1, alarm_active = 0, ignorelb = 0; static char *sockfn = NULL; @@ -42,6 +47,9 @@ struct ups_handler upsh; /* this may be a frequent stumbling point for new users, so be verbose here */ +static void sock_fail(const char *fn) + __attribute__((noreturn)); + static void sock_fail(const char *fn) { int sockerr; @@ -69,7 +77,7 @@ static void sock_fail(const char *fn) user->pw_name, (int)user->pw_uid); printf("Things to try:\n\n"); - printf(" - set different owners or permissions on %s\n\n", + printf(" - set different owners or permissions on %s\n\n", dflt_statepath()); printf(" - run this as some other user " "(try -u )\n"); @@ -86,9 +94,9 @@ static void sock_fail(const char *fn) printf(" - mkdir %s\n", dflt_statepath()); break; } - + /* - * there - that wasn't so bad. every helpful line of code here + * there - that wasn't so bad. every helpful line of code here * prevents one more "help me" mail to the list a year from now */ @@ -101,6 +109,8 @@ static int sock_open(const char *fn) int ret, fd; struct sockaddr_un ssaddr; + check_unix_socket_filename(fn); + fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) { @@ -162,13 +172,26 @@ static void sock_disconnect(conn_t *conn) static void send_to_all(const char *fmt, ...) { - int ret; + ssize_t ret; char buf[ST_SOCK_BUF_LEN]; + size_t buflen; va_list ap; conn_t *conn, *cnext; va_start(ap, fmt); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif ret = vsnprintf(buf, sizeof(buf), fmt, ap); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif va_end(ap); if (ret < 1) { @@ -176,45 +199,132 @@ static void send_to_all(const char *fmt, ...) return; } - upsdebugx(5, "%s: %.*s", __func__, ret-1, buf); + if (ret <= INT_MAX) + upsdebugx(5, "%s: %.*s", __func__, (int)(ret-1), buf); + + buflen = strlen(buf); + if (buflen >= SSIZE_MAX) { + /* Can't compare buflen to ret... though should not happen with ST_SOCK_BUF_LEN */ + upslog_with_errno(LOG_NOTICE, "%s failed: buffered message too large", __func__); + return; + } for (conn = connhead; conn; conn = cnext) { cnext = conn->next; - ret = write(conn->fd, buf, strlen(buf)); + ret = write(conn->fd, buf, buflen); - if (ret != (int)strlen(buf)) { - upsdebugx(2, "write %d bytes to socket %d failed", (int)strlen(buf), conn->fd); + if ((ret < 1) || (ret != (ssize_t)buflen)) { + upsdebugx(0, "WARNING: %s: write %zd bytes to " + "socket %d failed (ret=%zd), disconnecting: %s", + __func__, buflen, conn->fd, ret, strerror(errno)); + upsdebugx(6, "failed write: %s", buf); sock_disconnect(conn); + + /* TOTHINK: Maybe fallback elsewhere in other cases? */ + if (ret < 0 && errno == EAGAIN && do_synchronous == -1) { + upsdebugx(0, "%s: synchronous mode was 'auto', " + "will try 'on' for next connections", + __func__); + do_synchronous = 1; + } + + dstate_setinfo("driver.parameter.synchronous", "%s", + (do_synchronous==1)?"yes":((do_synchronous==0)?"no":"auto")); + } else { + upsdebugx(6, "%s: write %zd bytes to socket %d succeeded " + "(ret=%zd): %s", + __func__, buflen, conn->fd, ret, buf); } } } static int send_to_one(conn_t *conn, const char *fmt, ...) { - int ret; + ssize_t ret; va_list ap; char buf[ST_SOCK_BUF_LEN]; + size_t buflen; va_start(ap, fmt); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif ret = vsnprintf(buf, sizeof(buf), fmt, ap); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif va_end(ap); + upsdebugx(2, "%s: sending %.*s", __func__, (int)strcspn(buf, "\n"), buf); if (ret < 1) { upsdebugx(2, "%s: nothing to write", __func__); return 1; } - upsdebugx(5, "%s: %.*s", __func__, ret-1, buf); - - ret = write(conn->fd, buf, strlen(buf)); - - if (ret != (int)strlen(buf)) { - upsdebugx(2, "write %d bytes to socket %d failed", (int)strlen(buf), conn->fd); - sock_disconnect(conn); + buflen = strlen(buf); + if (buflen >= SSIZE_MAX) { + /* Can't compare buflen to ret... though should not happen with ST_SOCK_BUF_LEN */ + upslog_with_errno(LOG_NOTICE, "%s failed: buffered message too large", __func__); return 0; /* failed */ } + if (ret <= INT_MAX) + upsdebugx(5, "%s: %.*s", __func__, (int)(ret-1), buf); + +/* + upsdebugx(0, "%s: writing %zd bytes to socket %d: %s", + __func__, buflen, conn->fd, buf); +*/ + + ret = write(conn->fd, buf, buflen); + + if (ret < 0) { + /* Hacky bugfix: throttle down for upsd to read that */ + upsdebugx(1, "%s: had to throttle down to retry " + "writing %zd bytes to socket %d " + "(ret=%zd, errno=%d, strerror=%s): %s", + __func__, buflen, conn->fd, + ret, errno, strerror(errno), + buf); + usleep(200); + ret = write(conn->fd, buf, buflen); + if (ret == (ssize_t)buflen) { + upsdebugx(1, "%s: throttling down helped", __func__); + } + } + + if ((ret < 1) || (ret != (ssize_t)buflen)) { + upsdebugx(0, "WARNING: %s: write %zd bytes to " + "socket %d failed (ret=%zd), disconnecting: %s", + __func__, buflen, conn->fd, ret, strerror(errno)); + upsdebugx(6, "failed write: %s", buf); + sock_disconnect(conn); + + /* TOTHINK: Maybe fallback elsewhere in other cases? */ + if (ret < 0 && errno == EAGAIN && do_synchronous == -1) { + upsdebugx(0, "%s: synchronous mode was 'auto', " + "will try 'on' for next connections", + __func__); + do_synchronous = 1; + } + + dstate_setinfo("driver.parameter.synchronous", "%s", + (do_synchronous==1)?"yes":((do_synchronous==0)?"no":"auto")); + + return 0; /* failed */ + } else { + upsdebugx(6, "%s: write %zd bytes to socket %d succeeded " + "(ret=%zd): %s", + __func__, buflen, conn->fd, ret, buf); + } + return 1; /* OK */ } @@ -223,7 +333,7 @@ static void sock_connect(int sock) int fd, ret; conn_t *conn; struct sockaddr_un sa; -#if defined(__hpux) && !defined(_XOPEN_SOURCE_EXTENDED) +#if defined(__hpux) && !defined(_XOPEN_SOURCE_EXTENDED) int salen; #else socklen_t salen; @@ -236,23 +346,34 @@ static void sock_connect(int sock) return; } - /* enable nonblocking I/O */ + /* enable nonblocking I/O? + * -1 = auto (try async, allow fallback to sync) + * 0 = async + * 1 = sync + */ + if (do_synchronous < 1) { + upsdebugx(0, "%s: enabling asynchronous mode (%s)", + __func__, (do_synchronous<0)?"auto":"fixed"); - ret = fcntl(fd, F_GETFL, 0); + ret = fcntl(fd, F_GETFL, 0); - if (ret < 0) { - upslog_with_errno(LOG_ERR, "fcntl get on unix fd failed"); - close(fd); - return; + if (ret < 0) { + upslog_with_errno(LOG_ERR, "fcntl get on unix fd failed"); + close(fd); + return; + } + + ret = fcntl(fd, F_SETFL, ret | O_NDELAY); + + if (ret < 0) { + upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on unix fd failed"); + close(fd); + return; + } + } + else { + upsdebugx(0, "%s: keeping default synchronous mode", __func__); } - - ret = fcntl(fd, F_SETFL, ret | O_NDELAY); - - if (ret < 0) { - upslog_with_errno(LOG_ERR, "fcntl set O_NDELAY on unix fd failed"); - close(fd); - return; - } conn = xcalloc(1, sizeof(*conn)); conn->fd = fd; @@ -273,6 +394,7 @@ static int st_tree_dump_conn(st_tree_t *node, conn_t *conn) { int ret; enum_t *etmp; + range_t *rtmp; if (!node) { return 1; /* not an error */ @@ -297,9 +419,16 @@ static int st_tree_dump_conn(st_tree_t *node, conn_t *conn) } } + /* send any ranges */ + for (rtmp = node->range_list; rtmp; rtmp = rtmp->next) { + if (!send_to_one(conn, "ADDRANGE %s %i %i\n", node->var, rtmp->min, rtmp->max)) { + return 0; + } + } + /* provide any auxiliary data */ if (node->aux) { - if (!send_to_one(conn, "SETAUX %s %d\n", node->var, node->aux)) { + if (!send_to_one(conn, "SETAUX %s %ld\n", node->var, node->aux)) { return 0; } } @@ -317,8 +446,13 @@ static int st_tree_dump_conn(st_tree_t *node, conn_t *conn) if (node->flags & ST_FLAG_STRING) { snprintfcat(flist, sizeof(flist), " STRING"); } + if (node->flags & ST_FLAG_NUMBER) { + snprintfcat(flist, sizeof(flist), " NUMBER"); + } - send_to_one(conn, "SETFLAGS %s\n", flist); + if (!send_to_one(conn, "SETFLAGS %s\n", flist)) { + return 0; + } } if (node->right) { @@ -341,8 +475,17 @@ static int cmd_dump_conn(conn_t *conn) return 1; } -static int sock_arg(conn_t *conn, int numarg, char **arg) + +static void send_tracking(conn_t *conn, const char *id, int value) { + send_to_one(conn, "TRACKING %s %i\n", id, value); +} + +static int sock_arg(conn_t *conn, size_t numarg, char **arg) +{ + upsdebugx(6, "Driver on %s is now handling %s with %zu args", + sockfn, numarg ? arg[0] : "", numarg); + if (numarg < 1) { return 0; } @@ -379,20 +522,40 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) return 0; } - /* INSTCMD []*/ + /* INSTCMD [] [TRACKING ] */ if (!strcasecmp(arg[0], "INSTCMD")) { + int ret; + char *cmdname = arg[1]; + char *cmdparam = NULL; + char *cmdid = NULL; + + /* Check if and TRACKING were provided */ + if (numarg == 3) { + cmdparam = arg[2]; + } else if (numarg == 4 && !strcasecmp(arg[2], "TRACKING")) { + cmdid = arg[3]; + } else if (numarg == 5 && !strcasecmp(arg[3], "TRACKING")) { + cmdparam = arg[2]; + cmdid = arg[4]; + } else if (numarg != 2) { + upslogx(LOG_NOTICE, "Malformed INSTCMD request"); + return 0; + } + + if (cmdid) + upsdebugx(3, "%s: TRACKING = %s", __func__, cmdid); /* try the new handler first if present */ if (upsh.instcmd) { - if (numarg > 2) { - upsh.instcmd(arg[1], arg[2]); - return 1; - } + ret = upsh.instcmd(cmdname, cmdparam); - upsh.instcmd(arg[1], NULL); + /* send back execution result */ + if (cmdid) + send_tracking(conn, cmdid, ret); + + /* The command was handled, status is a separate consideration */ return 1; } - upslogx(LOG_NOTICE, "Got INSTCMD, but driver lacks a handler"); return 1; } @@ -401,12 +564,33 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) return 0; } - /* SET */ + /* SET [TRACKING ] */ if (!strcasecmp(arg[0], "SET")) { + int ret; + char *setid = NULL; + + /* Check if TRACKING was provided */ + if (numarg == 5) { + if (!strcasecmp(arg[3], "TRACKING")) { + setid = arg[4]; + } + else { + upslogx(LOG_NOTICE, "Got SET with unsupported parameters (%s/%s)", + arg[3], arg[4]); + return 0; + } + upsdebugx(3, "%s: TRACKING = %s", __func__, setid); + } /* try the new handler first if present */ if (upsh.setvar) { - upsh.setvar(arg[1], arg[2]); + ret = upsh.setvar(arg[1], arg[2]); + + /* send back execution result */ + if (setid) + send_tracking(conn, setid, ret); + + /* The command was handled, status is a separate consideration */ return 1; } @@ -420,7 +604,7 @@ static int sock_arg(conn_t *conn, int numarg, char **arg) static void sock_read(conn_t *conn) { - int i, ret; + ssize_t ret, i; char buf[SMALLBUF]; ret = read(conn->fd, buf, sizeof(buf)); @@ -451,7 +635,7 @@ static void sock_read(conn_t *conn) upslogx(LOG_INFO, "Unknown command on socket: "); - for (arg = 0; arg < conn->ctx.numargs; arg++) { + for (arg = 0; arg < conn->ctx.numargs && arg < INT_MAX; arg++) { upslogx(LOG_INFO, "arg %d: %s", (int)arg, conn->ctx.arglist[arg]); } } @@ -490,12 +674,19 @@ static void sock_close(void) /* interface */ -void dstate_init(const char *prog, const char *devname) +char * dstate_init(const char *prog, const char *devname) { char sockname[SMALLBUF]; /* do this here for now */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif signal(SIGPIPE, SIG_IGN); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic pop +#endif if (devname) { snprintf(sockname, sizeof(sockname), "%s/%s-%s", dflt_statepath(), prog, devname); @@ -506,6 +697,9 @@ void dstate_init(const char *prog, const char *devname) sockfd = sock_open(sockname); upsdebugx(2, "dstate_init: sock %s open on fd %d", sockname, sockfd); + + /* NOTE: Caller must free this string */ + return xstrdup(sockname); } /* returns 1 if timeout expired or data is available on UPS fd, 0 otherwise */ @@ -553,7 +747,7 @@ int dstate_poll_fds(struct timeval timeout, int extrafd) timeout.tv_sec -= now.tv_sec; timeout.tv_usec -= now.tv_usec; } - + ret = select(maxfd + 1, &rfds, NULL, NULL, &timeout); if (ret == 0) { @@ -602,7 +796,19 @@ int dstate_setinfo(const char *var, const char *fmt, ...) va_list ap; va_start(ap, fmt); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif vsnprintf(value, sizeof(value), fmt, ap); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif va_end(ap); ret = state_setinfo(&dtree_root, var, value); @@ -621,7 +827,19 @@ int dstate_addenum(const char *var, const char *fmt, ...) va_list ap; va_start(ap, fmt); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif vsnprintf(value, sizeof(value), fmt, ap); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif va_end(ap); ret = state_addenum(dtree_root, var, value); @@ -633,6 +851,21 @@ int dstate_addenum(const char *var, const char *fmt, ...) return ret; } +int dstate_addrange(const char *var, const int min, const int max) +{ + int ret; + + ret = state_addrange(dtree_root, var, min, max); + + if (ret == 1) { + send_to_all("ADDRANGE %s %i %i\n", var, min, max); + /* Also add the "NUMBER" flag for ranges */ + dstate_addflags(var, ST_FLAG_NUMBER); + } + + return ret; +} + void dstate_setflags(const char *var, int flags) { st_tree_t *sttmp; @@ -668,11 +901,51 @@ void dstate_setflags(const char *var, int flags) snprintfcat(flist, sizeof(flist), " STRING"); } + if (flags & ST_FLAG_NUMBER) { + snprintfcat(flist, sizeof(flist), " NUMBER"); + } + /* update listeners */ send_to_all("SETFLAGS %s\n", flist); } -void dstate_setaux(const char *var, int aux) +void dstate_addflags(const char *var, const int addflags) +{ + int flags = state_getflags(dtree_root, var); + + if (flags == -1) { + upslogx(LOG_ERR, "%s: cannot get flags of '%s'", __func__, var); + return; + } + + /* Already set */ + if ((flags & addflags) == addflags) + return; + + flags |= addflags; + + dstate_setflags(var, flags); +} + +void dstate_delflags(const char *var, const int delflags) +{ + int flags = state_getflags(dtree_root, var); + + if (flags == -1) { + upslogx(LOG_ERR, "%s: cannot get flags of '%s'", __func__, var); + return; + } + + /* Already not set */ + if (!(flags & delflags)) + return; + + flags &= ~delflags; + + dstate_setflags(var, flags); +} + +void dstate_setaux(const char *var, long aux) { st_tree_t *sttmp; @@ -691,7 +964,7 @@ void dstate_setaux(const char *var, int aux) sttmp->aux = aux; /* update listeners */ - send_to_all("SETAUX %s %d\n", var, aux); + send_to_all("SETAUX %s %ld\n", var, aux); } const char *dstate_getinfo(const char *var) @@ -739,6 +1012,20 @@ int dstate_delenum(const char *var, const char *val) return ret; } +int dstate_delrange(const char *var, const int min, const int max) +{ + int ret; + + ret = state_delrange(dtree_root, var, min, max); + + /* update listeners */ + if (ret == 1) { + send_to_all("DELRANGE %s %i %i\n", var, min, max); + } + + return ret; +} + int dstate_delcmd(const char *cmd) { int ret; @@ -757,7 +1044,7 @@ void dstate_free(void) { state_infofree(dtree_root); dtree_root = NULL; - + state_cmdfree(cmdhead); cmdhead = NULL; @@ -862,17 +1149,111 @@ void status_commit(void) void alarm_init(void) { - memset(alarm_buf, 0, sizeof(alarm_buf)); + /* reinit global counter */ + alarm_active = 0; + + device_alarm_init(); } +#if (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#if (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif void alarm_set(const char *buf) { + int ret; if (strlen(alarm_buf) > 0) { - snprintfcat(alarm_buf, sizeof(alarm_buf), " %s", buf); + ret = snprintfcat(alarm_buf, sizeof(alarm_buf), " %s", buf); } else { - snprintfcat(alarm_buf, sizeof(alarm_buf), "%s", buf); + ret = snprintfcat(alarm_buf, sizeof(alarm_buf), "%s", buf); + } + + if (ret < 0) { + /* Should we also try to print the potentially unusable buf? + * Generally - likely not. But if it is short enough... + * Note: LARGEBUF was the original limit mismatched vs alarm_buf + * size before PR #986. + */ + char alarm_tmp[LARGEBUF]; + memset(alarm_tmp, 0, sizeof(alarm_tmp)); + /* A bit of complexity to keep both (int)snprintf(...) and (size_t)sizeof(...) happy */ + int ibuflen = snprintf(alarm_tmp, sizeof(alarm_tmp), "%s", buf); + size_t buflen; + if (ibuflen < 0) { + alarm_tmp[0] = 'N'; + alarm_tmp[1] = '/'; + alarm_tmp[2] = 'A'; + alarm_tmp[3] = '\0'; + buflen = strlen(alarm_tmp); + } else { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +/* Note for gating macros above: unsuffixed HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP + * means support of contexts both inside and outside function body, so the push + * above and pop below (outside this finction) are not used. + */ +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +/* Note that the individual warning pragmas for use inside function bodies + * are named without a _INSIDEFUNC suffix, for simplicity and legacy reasons + */ +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if ((unsigned long long int)ibuflen < SIZE_MAX) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + buflen = (size_t)ibuflen; + } else { + buflen = SIZE_MAX; + } + } + upslogx(LOG_ERR, "%s: error setting alarm_buf to: %s%s", + __func__, alarm_tmp, ( (buflen < sizeof(alarm_tmp)) ? "" : "..." ) ); + } else if ((size_t)ret > sizeof(alarm_buf)) { + char alarm_tmp[LARGEBUF]; + memset(alarm_tmp, 0, sizeof(alarm_tmp)); + int ibuflen = snprintf(alarm_tmp, sizeof(alarm_tmp), "%s", buf); + size_t buflen; + if (ibuflen < 0) { + alarm_tmp[0] = 'N'; + alarm_tmp[1] = '/'; + alarm_tmp[2] = 'A'; + alarm_tmp[3] = '\0'; + buflen = strlen(alarm_tmp); + } else { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if ((unsigned long long int)ibuflen < SIZE_MAX) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + buflen = (size_t)ibuflen; + } else { + buflen = SIZE_MAX; + } + } + upslogx(LOG_WARNING, "%s: result was truncated while setting or appending " + "alarm_buf (limited to %zu bytes), with message: %s%s", + __func__, sizeof(alarm_buf), alarm_tmp, + ( (buflen < sizeof(alarm_tmp)) ? "" : "..." )); } } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) ) +# pragma GCC diagnostic pop +#endif /* write the status_buf into the info array */ void alarm_commit(void) @@ -885,3 +1266,259 @@ void alarm_commit(void) alarm_active = 0; } } + +void device_alarm_init(void) +{ + /* only clear the buffer, don't touch the alarms counter */ + memset(alarm_buf, 0, sizeof(alarm_buf)); +} + +/* same as above, but writes to "device.X.ups.alarm" or "ups.alarm" */ +void device_alarm_commit(const int device_number) +{ + char info_name[20]; + + memset(info_name, 0, 20); + + if (device_number != 0) /* would then go into "device.%i.alarm" */ + snprintf(info_name, 20, "device.%i.ups.alarm", device_number); + else /* would then go into "device.alarm" */ + snprintf(info_name, 20, "ups.alarm"); + + /* Daisychain subdevices note: + * increase the counter when alarms are present on a subdevice, but + * don't decrease the count. Otherwise, we may not get the ALARM flag + * in ups.status, while there are some alarms present on device.X */ + if (strlen(alarm_buf) > 0) { + dstate_setinfo(info_name, "%s", alarm_buf); + alarm_active++; + } else { + dstate_delinfo(info_name); + } +} + +/* For devices where we do not have phase-count info (no mapping provided + * in the tables), nor in the device data/protocol, we can still guesstimate + * and report a value. This routine may also replace an existing value, e.g. + * if we've found new data disproving old one (e.g. if the 3-phase UPS was + * disbalanced when the driver was started, so we thought it is 1-phase in + * practice, and then the additional lines came up loaded, hence the bools + * "may_reevaluate" and the readonly flag "may_change_dstate" (so the caller + * can query the current apparent situation, without changing any dstates). + * It is up to callers to decide if they already have data they want to keep. + * The "xput_prefix" is e.g. "input." or "input.bypass." or "output." with + * the trailing dot where applicable - we use this string verbatim below. + * The "inited_phaseinfo" and "num_phases" are addresses of caller's own + * variables to store the flag (if we have successfully inited) and the + * discovered amount of phases, or NULL if caller does not want to track it. + * + * NOTE: The code below can detect if the device is 1, 2 (split phase) or 3 + * phases. + * + * Returns: + * -1 Runtime/input error (non fatal, but routine was skipped) + * 0 Nothing changed: could not determine a value + * 1 A phase value was just determined (and set, if not read-only mode) + * 2 Nothing changed: already inited (and may_reevaluate==false) + * 3 Nothing changed: detected a value but it is already published + * as a dstate; populated inited_phaseinfo and num_phases though + */ +int dstate_detect_phasecount( + const char *xput_prefix, + const int may_change_dstate, + int *inited_phaseinfo, + int *num_phases, + const int may_reevaluate +) { + /* If caller does not want either of these back - loopback the values below */ + int local_inited_phaseinfo = 0, local_num_phases = -1; + /* Temporary local value storage */ + int old_num_phases = -1, detected_phaseinfo = 0; + + if (!inited_phaseinfo) + inited_phaseinfo = &local_inited_phaseinfo; + if (!num_phases) + num_phases = &local_num_phases; + old_num_phases = *num_phases; + + upsdebugx(3, "Entering %s('%s', %i, %i, %i, %i)", __func__, + xput_prefix, may_change_dstate, *inited_phaseinfo, *num_phases, may_reevaluate); + + if (!(*inited_phaseinfo) || may_reevaluate) { + const char *v1, *v2, *v3, *v0, + *v1n, *v2n, *v3n, + *v12, *v23, *v31, + *c1, *c2, *c3, *c0; + char buf[MAX_STRING_SIZE]; /* For concatenation of "xput_prefix" with items we want to query */ + size_t xput_prefix_len; + size_t bufrw_max; + char *bufrw_ptr = NULL; + + if (!xput_prefix) { + upsdebugx(0, "%s(): Bad xput_prefix was passed: it is NULL - function skipped", __func__); + return -1; + } + + xput_prefix_len = strlen(xput_prefix); + if (xput_prefix_len < 1) { + upsdebugx(0, "%s(): Bad xput_prefix was passed: it is empty - function skipped", __func__); + return -1; + } + + bufrw_max = sizeof(buf) - xput_prefix_len; + if (bufrw_max <= 15) { + /* We need to append max ~13 chars per below, so far */ + upsdebugx(0, "%s(): Bad xput_prefix was passed: it is too long - function skipped", __func__); + return -1; + } + memset(buf, 0, sizeof(buf)); + strncpy(buf, xput_prefix, sizeof(buf)); + bufrw_ptr = buf + xput_prefix_len ; + + /* We either have defined and non-zero (numeric) values below, or NULLs. + * Note that as "zero" we should expect any valid numeric representation + * of a zero value as some drivers may save strangely formatted values. + * For now, we limit the level of paranoia with missing dstate entries, + * empty entries, and actual single zero character as contents of the + * string. Other obscure cases (string of multiple zeroes, a floating + * point zero, surrounding whitespace etc. may be solved if the need + * does arise in the future. Arguably, drivers' translation/mapping + * tables should take care of this with converion routine and numeric + * data type flags. */ +#define dstate_getinfo_nonzero(var, suffix) \ + do { strncpy(bufrw_ptr, suffix, bufrw_max); \ + if ( (var = dstate_getinfo(buf)) ) { \ + if ( (var[0] == '0' && var[1] == '\0') || \ + (var[0] == '\0') ) { \ + var = NULL; \ + } \ + } \ + } while(0) + + dstate_getinfo_nonzero(v1, "L1.voltage"); + dstate_getinfo_nonzero(v2, "L2.voltage"); + dstate_getinfo_nonzero(v3, "L3.voltage"); + dstate_getinfo_nonzero(v1n, "L1-N.voltage"); + dstate_getinfo_nonzero(v2n, "L2-N.voltage"); + dstate_getinfo_nonzero(v3n, "L3-N.voltage"); + dstate_getinfo_nonzero(v1n, "L1-N.voltage"); + dstate_getinfo_nonzero(v12, "L1-L2.voltage"); + dstate_getinfo_nonzero(v23, "L2-L3.voltage"); + dstate_getinfo_nonzero(v31, "L3-L1.voltage"); + dstate_getinfo_nonzero(c1, "L1.current"); + dstate_getinfo_nonzero(c2, "L2.current"); + dstate_getinfo_nonzero(c3, "L3.current"); + dstate_getinfo_nonzero(v0, "voltage"); + dstate_getinfo_nonzero(c0, "current"); + + if ( (v1 && v2 && !v3) || + (v1n && v2n && !v3n) || + (c1 && c2 && !c3) || + (v12 && !v23 && !v31) ) { + upsdebugx(5, "%s(): determined a 2-phase case", __func__); + *num_phases = 2; + *inited_phaseinfo = 1; + detected_phaseinfo = 1; + } else if ( (v1 && v2 && v3) || + (v1n && v2n && v3n) || + (c1 && (c2 || c3)) || + (c2 && (c1 || c3)) || + (c3 && (c1 || c2)) || + v12 || v23 || v31 ) { + upsdebugx(5, "%s(): determined a 3-phase case", __func__); + *num_phases = 3; + *inited_phaseinfo = 1; + detected_phaseinfo = 1; + } else if ( /* We definitely have only one non-zero line */ + !v12 && !v23 && !v31 && ( + (c0 && !c1 && !c2 && !c3) || + (v0 && !v1 && !v2 && !v3) || + (c1 && !c2 && !c3) || + (!c1 && c2 && !c3) || + (!c1 && !c2 && c3) || + (v1 && !v2 && !v3) || + (!v1 && v2 && !v3) || + (!v1 && !v2 && v3) || + (v1n && !v2n && !v3n) || + (!v1n && v2n && !v3n) || + (!v1n && !v2n && v3n) ) ) { + *num_phases = 1; + *inited_phaseinfo = 1; + detected_phaseinfo = 1; + upsdebugx(5, "%s(): determined a 1-phase case", __func__); + } else { + upsdebugx(5, "%s(): could not determine the phase case", __func__); + } + + if (detected_phaseinfo) { + const char *oldphases; + strncpy(bufrw_ptr, "phases", bufrw_max); + oldphases = dstate_getinfo(buf); + + if (oldphases) { + if (atoi(oldphases) == *num_phases) { + /* Technically, a bit has changed: we have set the flag which may have been missing before */ + upsdebugx(5, "%s(): Nothing changed, with a valid reason; dstate already published with the same value: %s=%s (detected %d)", + __func__, buf, oldphases, *num_phases); + return 3; + } + } + + if ( (*num_phases != old_num_phases) || (!oldphases) ) { + if (may_change_dstate) { + dstate_setinfo(buf, "%d", *num_phases); + upsdebugx(3, "%s(): calculated non-XML value for NUT variable %s was set to %d", + __func__, buf, *num_phases); + } else { + upsdebugx(3, "%s(): calculated non-XML value for NUT variable %s=%d but did not set its dstate (read-only request)", + __func__, buf, *num_phases); + } + return 1; + } + } + + upsdebugx(5, "%s(): Nothing changed: could not determine a value", __func__); + return 0; + } + + upsdebugx(5, "%s(): Nothing changed, with a valid reason; already inited", __func__); + return 2; +} + +/* Dump the data tree (in upsc-like format) to stdout */ +/* Actual implementation */ +static int dstate_tree_dump(const st_tree_t *node) +{ + int ret; + + if (!node) { + return 1; /* not an error */ + } + + if (node->left) { + ret = dstate_tree_dump(node->left); + + if (!ret) { + return 0; /* write failed in the child */ + } + } + + printf("%s: %s\n", node->var, node->val); + + if (node->right) { + return dstate_tree_dump(node->right); + } + + return 1; /* everything's OK here ... */ +} + +/* Dump the data tree (in upsc-like format) to stdout */ +/* Public interface */ +void dstate_dump(void) +{ + upsdebugx(3, "Entering %s", __func__); + + const st_tree_t *node = (const st_tree_t *)dstate_getroot(); + + dstate_tree_dump(node); +} diff --git a/drivers/dstate.h b/drivers/dstate.h index 7ffe899..94829df 100644 --- a/drivers/dstate.h +++ b/drivers/dstate.h @@ -1,6 +1,8 @@ /* dstate.h - Network UPS Tools driver-side state management - Copyright (C) 2003 Russell Kroll + Copyright (C) + 2003 Russell Kroll + 2012-2017 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,6 +22,7 @@ #ifndef DSTATE_H_SEEN #define DSTATE_H_SEEN 1 +#include "timehead.h" #include "state.h" #include "attribute.h" @@ -29,6 +32,10 @@ #define DS_LISTEN_BACKLOG 16 #define DS_MAX_READ 256 /* don't read forever from upsd */ +#ifndef MAX_STRING_SIZE +#define MAX_STRING_SIZE 128 +#endif + /* track client connections */ typedef struct conn_s { int fd; @@ -39,18 +46,26 @@ typedef struct conn_s { extern struct ups_handler upsh; -void dstate_init(const char *prog, const char *devname); + /* asynchronous (nonblocking) Vs synchronous (blocking) I/O + * Defaults to nonblocking, for backward compatibility */ + extern int do_synchronous; + +char * dstate_init(const char *prog, const char *devname); int dstate_poll_fds(struct timeval timeout, int extrafd); int dstate_setinfo(const char *var, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); int dstate_addenum(const char *var, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); +int dstate_addrange(const char *var, const int min, const int max); void dstate_setflags(const char *var, int flags); -void dstate_setaux(const char *var, int aux); +void dstate_addflags(const char *var, const int addflags); +void dstate_delflags(const char *var, const int delflags); +void dstate_setaux(const char *var, long aux); const char *dstate_getinfo(const char *var); void dstate_addcmd(const char *cmdname); int dstate_delinfo(const char *var); int dstate_delenum(const char *var, const char *val); +int dstate_delrange(const char *var, const int min, const int max); int dstate_delcmd(const char *cmd); void dstate_free(void); const st_tree_t *dstate_getroot(void); @@ -74,5 +89,16 @@ void status_commit(void); void alarm_init(void); void alarm_set(const char *buf); void alarm_commit(void); +void device_alarm_init(void); +void device_alarm_commit(const int device_number); + +int dstate_detect_phasecount( + const char *xput_prefix, + const int may_change_dstate, + int *inited_phaseinfo, + int *num_phases, + const int may_reevaluate); + +void dstate_dump(void); #endif /* DSTATE_H_SEEN */ diff --git a/drivers/dummy-ups.c b/drivers/dummy-ups.c index c2e9957..c75cc1e 100644 --- a/drivers/dummy-ups.c +++ b/drivers/dummy-ups.c @@ -1,7 +1,7 @@ /* dummy-ups.c - NUT simulation and device repeater driver Copyright (C) - 2005 - 2010 Arnaud Quette + 2005 - 2015 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -29,18 +29,22 @@ * * poll the "port" file for change */ +#include "config.h" /* must be the first header */ + #include #include #include +#include #include #include "main.h" #include "parseconf.h" +#include "nut_stdint.h" #include "upsclient.h" #include "dummy-ups.h" #define DRIVER_NAME "Device simulation and repeater driver" -#define DRIVER_VERSION "0.12" +#define DRIVER_VERSION "0.15" /* driver description structure */ upsdrv_info_t upsdrv_info = @@ -52,16 +56,40 @@ upsdrv_info_t upsdrv_info = { NULL } }; -#define MODE_NONE 0 -#define MODE_DUMMY 1 /* use the embedded defintion or a definition file */ -#define MODE_REPEATER 2 /* use libupsclient to repeat an UPS */ -#define MODE_META 3 /* consolidate data from several UPSs (TBS) */ +enum drivermode { + MODE_NONE = 0, -int mode=MODE_NONE; + /* use the embedded defintion or a definition file, parsed in a + * loop again and again (often with TIMER lines to delay changes) + * Default mode for files with *.seq naming pattern + * (legacy-compatibility note: and other patterns except *.dev) + */ + MODE_DUMMY_LOOP, + + /* use the embedded defintion or a definition file, parsed once + * + * This allows to spin up a dummy device with initial readings + * and retain in memory whatever SET VAR was sent by clients later. + * This is also less stressful on system resources to run the dummy. + * + * Default mode for files with *.dev naming pattern + */ + MODE_DUMMY_ONCE, + + /* use libupsclient to repeat another UPS */ + MODE_REPEATER, + + /* consolidate data from several UPSs (TBS) */ + MODE_META +}; +typedef enum drivermode drivermode_t; + +static drivermode_t mode = MODE_NONE; /* parseconf context, for dummy mode using a file */ -PCONF_CTX_t *ctx=NULL; -time_t next_update = -1; +static PCONF_CTX_t *ctx = NULL; +static time_t next_update = -1; +static struct stat datafile_stat; #define MAX_STRING_SIZE 128 @@ -77,7 +105,7 @@ static int upsclient_update_vars(void); /* connection information */ static char *client_upsname = NULL, *hostname = NULL; static UPSCONN_t *ups = NULL; -static int port; +static uint16_t port; /* Driver functions */ @@ -87,7 +115,8 @@ void upsdrv_initinfo(void) switch (mode) { - case MODE_DUMMY: + case MODE_DUMMY_ONCE: + case MODE_DUMMY_LOOP: /* Initialise basic essential variables */ for ( item = nut_data ; item->info_type != NULL ; item++ ) { @@ -111,6 +140,7 @@ void upsdrv_initinfo(void) dstate_dataok(); break; + case MODE_META: case MODE_REPEATER: /* Obtain the target name */ @@ -139,10 +169,35 @@ void upsdrv_initinfo(void) } /* FIXME: commands and settable variable! */ break; - default: + case MODE_NONE: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: fatalx(EXIT_FAILURE, "no suitable definition found!"); - break; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif } upsh.instcmd = instcmd; @@ -157,11 +212,48 @@ void upsdrv_updateinfo(void) switch (mode) { - case MODE_DUMMY: + case MODE_DUMMY_LOOP: /* Now get user's defined variables */ if (parse_data_file(upsfd) >= 0) dstate_dataok(); break; + + case MODE_DUMMY_ONCE: + /* less stress on the sys */ + if (ctx == NULL && next_update == -1) { + struct stat fs; + char fn[SMALLBUF]; + + if (device_path[0] == '/') + snprintf(fn, sizeof(fn), "%s", device_path); + else + snprintf(fn, sizeof(fn), "%s/%s", confpath(), device_path); + + if (0 != fstat (upsfd, &fs) && 0 != stat (fn, &fs)) { + upsdebugx(2, "Can't open %s currently", fn); + /* retry ASAP until we get a file */ + memset(&datafile_stat, 0, sizeof(struct stat)); + next_update = 1; + } else { + if (datafile_stat.st_mtim.tv_sec != fs.st_mtim.tv_sec) { + upsdebugx(2, "upsdrv_updateinfo: input file was already read once to the end, but changed later - re-reading"); + /* updated file => retry ASAP */ + next_update = 1; + datafile_stat = fs; + } + } + } + + if (ctx == NULL && next_update == -1) { + upsdebugx(2, "upsdrv_updateinfo: NO-OP: input file was already read once to the end"); + dstate_dataok(); + } else { + /* initial parsing interrupted by e.g. TIMER line */ + if (parse_data_file(upsfd) >= 0) + dstate_dataok(); + } + break; + case MODE_META: case MODE_REPEATER: if (upsclient_update_vars() > 0) @@ -182,19 +274,48 @@ void upsdrv_updateinfo(void) } } break; + case MODE_NONE: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: break; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif } } +void upsdrv_shutdown(void) + __attribute__((noreturn)); + void upsdrv_shutdown(void) { fatalx(EXIT_FAILURE, "shutdown not supported"); } static int instcmd(const char *cmdname, const char *extra) -{ +{ /* if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); @@ -206,7 +327,7 @@ static int instcmd(const char *cmdname, const char *extra) * if (mode == MODE_META) => ? */ - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -216,13 +337,30 @@ void upsdrv_help(void) void upsdrv_makevartable(void) { + addvar(VAR_VALUE, "mode", "Specify mode instead of guessing it from port value (dummy = dummy-loop, dummy-once, repeater)"); /* meta */ } void upsdrv_initups(void) { + const char *val; + + val = dstate_getinfo("driver.parameter.mode"); + if (val) { + if (!strcmp(val, "dummy-loop") + && !strcmp(val, "dummy-once") + && !strcmp(val, "dummy") + && !strcmp(val, "repeater") + /* && !strcmp(val, "meta") */ + ) { + fatalx(EXIT_FAILURE, "Unsupported mode was specified: %s", val); + } + } + /* check the running mode... */ - if (strchr(device_path, '@')) - { + if ( (!val && strchr(device_path, '@')) + || (val && !strcmp(val, "repeater")) + /*|| (val && !strcmp(val, "meta")) */ + ) { upsdebugx(1, "Repeater mode"); mode = MODE_REPEATER; dstate_setinfo("driver.parameter.mode", "repeater"); @@ -230,9 +368,95 @@ void upsdrv_initups(void) } else { - upsdebugx(1, "Dummy (simulation) mode"); - mode = MODE_DUMMY; - dstate_setinfo("driver.parameter.mode", "dummy"); + char fn[SMALLBUF]; + mode = MODE_NONE; + + if (val) { + if (!strcmp(val, "dummy-loop")) { + upsdebugx(2, "Dummy (simulation) mode looping infinitely was explicitly requested"); + mode = MODE_DUMMY_LOOP; + } else + if (!strcmp(val, "dummy-once")) { + upsdebugx(2, "Dummy (simulation) mode with data read once was explicitly requested"); + mode = MODE_DUMMY_ONCE; + } else + if (!strcmp(val, "dummy")) { + upsdebugx(2, "Dummy (simulation) mode default (looping infinitely) was explicitly requested"); + mode = MODE_DUMMY_LOOP; + } + } + + if (mode == MODE_NONE) { + if (str_ends_with(device_path, ".seq")) { + upsdebugx(2, "Dummy (simulation) mode with a sequence file name pattern (looping infinitely)"); + mode = MODE_DUMMY_LOOP; + } else if (str_ends_with(device_path, ".dev")) { + upsdebugx(2, "Dummy (simulation) mode with a device data dump file name pattern (read once)"); + mode = MODE_DUMMY_ONCE; + } + } + + /* Report decisions similar to those above, + * just a bit shorter and at another level */ + switch (mode) { + case MODE_DUMMY_ONCE: + upsdebugx(1, "Dummy (simulation) mode using data read once"); + dstate_setinfo("driver.parameter.mode", "dummy-once"); + break; + + case MODE_DUMMY_LOOP: + upsdebugx(1, "Dummy (simulation) mode looping infinitely"); + dstate_setinfo("driver.parameter.mode", "dummy-loop"); + break; + + case MODE_NONE: + case MODE_REPEATER: + case MODE_META: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: + /* This was the only mode until MODE_DUMMY_LOOP + * got split from MODE_DUMMY_ONCE in NUT v2.8.0 + * so we keep the previously known mode string + * and it remains default when we are not sure + */ + upsdebugx(1, "Dummy (simulation) mode default (looping infinitely)"); + mode = MODE_DUMMY_LOOP; + dstate_setinfo("driver.parameter.mode", "dummy"); + break; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif + } + + if (device_path[0] == '/') + snprintf(fn, sizeof(fn), "%s", device_path); + else + snprintf(fn, sizeof(fn), "%s/%s", confpath(), device_path); + + if (0 != fstat (upsfd, &datafile_stat) && 0 != stat (device_path, &datafile_stat)) { + upsdebugx(2, "Can't open %s currently", device_path); + } } } @@ -309,6 +533,12 @@ static int setvar(const char *varname, const char *val) if (item->info_flags & ST_FLAG_STRING) dstate_setaux(item->info_type, item->info_len); } + else + { + upsdebugx(2, "setvar: unknown variable (%s), using default flags", varname); + dstate_setflags(varname, ST_FLAG_STRING | ST_FLAG_RW); + dstate_setaux(varname, 32); + } } return STAT_SET_HANDLED; } @@ -320,7 +550,7 @@ static int setvar(const char *varname, const char *val) static int upsclient_update_vars(void) { int ret; - unsigned int numq, numa; + size_t numq, numa; const char *query[4]; char **answer; @@ -341,7 +571,7 @@ static int upsclient_update_vars(void) /* VAR */ if (numa < 4) { - upsdebugx(1, "Error: insufficient data (got %d args, need at least 4)", numa); + upsdebugx(1, "Error: insufficient data (got %zu args, need at least 4)", numa); } upsdebugx(5, "Received: %s %s %s %s", @@ -393,6 +623,7 @@ static int is_valid_data(const char* varname) static int is_valid_value(const char* varname, const char *value) { dummy_info_t *item; + NUT_UNUSED_VARIABLE(value); if ( (item = find_info(varname)) != NULL) { @@ -417,13 +648,14 @@ static void upsconf_err(const char *errmsg) /* for dummy mode * parse the definition file and process its content - */ -static int parse_data_file(int upsfd) + */ +static int parse_data_file(int arg_upsfd) { char fn[SMALLBUF]; char *ptr, var_value[MAX_STRING_SIZE]; - int value_args = 0, counter; + size_t value_args = 0, counter; time_t now; + NUT_UNUSED_VARIABLE(arg_upsfd); time(&now); @@ -525,7 +757,7 @@ static int parse_data_file(int upsfd) ctx->arglist[0], var_value, ctx->errmsg); } else - { + { upsdebugx(3, "parse_data_file: added \"%s\" with value \"%s\"", ctx->arglist[0], var_value); } diff --git a/drivers/dummy-ups.h b/drivers/dummy-ups.h index b086a52..34240a5 100644 --- a/drivers/dummy-ups.h +++ b/drivers/dummy-ups.h @@ -1,7 +1,7 @@ /* dummy-ups.h - NUT testing driver and repeater Copyright (C) - 2005 - 2010 Arnaud Quette + 2005 - 2012 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -18,11 +18,18 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* This file list all valid data with their type and info. - * this are then enable through a definition file, specified +#ifndef NUT_DUMMY_UPS_H_SEEN +#define NUT_DUMMY_UPS_H_SEEN 1 + +/* This file lists all valid data with their type and info. + * + * These are then enabled through a definition file, specified * as the "port" parameter (only the file name, not the path). + * * The format of this file is the same as an upsc dump: + * * : + * * FIXME: use cmdvartab for conformance checking * ... * Once the driver is loaded: @@ -36,24 +43,31 @@ /* Struct & data for ups.status processing */ /* --------------------------------------------------------------- */ +#if 0 /* XXX status lookup table not currently used???? */ +/* + * Status lookup table type definition + */ typedef struct { - const char *status_str; /* ups.status string */ - int status_value; /* ups.status value */ + const char *status_str; /* ups.status string */ + int status_value; /* ups.status flag bit */ } status_lkp_t; -#define STATUS_CAL 1 /* calibration */ -#define STATUS_TRIM 2 /* SmartTrim */ -#define STATUS_BOOST 4 /* SmartBoost */ -#define STATUS_OL 8 /* on line */ -#define STATUS_OB 16 /* on battery */ -#define STATUS_OVER 32 /* overload */ -#define STATUS_LB 64 /* low battery */ -#define STATUS_RB 128 /* replace battery */ -#define STATUS_BYPASS 256 /* on bypass */ -#define STATUS_OFF 512 /* ups is off */ -#define STATUS_CHRG 1024 /* charging */ -#define STATUS_DISCHRG 2048 /* discharging */ +#define STATUS_CAL (1 << 0) /* calibration */ +#define STATUS_TRIM (1 << 1) /* SmartTrim */ +#define STATUS_BOOST (1 << 2) /* SmartBoost */ +#define STATUS_OL (1 << 3) /* on line */ +#define STATUS_OB (1 << 4) /* on battery */ +#define STATUS_OVER (1 << 5) /* overload */ +#define STATUS_LB (1 << 6) /* low battery */ +#define STATUS_RB (1 << 7) /* replace battery */ +#define STATUS_BYPASS (1 << 8) /* on bypass */ +#define STATUS_OFF (1 << 9) /* ups is off */ +#define STATUS_CHRG (1 << 10) /* charging */ +#define STATUS_DISCHRG (1 << 11) /* discharging */ +/* + * Status lookup table + */ status_lkp_t status_info[] = { { "CAL", STATUS_CAL }, { "TRIM", STATUS_TRIM }, @@ -69,7 +83,7 @@ status_lkp_t status_info[] = { { "DISCHRG", STATUS_DISCHRG }, { "NULL", 0 }, }; -/* from usbhid-ups.h */ +#endif /* 0 -- not currently used??? */ typedef struct { char hid_value; /* HID value */ @@ -96,14 +110,15 @@ typedef struct { /* data flags */ #define DU_FLAG_NONE 0 #define DU_FLAG_INIT 1 /* intialy show element to upsd */ -#define DU_TYPE_CMD 2 /* --------------------------------------------------------------- */ /* Data table (all possible info from NUT, then enable upon cong */ /* --------------------------------------------------------------- */ /* FIXME: need to enforce value check with enum or bounds */ -dummy_info_t nut_data[] = +/* This array is only used from dummy-ups.c (there's a namesake + * for apcupsd-ups.c defined elsewhere) */ +static dummy_info_t nut_data[] = { /* Essential variables, loaded before parsing the definition file */ { "ups.mfr", ST_FLAG_STRING | ST_FLAG_RW, 32, "Dummy Manufacturer", DU_FLAG_INIT, NULL }, @@ -166,17 +181,17 @@ battery.alarm.threshold battery.date battery.packs battery.packs.bad - -ambient.temperature -ambient.temperature.alarm -ambient.temperature.high -ambient.temperature.low -ambient.humidity -ambient.humidity.alarm -ambient.humidity.high -ambient.humidity.low - -FIXME: how to manage these? +*/ + { "ambient.temperature", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, + { "ambient.temperature.alarm", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, + { "ambient.temperature.high", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, + { "ambient.temperature.low", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, + { "ambient.humidity", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, + { "ambient.humidity.alarm", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, + { "ambient.humidity.high", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, + { "ambient.humidity.low", ST_FLAG_RW, 1, NULL, DU_FLAG_NONE, NULL }, +/* +FIXME: how to manage these? (i.e. index ) outlet.n.id outlet.n.desc outlet.n.switch @@ -185,6 +200,7 @@ outlet.n.switchable outlet.n.autoswitch.charge.low outlet.n.delay.shutdown outlet.n.delay.start +... driver.name driver.version @@ -223,3 +239,5 @@ beeper.disable /* end of structure. */ { NULL, 0, 0, NULL, DU_FLAG_NONE, NULL } }; + +#endif /* NUT_DUMMY_UPS_H_SEEN */ diff --git a/drivers/eaton-ats16-nm2-mib.c b/drivers/eaton-ats16-nm2-mib.c new file mode 100644 index 0000000..1980c30 --- /dev/null +++ b/drivers/eaton-ats16-nm2-mib.c @@ -0,0 +1,280 @@ +/* eaton-ats16-nm2-mib.c - subdriver to monitor Eaton ATS16 SNMP devices with NUT + * using newer Network-M2 cards + * + * Copyright (C) + * 2011-2012 Arnaud Quette + * 2016-2020 Eaton (author: Arnaud Quette ) + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-snmp-subdriver script. It must be customized! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "eaton-ats16-nm2-mib.h" + +#define EATON_ATS16_NM2_MIB_VERSION "0.22" + +#define EATON_ATS16_NM2_SYSOID ".1.3.6.1.4.1.534.10.2" /* newer Network-M2 */ +#define EATON_ATS16_NM2_MODEL ".1.3.6.1.4.1.534.10.2.1.2.0" + +static info_lkp_t eaton_ats16_nm2_source_info[] = { + { 1, "init", NULL, NULL }, + { 2, "diagnosis", NULL, NULL }, + { 3, "off", NULL, NULL }, + { 4, "1", NULL, NULL }, + { 5, "2", NULL, NULL }, + { 6, "safe", NULL, NULL }, + { 7, "fault", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats16_nm2_sensitivity_info[] = { + { 1, "normal", NULL, NULL }, + { 2, "high", NULL, NULL }, + { 3, "low", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats16_nm2_input_frequency_status_info[] = { + { 1, "good", NULL, NULL }, /* No threshold triggered */ + { 2, "out-of-range", NULL, NULL }, /* Frequency out of range triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats16_nm2_input_voltage_status_info[] = { + { 1, "good", NULL, NULL }, /* No threshold triggered */ + { 2, "derated-range", NULL, NULL }, /* Voltage derated */ + { 3, "out-of-range", NULL, NULL }, /* Voltage out of range triggered */ + { 4, "unknown", NULL, NULL }, /* "missing" */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats16_nm2_test_result_info[] = { + { 1, "done and passed", NULL, NULL }, + { 2, "done and warning", NULL, NULL }, + { 3, "done and error", NULL, NULL }, + { 4, "aborted", NULL, NULL }, + { 5, "in progress", NULL, NULL }, + { 6, "no test initiated", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats16_nm2_output_status_info[] = { + { 1, "OFF", NULL, NULL }, /* Output not powered */ + { 2, "OL", NULL, NULL }, /* Output powered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats16_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "opened", NULL, NULL }, + { 2, "closed", NULL, NULL }, + { 3, "opened", NULL, NULL }, /* openWithNotice */ + { 4, "closed", NULL, NULL }, /* closedWithNotice */ + { 0, NULL, NULL, NULL } +}; + +/* EATON_ATS Snmp2NUT lookup table */ +static snmp_info_t eaton_ats16_nm2_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* Device collection */ + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* ats2IdentManufacturer.0 = STRING: EATON */ + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.1.0", "Eaton", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* ats2IdentModel.0 = STRING: Eaton ATS */ + { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.2.0", "ATS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* FIXME: RFC for device.firmware! */ + /* FIXME: the 2 "firmware" entries below should be SU_FLAG_SEMI_STATIC */ + /* ats2IdentFWVersion.0 = STRING: 00.00.0009 */ + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.3.0", NULL, SU_FLAG_OK, NULL }, + /* FIXME: RFC for device.firmware.aux! */ + /* ats2IdentRelease.0 = STRING: 1.7.5 */ + { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.7.0", NULL, SU_FLAG_OK, NULL }, + /* ats2IdentSerialNumber.0 = STRING: GA04F23009 */ + { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.5.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* ats2IdentPartNumber.0 = STRING: EATS16N */ + { "device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.6.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* ats2IdentAgentVersion.0 = STRING: 301F23C28 */ + /* { "unmapped.ats2IdentAgentVersion", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.7.0", NULL, SU_FLAG_OK, NULL, NULL }, */ + /* ats2InputDephasing.0 = INTEGER: 2 degrees */ + /* { "unmapped.ats2InputDephasing", 0, 1, ".1.3.6.1.4.1.534.10.2.2.1.1.0", NULL, SU_FLAG_OK, NULL, NULL }, */ + + /* Input collection */ + /* ats2InputIndex.source1 = INTEGER: source1(1) */ + { "input.1.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* ats2InputIndex.source2 = INTEGER: source2(2) */ + { "input.2.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.2", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* ats2InputVoltage.source1 = INTEGER: 2292 0.1 V */ + { "input.1.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* ats2InputVoltage.source2 = INTEGER: 2432 0.1 V */ + { "input.2.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusVoltage.source1 = INTEGER: normalRange(1) */ + { "input.1.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.1", NULL, SU_FLAG_OK, eaton_ats16_nm2_input_voltage_status_info }, + /* ats2InputStatusVoltage.source2 = INTEGER: normalRange(1) */ + { "input.2.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.2", NULL, SU_FLAG_OK, eaton_ats16_nm2_input_voltage_status_info }, + /* ats2InputFrequency.source1 = INTEGER: 500 0.1 Hz */ + { "input.1.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* ats2InputFrequency.source2 = INTEGER: 500 0.1 Hz */ + { "input.2.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusFrequency.source1 = INTEGER: good(1) */ + { "input.1.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.1", NULL, SU_FLAG_OK, eaton_ats16_nm2_input_frequency_status_info }, + /* ats2InputStatusFrequency.source2 = INTEGER: good(1) */ + { "input.2.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.2", NULL, SU_FLAG_OK, eaton_ats16_nm2_input_frequency_status_info }, + /* ats2ConfigSensitivity.0 = INTEGER: normal(1) */ + { "input.sensitivity", ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.6.0", NULL, SU_FLAG_OK, &eaton_ats16_nm2_sensitivity_info[0] }, + /* ats2OperationMode.0 = INTEGER: source1(4) */ + { "input.source", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.2.4.0", NULL, SU_FLAG_OK, eaton_ats16_nm2_source_info }, + /* ats2ConfigPreferred.0 = INTEGER: source1(1) */ + { "input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.5.0", NULL, SU_FLAG_OK, NULL }, + /* ats2InputDephasing = INTEGER: 181 */ + { "input.phase.shift", 0, 1, ".1.3.6.1.4.1.534.10.2.2.1.1.0", NULL, SU_FLAG_OK, NULL }, + + /* Output collection */ + /* ats2OutputVoltage.0 = INTEGER: 2304 0.1 V */ + { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.1.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigOutputVoltage.0 = INTEGER: 230 1 V */ + { "output.voltage.nominal", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.4.0", NULL, SU_FLAG_OK, NULL }, + /* ats2OutputCurrent.0 = INTEGER: 5 0.1 A */ + { "output.current", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.2.0", NULL, SU_FLAG_OK, NULL }, + + /* UPS collection */ + /* FIXME: RFC for device.test.result! */ + /* ats2ConfigTransferTest.0 = INTEGER: noTestInitiated(6) */ + { "ups.test.result", 0, 1, ".1.3.6.1.4.1.534.10.2.4.8.0", NULL, SU_FLAG_OK, eaton_ats16_nm2_test_result_info }, + /* FIXME: RFC for device.status! */ + /* ats2StatusOutput.0 = INTEGER: outputPowered(2) */ + { "ups.status", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.2.0", NULL, SU_FLAG_OK, eaton_ats16_nm2_output_status_info }, + + /* Ambient collection */ + /* ats2EnvRemoteTemp.0 = INTEGER: 0 degrees Centigrade */ + { "ambient.temperature", 0, 1, ".1.3.6.1.4.1.534.10.2.5.1.0", NULL, SU_FLAG_OK, NULL }, + /* ats2EnvRemoteTempLowerLimit.0 = INTEGER: 5 degrees Centigrade */ + { "ambient.temperature.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.5.0", NULL, SU_FLAG_OK, NULL }, + /* ats2EnvRemoteTempUpperLimit.0 = INTEGER: 40 degrees Centigrade */ + { "ambient.temperature.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.6.0", NULL, SU_FLAG_OK, NULL }, + /* ats2EnvRemoteHumidity.0 = INTEGER: 0 percent */ + { "ambient.humidity", 0, 1, ".1.3.6.1.4.1.534.10.2.5.2.0", NULL, SU_FLAG_OK, NULL }, + /* ats2EnvRemoteHumidityLowerLimit.0 = INTEGER: 5 percent */ + { "ambient.humidity.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.7.0", NULL, SU_FLAG_OK, NULL }, + /* ats2EnvRemoteHumidityUpperLimit.0 = INTEGER: 90 percent */ + { "ambient.humidity.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.8.0", NULL, SU_FLAG_OK, NULL }, + /* Dry contacts on EMP001 TH module */ + /* ats2ContactState.1 = INTEGER: open(1) */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.10.2.5.4.1.3.1", + NULL, SU_FLAG_OK, &eaton_ats16_ambient_drycontacts_info[0] }, + /* ats2ContactState.2 = INTEGER: open(1) */ + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.10.2.5.4.1.3.2", + NULL, SU_FLAG_OK, &eaton_ats16_ambient_drycontacts_info[0] }, + +#if 0 /* FIXME: Remaining data to be processed */ + /* ats2InputStatusDephasing.0 = INTEGER: normal(1) */ + { "unmapped.ats2InputStatusDephasing", 0, 1, ".1.3.6.1.4.1.534.10.2.3.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusIndex.source1 = INTEGER: source1(1) */ + { "unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusIndex.source2 = INTEGER: source2(2) */ + { "unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + + /* ats2InputStatusGood.source1 = INTEGER: voltageAndFreqNormalRange(2) */ + { "unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusGood.source2 = INTEGER: voltageAndFreqNormalRange(2) */ + { "unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusInternalFailure.source1 = INTEGER: good(1) */ + { "unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusInternalFailure.source2 = INTEGER: good(1) */ + { "unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + + /* ats2InputStatusUsed.source1 = INTEGER: poweringLoad(2) */ + { "unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.1", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusUsed.source2 = INTEGER: notPoweringLoad(1) */ + { "unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.2", NULL, SU_FLAG_OK, NULL }, + /* ats2StatusInternalFailure.0 = INTEGER: good(1) */ + { "unmapped.ats2StatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.1.0", NULL, SU_FLAG_OK, NULL }, + + /* ats2StatusOverload.0 = INTEGER: noOverload(1) */ + { "unmapped.ats2StatusOverload", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.3.0", NULL, SU_FLAG_OK, NULL }, + /* ats2StatusOverTemperature.0 = INTEGER: noOverTemperature(1) */ + { "unmapped.ats2StatusOverTemperature", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.4.0", NULL, SU_FLAG_OK, NULL }, + /* ats2StatusShortCircuit.0 = INTEGER: noShortCircuit(1) */ + { "unmapped.ats2StatusShortCircuit", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.5.0", NULL, SU_FLAG_OK, NULL }, + /* ats2StatusCommunicationLost.0 = INTEGER: good(1) */ + { "unmapped.ats2StatusCommunicationLost", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.6.0", NULL, SU_FLAG_OK, NULL }, + /* ats2StatusConfigurationFailure.0 = INTEGER: good(1) */ + { "unmapped.ats2StatusConfigurationFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.7.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigTimeRTC.0 = Wrong Type (should be Counter32): Gauge32: 19191036 */ + { "unmapped.ats2ConfigTimeRTC", 0, 1, ".1.3.6.1.4.1.534.10.2.4.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigTimeTextDate.0 = STRING: 08/11/1970 */ + { "unmapped.ats2ConfigTimeTextDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.2.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigTimeTextTime.0 = STRING: 02/50/36 */ + { "unmapped.ats2ConfigTimeTextTime", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.3.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigInputVoltageRating.0 = INTEGER: 1 1 V */ + { "unmapped.ats2ConfigInputVoltageRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.2.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigInputFrequencyRating.0 = INTEGER: 50 Hz */ + { "unmapped.ats2ConfigInputFrequencyRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.3.0", NULL, SU_FLAG_OK, NULL }, + + /* ats2ConfigTransferMode.0 = INTEGER: standard(1) */ + { "unmapped.ats2ConfigTransferMode", 0, 1, ".1.3.6.1.4.1.534.10.2.4.7.0", NULL, SU_FLAG_OK, NULL }, + + /* ats2ConfigBrownoutLow.0 = INTEGER: 202 1 V */ + { "unmapped.ats2ConfigBrownoutLow", 0, 1, ".1.3.6.1.4.1.534.10.2.4.9.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigBrownoutLowDerated.0 = INTEGER: 189 1 V */ + { "unmapped.ats2ConfigBrownoutLowDerated", 0, 1, ".1.3.6.1.4.1.534.10.2.4.10.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigBrownoutHigh.0 = INTEGER: 258 1 V */ + { "unmapped.ats2ConfigBrownoutHigh", 0, 1, ".1.3.6.1.4.1.534.10.2.4.11.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigHysteresisVoltage.0 = INTEGER: 5 1 V */ + { "unmapped.ats2ConfigHysteresisVoltage", 0, 1, ".1.3.6.1.4.1.534.10.2.4.12.0", NULL, SU_FLAG_OK, NULL }, + + /* Ambient collection */ + /* ats2EnvNumContacts.0 = INTEGER: 2 */ + { "unmapped.ats2EnvNumContacts", 0, 1, ".1.3.6.1.4.1.534.10.2.5.3.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactIndex.1 = INTEGER: 1 */ + { "unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactIndex.2 = INTEGER: 2 */ + { "unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactType.1 = INTEGER: notUsed(4) */ + { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactType.2 = INTEGER: notUsed(4) */ + { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactState.1 = INTEGER: open(1) */ + { "unmapped.ats2ContactState", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactState.2 = INTEGER: open(1) */ + { "unmapped.ats2ContactState", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactDescr.1 = STRING: Input #1 */ + { "unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactDescr.2 = STRING: Input #2 */ + { "unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.2", NULL, SU_FLAG_OK, NULL }, +#endif /* if 0 */ + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + +/* Note: keep the legacy definition intact, to avoid breaking compatibility */ + +/* FIXME: The lines below are duplicated to fix an issue with the code generator (nut-snmpinfo.py -> line is discarding) */ +/* Note: + * due to a bug in tools/nut-snmpinfo.py, prepending a 2nd mib2nut_info_t + * declaration with a comment line results in data extraction not being + * done for all entries in the file. Hence the above comment line being + * after its belonging declaration! */ + +/*mib2nut_info_t eaton_ats16_nm2 = { "eaton_ats16_nm2", EATON_ATS16_NM2_MIB_VERSION, NULL, EATON_ATS16_NM2_MODEL, eaton_ats16_nm2_mib, EATON_ATS16_NM2_SYSOID, NULL };*/ +mib2nut_info_t eaton_ats16_nm2 = { "eaton_ats16_nm2", EATON_ATS16_NM2_MIB_VERSION, NULL, EATON_ATS16_NM2_MODEL, eaton_ats16_nm2_mib, EATON_ATS16_NM2_SYSOID, NULL }; diff --git a/drivers/eaton-ats16-nm2-mib.h b/drivers/eaton-ats16-nm2-mib.h new file mode 100644 index 0000000..4fcd18c --- /dev/null +++ b/drivers/eaton-ats16-nm2-mib.h @@ -0,0 +1,30 @@ +/* eaton_ats16-nm2-mib.h - subdriver to monitor Eaton ATS16 NM2 SNMP devices with NUT + * + * Copyright (C) + * 2011-2012 Arnaud Quette + * 2016-2017 Eaton (author: Arnaud Quette ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EATON_ATS16_NM2_MIB_H +#define EATON_ATS16_NM2_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t eaton_ats16_nm2; + +#endif /* EATON_ATS16_NM2_MIB_H */ diff --git a/drivers/eaton-ats16-nmc-mib.c b/drivers/eaton-ats16-nmc-mib.c new file mode 100644 index 0000000..8cf2496 --- /dev/null +++ b/drivers/eaton-ats16-nmc-mib.c @@ -0,0 +1,275 @@ +/* eaton-ats16-nmc-mib.c - subdriver to monitor Eaton ATS16 NMC SNMP devices with NUT + * using legacy NMC cards + * + * Copyright (C) + * 2011-2012 Arnaud Quette + * 2016-2020 Eaton (author: Arnaud Quette ) + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-snmp-subdriver script. It must be customized! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "eaton-ats16-nmc-mib.h" + +#define EATON_ATS16_NMC_MIB_VERSION "0.21" + +#define EATON_ATS16_NMC_SYSOID ".1.3.6.1.4.1.705.1" /* legacy NMC */ +#define EATON_ATS16_NMC_MODEL ".1.3.6.1.4.1.534.10.2.1.2.0" + +static info_lkp_t eaton_ats16_nmc_source_info[] = { + { 1, "init", NULL, NULL }, + { 2, "diagnosis", NULL, NULL }, + { 3, "off", NULL, NULL }, + { 4, "1", NULL, NULL }, + { 5, "2", NULL, NULL }, + { 6, "safe", NULL, NULL }, + { 7, "fault", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats16_nmc_sensitivity_info[] = { + { 1, "normal", NULL, NULL }, + { 2, "high", NULL, NULL }, + { 3, "low", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats16_nmc_input_frequency_status_info[] = { + { 1, "good", NULL, NULL }, /* No threshold triggered */ + { 2, "out-of-range", NULL, NULL }, /* Frequency out of range triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats16_nmc_input_voltage_status_info[] = { + { 1, "good", NULL, NULL }, /* No threshold triggered */ + { 2, "derated-range", NULL, NULL }, /* Voltage derated */ + { 3, "out-of-range", NULL, NULL }, /* Voltage out of range triggered */ + { 4, "unknown", NULL, NULL }, /* "missing" */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats16_nmc_test_result_info[] = { + { 1, "done and passed", NULL, NULL }, + { 2, "done and warning", NULL, NULL }, + { 3, "done and error", NULL, NULL }, + { 4, "aborted", NULL, NULL }, + { 5, "in progress", NULL, NULL }, + { 6, "no test initiated", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats16_nmc_output_status_info[] = { + { 1, "OFF", NULL, NULL }, /* Output not powered */ + { 2, "OL", NULL, NULL }, /* Output powered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats16_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "opened", NULL, NULL }, + { 2, "closed", NULL, NULL }, + { 3, "opened", NULL, NULL }, /* openWithNotice */ + { 4, "closed", NULL, NULL }, /* closedWithNotice */ + { 0, NULL, NULL, NULL } +}; + +/* EATON_ATS_NMC Snmp2NUT lookup table */ +static snmp_info_t eaton_ats16_nmc_mib[] = { + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* Device collection */ + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* ats2IdentManufacturer.0 = STRING: EATON */ + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.1.0", "Eaton", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* ats2IdentModel.0 = STRING: Eaton ATS */ + { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.2.0", "ATS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* FIXME: RFC for device.firmware! */ + /* FIXME: the 2 "firmware" entries below should be SU_FLAG_SEMI_STATIC */ + /* ats2IdentFWVersion.0 = STRING: 00.00.0009 */ + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.3.0", NULL, SU_FLAG_OK, NULL }, + /* FIXME: RFC for device.firmware.aux! */ + /* ats2IdentRelease.0 = STRING: JF */ + { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.4.0", NULL, SU_FLAG_OK, NULL }, + /* ats2IdentSerialNumber.0 = STRING: GA04F23009 */ + { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.5.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* ats2IdentPartNumber.0 = STRING: EATS16N */ + { "device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.6.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* ats2IdentAgentVersion.0 = STRING: 301F23C28 */ + /* { "unmapped.ats2IdentAgentVersion", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.1.7.0", NULL, SU_FLAG_OK, NULL, NULL }, */ + /* ats2InputDephasing.0 = INTEGER: 2 degrees */ + /* { "unmapped.ats2InputDephasing", 0, 1, ".1.3.6.1.4.1.534.10.2.2.1.1.0", NULL, SU_FLAG_OK, NULL, NULL }, */ + + /* Input collection */ + /* ats2InputIndex.source1 = INTEGER: source1(1) */ + { "input.1.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* ats2InputIndex.source2 = INTEGER: source2(2) */ + { "input.2.id", 0, 1, ".1.3.6.1.4.1.534.10.2.2.2.1.1.2", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* ats2InputVoltage.source1 = INTEGER: 2292 0.1 V */ + { "input.1.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* ats2InputVoltage.source2 = INTEGER: 2432 0.1 V */ + { "input.2.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusVoltage.source1 = INTEGER: normalRange(1) */ + { "input.1.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.1", NULL, SU_FLAG_OK, eaton_ats16_nmc_input_voltage_status_info }, + /* ats2InputStatusVoltage.source2 = INTEGER: normalRange(1) */ + { "input.2.voltage.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.5.2", NULL, SU_FLAG_OK, eaton_ats16_nmc_input_voltage_status_info }, + /* ats2InputFrequency.source1 = INTEGER: 500 0.1 Hz */ + { "input.1.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* ats2InputFrequency.source2 = INTEGER: 500 0.1 Hz */ + { "input.2.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusFrequency.source1 = INTEGER: good(1) */ + { "input.1.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.1", NULL, SU_FLAG_OK, eaton_ats16_nmc_input_frequency_status_info }, + /* ats2InputStatusFrequency.source2 = INTEGER: good(1) */ + { "input.2.frequency.status", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.2.2", NULL, SU_FLAG_OK, eaton_ats16_nmc_input_frequency_status_info }, + /* ats2ConfigSensitivity.0 = INTEGER: normal(1) */ + { "input.sensitivity", ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.6.0", NULL, SU_FLAG_OK, &eaton_ats16_nmc_sensitivity_info[0] }, + /* ats2OperationMode.0 = INTEGER: source1(4) */ + { "input.source", ST_FLAG_STRING, 1, ".1.3.6.1.4.1.534.10.2.2.4.0", NULL, SU_FLAG_OK, eaton_ats16_nmc_source_info }, + /* ats2ConfigPreferred.0 = INTEGER: source1(1) */ + { "input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.5.0", NULL, SU_FLAG_OK, NULL }, + /* ats2InputDephasing = INTEGER: 181 */ + { "input.phase.shift", 0, 1, ".1.3.6.1.4.1.534.10.2.2.1.1.0", NULL, SU_FLAG_OK, NULL }, + + /* Output collection */ + /* ats2OutputVoltage.0 = INTEGER: 2304 0.1 V */ + { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.1.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigOutputVoltage.0 = INTEGER: 230 1 V */ + { "output.voltage.nominal", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.4.4.0", NULL, SU_FLAG_OK, NULL }, + /* ats2OutputCurrent.0 = INTEGER: 5 0.1 A */ + { "output.current", 0, 0.1, ".1.3.6.1.4.1.534.10.2.2.3.2.0", NULL, SU_FLAG_OK, NULL }, + + /* UPS collection */ + /* FIXME: RFC for device.test.result! */ + /* ats2ConfigTransferTest.0 = INTEGER: noTestInitiated(6) */ + { "ups.test.result", 0, 1, ".1.3.6.1.4.1.534.10.2.4.8.0", NULL, SU_FLAG_OK, eaton_ats16_nmc_test_result_info }, + /* FIXME: RFC for device.status! */ + /* ats2StatusOutput.0 = INTEGER: outputPowered(2) */ + { "ups.status", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.2.0", NULL, SU_FLAG_OK, eaton_ats16_nmc_output_status_info }, + + /* Ambient collection */ + /* ats2EnvRemoteTemp.0 = INTEGER: 0 degrees Centigrade */ + { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.534.10.2.5.1.0", NULL, SU_FLAG_OK, NULL }, + /* ats2EnvRemoteTempLowerLimit.0 = INTEGER: 5 degrees Centigrade */ + { "ambient.temperature.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.5.0", NULL, SU_FLAG_OK, NULL }, + /* ats2EnvRemoteTempUpperLimit.0 = INTEGER: 40 degrees Centigrade */ + { "ambient.temperature.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.6.0", NULL, SU_FLAG_OK, NULL }, + /* ats2EnvRemoteHumidity.0 = INTEGER: 0 percent */ + { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.534.10.2.5.2.0", NULL, SU_FLAG_OK, NULL }, + /* ats2EnvRemoteHumidityLowerLimit.0 = INTEGER: 5 percent */ + { "ambient.humidity.low", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.7.0", NULL, SU_FLAG_OK, NULL }, + /* ats2EnvRemoteHumidityUpperLimit.0 = INTEGER: 90 percent */ + { "ambient.humidity.high", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.2.5.8.0", NULL, SU_FLAG_OK, NULL }, + /* Dry contacts on EMP001 TH module */ + /* ats2ContactState.1 = INTEGER: open(1) */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.10.2.5.4.1.3.1", + NULL, SU_FLAG_OK, &eaton_ats16_ambient_drycontacts_info[0] }, + /* ats2ContactState.2 = INTEGER: open(1) */ + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.10.2.5.4.1.3.2", + NULL, SU_FLAG_OK, &eaton_ats16_ambient_drycontacts_info[0]}, + +#if 0 /* FIXME: Remaining data to be processed */ + /* ats2InputStatusDephasing.0 = INTEGER: normal(1) */ + { "unmapped.ats2InputStatusDephasing", 0, 1, ".1.3.6.1.4.1.534.10.2.3.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusIndex.source1 = INTEGER: source1(1) */ + { "unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusIndex.source2 = INTEGER: source2(2) */ + { "unmapped.ats2InputStatusIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + + /* ats2InputStatusGood.source1 = INTEGER: voltageAndFreqNormalRange(2) */ + { "unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusGood.source2 = INTEGER: voltageAndFreqNormalRange(2) */ + { "unmapped.ats2InputStatusGood", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusInternalFailure.source1 = INTEGER: good(1) */ + { "unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusInternalFailure.source2 = INTEGER: good(1) */ + { "unmapped.ats2InputStatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + + /* ats2InputStatusUsed.source1 = INTEGER: poweringLoad(2) */ + { "unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.1", NULL, SU_FLAG_OK, NULL }, + /* ats2InputStatusUsed.source2 = INTEGER: notPoweringLoad(1) */ + { "unmapped.ats2InputStatusUsed", 0, 1, ".1.3.6.1.4.1.534.10.2.3.2.1.6.2", NULL, SU_FLAG_OK, NULL }, + /* ats2StatusInternalFailure.0 = INTEGER: good(1) */ + { "unmapped.ats2StatusInternalFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.1.0", NULL, SU_FLAG_OK, NULL }, + + /* ats2StatusOverload.0 = INTEGER: noOverload(1) */ + { "unmapped.ats2StatusOverload", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.3.0", NULL, SU_FLAG_OK, NULL }, + /* ats2StatusOverTemperature.0 = INTEGER: noOverTemperature(1) */ + { "unmapped.ats2StatusOverTemperature", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.4.0", NULL, SU_FLAG_OK, NULL }, + /* ats2StatusShortCircuit.0 = INTEGER: noShortCircuit(1) */ + { "unmapped.ats2StatusShortCircuit", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.5.0", NULL, SU_FLAG_OK, NULL }, + /* ats2StatusCommunicationLost.0 = INTEGER: good(1) */ + { "unmapped.ats2StatusCommunicationLost", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.6.0", NULL, SU_FLAG_OK, NULL }, + /* ats2StatusConfigurationFailure.0 = INTEGER: good(1) */ + { "unmapped.ats2StatusConfigurationFailure", 0, 1, ".1.3.6.1.4.1.534.10.2.3.3.7.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigTimeRTC.0 = Wrong Type (should be Counter32): Gauge32: 19191036 */ + { "unmapped.ats2ConfigTimeRTC", 0, 1, ".1.3.6.1.4.1.534.10.2.4.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigTimeTextDate.0 = STRING: 08/11/1970 */ + { "unmapped.ats2ConfigTimeTextDate", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.2.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigTimeTextTime.0 = STRING: 02/50/36 */ + { "unmapped.ats2ConfigTimeTextTime", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.4.1.3.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigInputVoltageRating.0 = INTEGER: 1 1 V */ + { "unmapped.ats2ConfigInputVoltageRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.2.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigInputFrequencyRating.0 = INTEGER: 50 Hz */ + { "unmapped.ats2ConfigInputFrequencyRating", 0, 1, ".1.3.6.1.4.1.534.10.2.4.3.0", NULL, SU_FLAG_OK, NULL }, + + /* ats2ConfigTransferMode.0 = INTEGER: standard(1) */ + { "unmapped.ats2ConfigTransferMode", 0, 1, ".1.3.6.1.4.1.534.10.2.4.7.0", NULL, SU_FLAG_OK, NULL }, + + /* ats2ConfigBrownoutLow.0 = INTEGER: 202 1 V */ + { "unmapped.ats2ConfigBrownoutLow", 0, 1, ".1.3.6.1.4.1.534.10.2.4.9.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigBrownoutLowDerated.0 = INTEGER: 189 1 V */ + { "unmapped.ats2ConfigBrownoutLowDerated", 0, 1, ".1.3.6.1.4.1.534.10.2.4.10.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigBrownoutHigh.0 = INTEGER: 258 1 V */ + { "unmapped.ats2ConfigBrownoutHigh", 0, 1, ".1.3.6.1.4.1.534.10.2.4.11.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ConfigHysteresisVoltage.0 = INTEGER: 5 1 V */ + { "unmapped.ats2ConfigHysteresisVoltage", 0, 1, ".1.3.6.1.4.1.534.10.2.4.12.0", NULL, SU_FLAG_OK, NULL }, + + /* Ambient collection */ + /* ats2EnvNumContacts.0 = INTEGER: 2 */ + { "unmapped.ats2EnvNumContacts", 0, 1, ".1.3.6.1.4.1.534.10.2.5.3.0", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactIndex.1 = INTEGER: 1 */ + { "unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactIndex.2 = INTEGER: 2 */ + { "unmapped.ats2ContactIndex", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactType.1 = INTEGER: notUsed(4) */ + { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactType.2 = INTEGER: notUsed(4) */ + { "unmapped.ats2ContactType", 0, 1, ".1.3.6.1.4.1.534.10.2.5.4.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactDescr.1 = STRING: Input #1 */ + { "unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* ats2ContactDescr.2 = STRING: Input #2 */ + { "unmapped.ats2ContactDescr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.2.5.4.1.4.2", NULL, SU_FLAG_OK, NULL }, +#endif /* if 0 */ + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + +/* Note: keep the legacy definition intact, to avoid breaking compatibility */ + +/* FIXME: The lines below are duplicated to fix an issue with the code generator (nut-snmpinfo.py -> line is discarding) */ +/* Note: + * due to a bug in tools/nut-snmpinfo.py, prepending a 2nd mib2nut_info_t + * declaration with a comment line results in data extraction not being + * done for all entries in the file. Hence the above comment line being + * after its belonging declaration! */ + +/*mib2nut_info_t eaton_ats16_nmc = { "eaton_ats16_nmc", EATON_ATS16_NMC_MIB_VERSION, NULL, EATON_ATS16_NMC_MODEL, EATON_ATS16_NMC_mib, EATON_ATS16_NMC_SYSOID, NULL }; */ +mib2nut_info_t eaton_ats16_nmc = { "eaton_ats16_nmc", EATON_ATS16_NMC_MIB_VERSION, NULL, EATON_ATS16_NMC_MODEL, eaton_ats16_nmc_mib, EATON_ATS16_NMC_SYSOID, NULL }; diff --git a/drivers/eaton-ats16-nmc-mib.h b/drivers/eaton-ats16-nmc-mib.h new file mode 100644 index 0000000..14431b0 --- /dev/null +++ b/drivers/eaton-ats16-nmc-mib.h @@ -0,0 +1,30 @@ +/* eaton_ats16-nmc-mib.h - subdriver to monitor Eaton ATS16 NMC SNMP devices with NUT + * + * Copyright (C) + * 2011-2012 Arnaud Quette + * 2016-2017 Eaton (author: Arnaud Quette ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EATON_ATS16_NMC_MIB_H +#define EATON_ATS16_NMC_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t eaton_ats16_nmc; + +#endif /* EATON_ATS16_NMC_MIB_H */ diff --git a/drivers/eaton-ats30-mib.c b/drivers/eaton-ats30-mib.c new file mode 100644 index 0000000..ec03aab --- /dev/null +++ b/drivers/eaton-ats30-mib.c @@ -0,0 +1,372 @@ +/* eaton-ats30-mib.c - subdriver to monitor eaton_ats30 SNMP devices with NUT + * + * Copyright (C) 2017 Eaton + * Author: Tomas Halman + * 2011-2012 Arnaud Quette + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-snmp-subdriver script. It must be customized! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "eaton-ats30-mib.h" + +#define EATON_ATS30_MIB_VERSION "0.03" + +#define EATON_ATS30_SYSOID ".1.3.6.1.4.1.534.10.1" +#define EATON_ATS30_MODEL ".1.3.6.1.4.1.534.10.1.2.1.0" + +static info_lkp_t eaton_ats30_source_info[] = { + { 1, "init", NULL, NULL }, + { 2, "diagnosis", NULL, NULL }, + { 3, "off", NULL, NULL }, + { 4, "1", NULL, NULL }, + { 5, "2", NULL, NULL }, + { 6, "safe", NULL, NULL }, + { 7, "fault", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_ats30_input_sensitivity[] = { + { 1, "high", NULL, NULL }, + { 2, "low", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* + * bitmap values of atsStatus.atsFailureIndicator + * + * 1 atsFailureSwitchFault N/A + * 2 atsFailureNoOutput OFF + * 3 atsFailureOutputOC OVER + * 4 atsFailureOverTemperature N/A + */ +static info_lkp_t eaton_ats30_status_info[] = { + { 0, "OL", NULL, NULL }, + { 1, "OL", NULL, NULL }, /* SwitchFault */ + { 2, "OFF", NULL, NULL }, /* NoOutput */ + { 3, "OFF", NULL, NULL }, /* SwitchFault + NoOutput */ + { 4, "OL OVER", NULL, NULL }, /* OutputOC */ + { 5, "OL OVER", NULL, NULL }, /* OutputOC + SwitchFault */ + { 6, "OFF OVER", NULL, NULL }, /* OutputOC + NoOutput */ + { 7, "OFF OVER", NULL, NULL }, /* OutputOC + SwitchFault + NoOutput */ + { 8, "OL", NULL, NULL }, /* OverTemperature */ + { 9, "OL", NULL, NULL }, /* OverTemperature + SwitchFault */ + { 10, "OFF", NULL, NULL }, /* OverTemperature + NoOutput */ + { 11, "OFF", NULL, NULL }, /* OverTemperature + SwitchFault + NoOutput */ + { 12, "OL OVER", NULL, NULL }, /* OverTemperature + OutputOC */ + { 13, "OL OVER", NULL, NULL }, /* OverTemperature + OutputOC + SwitchFault */ + { 14, "OFF OVER", NULL, NULL }, /* OverTemperature + OutputOC + NoOutput */ + { 15, "OFF OVER", NULL, NULL }, /* OverTemperature + OutputOC + SwitchFault + NoOutput */ + { 0, NULL, NULL, NULL } +}; + +/* EATON_ATS30 Snmp2NUT lookup table */ +static snmp_info_t eaton_ats30_mib[] = { + /* device type: ats */ + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "ats", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* enterprises.534.10.1.1.1.0 = STRING: "Eaton" */ + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.1.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.1.2.0 = STRING: "01.12.13b" -- SNMP agent version */ + /* { "device.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.1.2.0", NULL, SU_FLAG_OK, NULL }, */ + /* enterprises.534.10.1.1.3.1.0 = INTEGER: 1 */ + /* { "unmapped.enterprise", 0, 1, ".1.3.6.1.4.1.534.10.1.1.3.1.0", NULL, SU_FLAG_OK, NULL }, */ + /* enterprises.534.10.1.2.1.0 = STRING: "STS30002SR10019 " */ + { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.2.2.0 = STRING: "1A0003AR00.00.00" -- Firmware */ + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.2.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.2.3.0 = STRING: "2014-09-17 " -- Release date */ + /* { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.3.0", NULL, SU_FLAG_OK, NULL }, */ + /* enterprises.534.10.1.2.4.0 = STRING: "JA00E52021 " */ + { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.4.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.2.5.0 = STRING: " " -- Device ID codes */ + /* { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.2.5.0", NULL, SU_FLAG_OK, NULL }, */ + + /* ats measure */ + /* =========== */ + /* enterprises.534.10.1.3.1.1.1.1 = INTEGER: 1 */ + { "input.1.id", 0, 1, ".1.3.6.1.4.1.534.10.1.3.1.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.3.1.1.1.2 = INTEGER: 2 */ + { "input.2.id", 0, 1, ".1.3.6.1.4.1.534.10.1.3.1.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.3.1.1.2.1 = INTEGER: 2379 */ + { "input.1.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.1.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.3.1.1.2.2 = INTEGER: 0 */ + { "input.2.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.1.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.3.1.1.3.1 = INTEGER: 500 */ + { "input.1.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.1.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.3.1.1.3.2 = INTEGER: 0 */ + { "input.2.frequency", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.1.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.3.2.1.0 = INTEGER: 2375 */ + { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.2.1.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.3.2.2.0 = INTEGER: 0 */ + { "output.current", 0, 0.1, ".1.3.6.1.4.1.534.10.1.3.2.2.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.3.3.0 = INTEGER: 25 -- internal temperature in celsius */ + { "ups.temperature", 0, 1, ".1.3.6.1.4.1.534.10.1.3.3.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.3.4.0 = INTEGER: 77 -- internal temperature in fahrenheit */ + /* { "ups.temperatureF", 0, 1, ".1.3.6.1.4.1.534.10.1.3.4.0", NULL, SU_FLAG_OK, NULL }, */ + /* enterprises.534.10.1.3.5.0 = INTEGER: 37937541 */ + { "device.uptime", 0, 1, ".1.3.6.1.4.1.534.10.1.3.5.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.3.6.0 = INTEGER: 284 */ + /* { "unmapped.atsMessureTransferedTimes", 0, 1, ".1.3.6.1.4.1.534.10.1.3.6.0", NULL, SU_FLAG_OK, NULL }, */ + /* enterprises.534.10.1.3.7.0 = INTEGER: 4 */ + { "input.source", 0, 1, ".1.3.6.1.4.1.534.10.1.3.7.0", NULL, SU_FLAG_OK, eaton_ats30_source_info }, + + /* atsStatus */ + /* ========= */ +#if 0 + /* NOTE: Unused OIDs are left as comments for potential future improvements */ + /* enterprises.534.10.1.4.1.0 = INTEGER: 7 */ + { "unmapped.atsInputFlowIndicator", 0, 1, ".1.3.6.1.4.1.534.10.1.4.1.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.2.1.1.1 = INTEGER: 1 -- atsInputFlowTable start */ + { "unmapped.atsInputFlowIndex.1", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.2.1.1.2 = INTEGER: 2 */ + { "unmapped.atsInputFlowIndex.2", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.2.1.2.1 = INTEGER: 1 */ + { "unmapped.atsInputFlowRelay.1", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.2.1.2.2 = INTEGER: 2 */ + { "unmapped.atsInputFlowRelay.2", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.2.1.3.1 = INTEGER: 1 */ + { "unmapped.atsInputFlowSCR.1", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.2.1.3.2 = INTEGER: 2 */ + { "unmapped.atsInputFlowSCR.2", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.2.1.4.1 = INTEGER: 1 */ + { "unmapped.atsInputFlowParallelRelay.1", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.2.1.4.2 = INTEGER: 2 */ + { "unmapped.atsInputFlowParallelRelay.2", 0, 1, ".1.3.6.1.4.1.534.10.1.4.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.3.0 = INTEGER: 58720256 */ + { "unmapped.atsInputFailureIndicator", 0, 1, ".1.3.6.1.4.1.534.10.1.4.3.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.1.1 = INTEGER: 1 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.1.2 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.2.1 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.2.2 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.3.1 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.3.2 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.4.1 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.4.2 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.4.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.5.1 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.5.2 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.5.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.6.1 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.6.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.6.2 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.6.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.7.1 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.7.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.7.2 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.7.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.8.1 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.8.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.8.2 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.8.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.9.1 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.9.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.9.2 = INTEGER: 1 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.9.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.10.1 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.10.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.10.2 = INTEGER: 1 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.10.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.11.1 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.11.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.4.1.11.2 = INTEGER: 1 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.4.1.11.2", NULL, SU_FLAG_OK, NULL }, +#endif /* 0 */ + + /* enterprises.atsFailureIndicator = INTEGER: 0 */ + { "ups.status", 0, 1, ".1.3.6.1.4.1.534.10.1.4.5.0", NULL, SU_FLAG_OK, eaton_ats30_status_info }, + +#if 0 + /* enterprises.534.10.1.4.6.1.0 = INTEGER: 2 -- atsFailure start */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.6.1.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.6.2.0 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.6.2.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.6.3.0 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.6.3.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.4.6.4.0 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.4.6.4.0", NULL, SU_FLAG_OK, NULL }, +#endif /* 0 */ + + /* atsLog */ + /* ====== */ +#if 0 + /* We are not interested in log */ + /* enterprises.534.10.1.5.1.0 = INTEGER: 272 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.1.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.1.1 = INTEGER: 1 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.1.2 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.1.3 = INTEGER: 3 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.3", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.1.4 = INTEGER: 4 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.1.5 = INTEGER: 5 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.1.6 = INTEGER: 6 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.1.7 = INTEGER: 7 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.1.8 = INTEGER: 8 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.1.9 = INTEGER: 9 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.9", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.1.10 = INTEGER: 10 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.1.10", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.2.1 = INTEGER: 1482323677 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.2.2 = INTEGER: 1480076955 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.2.3 = INTEGER: 1480069128 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.3", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.2.4 = INTEGER: 1480069093 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.4", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.2.5 = INTEGER: 1478693745 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.5", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.2.6 = INTEGER: 1478693741 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.6", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.2.7 = INTEGER: 1466604406 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.7", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.2.8 = INTEGER: 1466604386 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.8", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.2.9 = INTEGER: 1466604386 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.9", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.2.10 = INTEGER: 1463038288 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.2.10", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.3.1 = INTEGER: 41 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.3.2 = INTEGER: 41 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.3.3 = INTEGER: 44 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.3", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.3.4 = INTEGER: 44 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.4", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.3.5 = INTEGER: 44 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.5", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.3.6 = INTEGER: 41 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.6", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.3.7 = INTEGER: 41 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.7", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.3.8 = INTEGER: 46 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.8", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.3.9 = INTEGER: 45 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.9", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.3.10 = INTEGER: 41 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.5.2.1.3.10", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.4.1 = STRING: "12:34:37 12/21/2016" */ + { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.4.2 = STRING: "12:29:15 11/25/2016" */ + { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.4.3 = STRING: "10:18:48 11/25/2016" */ + { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.3", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.4.4 = STRING: "10:18:13 11/25/2016" */ + { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.4", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.4.5 = STRING: "12:15:45 11/09/2016" */ + { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.5", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.4.6 = STRING: "12:15:41 11/09/2016" */ + { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.6", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.4.7 = STRING: "14:06:46 06/22/2016" */ + { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.7", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.4.8 = STRING: "14:06:26 06/22/2016" */ + { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.8", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.4.9 = STRING: "14:06:26 06/22/2016" */ + { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.9", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.5.2.1.4.10 = STRING: "07:31:28 05/12/2016" */ + { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.5.2.1.4.10", NULL, SU_FLAG_OK, NULL }, +#endif /* 0 */ + + /* atsConfig */ + /* ========= */ +#if 0 + /* enterprises.534.10.1.6.1.1.0 = INTEGER: 538562409 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.1.1.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.1.2.0 = STRING: "01/24/2017" */ + { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.6.1.2.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.1.3.0 = STRING: "08:40:09" */ + { "unmapped.enterprises", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.10.1.6.1.3.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.1.1 = INTEGER: 1 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.1.2 = INTEGER: 2 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.1.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.2.1 = INTEGER: 1700 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.2.2 = INTEGER: 1700 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.2.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.3.1 = INTEGER: 1800 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.3.2 = INTEGER: 1800 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.3.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.4.1 = INTEGER: 2640 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.4.2 = INTEGER: 2640 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.4.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.5.1 = INTEGER: 3000 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.5.2 = INTEGER: 3000 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.5.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.6.1 = INTEGER: 50 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.6.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.6.2 = INTEGER: 50 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.6.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.7.1 = INTEGER: 40 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.7.1", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.2.1.7.2 = INTEGER: 40 */ + { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.6.2.1.7.2", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.3.0 = INTEGER: 2640 */ + { "unmapped.atsConfigInputVoltageRating", 0, 1, ".1.3.6.1.4.1.534.10.1.6.3.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.4.0 = INTEGER: 26 */ + { "unmapped.atsConfigRandomTime", 0, 1, ".1.3.6.1.4.1.534.10.1.6.4.0", NULL, SU_FLAG_OK, NULL }, +#endif /* 0 */ + + /* enterprises.534.10.1.6.5.0 = INTEGER: 1 */ + { "input.source.preferred", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.1.6.5.0", NULL, SU_FLAG_OK, NULL }, + /* enterprises.534.10.1.6.6.0 = INTEGER: 2 */ + { "input.sensitivity", ST_FLAG_RW, 1, ".1.3.6.1.4.1.534.10.1.6.6.0", NULL, SU_FLAG_OK, eaton_ats30_input_sensitivity }, + + /* enterprises.534.10.1.6.7.0 = INTEGER: 2 */ + /* { "unmapped.atsConfigTest", 0, 1, ".1.3.6.1.4.1.534.10.1.6.7.0", NULL, SU_FLAG_OK, NULL }, */ + + /* atsUpgrade */ + /* ========== */ +#if 0 + /* We are not interested in atsUpgrade */ + /* enterprises.534.10.1.7.1.0 = INTEGER: 1 */ + /* { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.7.1.0", NULL, SU_FLAG_OK, NULL }, */ + /* enterprises.534.10.1.7.2.0 = INTEGER: 1 */ + /* { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.7.2.0", NULL, SU_FLAG_OK, NULL }, */ + /* enterprises.534.10.1.7.3.0 = INTEGER: 0 */ + /* { "unmapped.enterprises", 0, 1, ".1.3.6.1.4.1.534.10.1.7.3.0", NULL, SU_FLAG_OK, NULL }, */ +#endif /* 0 */ + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + +mib2nut_info_t eaton_ats30 = { "eaton_ats30", EATON_ATS30_MIB_VERSION, NULL, EATON_ATS30_MODEL, eaton_ats30_mib, EATON_ATS30_SYSOID, NULL }; diff --git a/drivers/eaton-ats30-mib.h b/drivers/eaton-ats30-mib.h new file mode 100644 index 0000000..09b0b18 --- /dev/null +++ b/drivers/eaton-ats30-mib.h @@ -0,0 +1,29 @@ +/* eaton_ats30-mib.h - subdriver to monitor eaton_ats30 SNMP devices with NUT + * + * Copyright (C) 2017 Eaton + * Author: Tomas Halman + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EATON_ATS30_MIB_H +#define EATON_ATS30_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t eaton_ats30; + +#endif /* EATON_ATS30_MIB_H */ diff --git a/drivers/eaton-mib.c b/drivers/eaton-mib.c deleted file mode 100644 index 6da265b..0000000 --- a/drivers/eaton-mib.c +++ /dev/null @@ -1,354 +0,0 @@ -/* eaton-mib.c - data to monitor Eaton Aphel PDUs (Basic and Complex) - * - * Copyright (C) 2008 - 2010 - * Arnaud Quette - * - * Sponsored by Eaton - * and MGE Office Protection Systems - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "eaton-mib.h" - -#define EATON_APHEL_MIB_VERSION "0.46" - -/* APHEL-GENESIS-II-MIB (monitored ePDU) - * ************************************* - * Note: we should also be able to support this one using netxml-ups! - */ - -#define APHEL1_OID_MIB ".1.3.6.1.4.1.17373" -#define APHEL1_SYSOID APHEL1_OID_MIB -#define APHEL1_OID_MODEL_NAME ".1.3.6.1.4.1.17373.3.1.1.0" -#define APHEL1_OID_FIRMREV ".1.3.6.1.4.1.17373.3.1.2.0" -#define APHEL1_OID_DEVICE_NAME ".1.3.6.1.4.1.17373.3.1.3.0" -#define APHEL1_OID_UNIT_MACADDR ".1.3.6.1.4.1.17373.3.1.4.0" -/* needs concat ..0 */ -#define APHEL1_OID_OUTLET_CURRENT ".1.3.6.1.4.1.17373.3.2" - -/* Snmp2NUT lookup table for GenesisII MIB */ -static snmp_info_t eaton_aphel_genesisII_mib[] = { - /* Device page */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_MODEL_NAME, - "Eaton Powerware ePDU Monitored", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - - /* UPS page */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_MODEL_NAME, - "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_DEVICE_NAME, - "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_FIRMREV, "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - { "ups.macaddr", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_UNIT_MACADDR, "unknown", - 0, NULL, NULL }, - - /* Outlet page */ - /* we can't use template since there is no counterpart to outlet.count */ - { "outlet.1.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".1.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, - { "outlet.2.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".2.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, - { "outlet.3.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".3.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, - { "outlet.4.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".4.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, - { "outlet.5.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".5.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, - { "outlet.6.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".6.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, - { "outlet.7.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".7.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, - { "outlet.8.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".8.0", NULL, SU_FLAG_NEGINVALID, NULL, NULL }, - - /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } -}; - - -/* APHEL PDU-MIB - Revelation MIB (Managed ePDU) - * ********************************************* */ - -#define AR_BASE_OID ".1.3.6.1.4.1.534.6.6.6" -#define APHEL2_SYSOID AR_BASE_OID -#define APHEL2_OID_MODEL_NAME AR_OID_MODEL_NAME - -#define AR_OID_MODEL_NAME AR_BASE_OID ".1.1.12.0" -#define AR_OID_DEVICE_NAME AR_BASE_OID ".1.1.13.0" -#define AR_OID_FIRMREV AR_BASE_OID ".1.1.1.0" -#define AR_OID_SERIAL AR_BASE_OID ".1.1.2.0" -#define AR_OID_UNIT_MACADDR AR_BASE_OID ".1.1.6.0" - -#define AR_OID_UNIT_CURRENT AR_BASE_OID ".1.3.1.1" -#define AR_OID_UNIT_VOLTAGE AR_BASE_OID ".1.3.1.2" -#define AR_OID_UNIT_ACTIVEPOWER AR_BASE_OID ".1.3.1.3" -#define AR_OID_UNIT_APPARENTPOWER AR_BASE_OID ".1.3.1.4" -#define AR_OID_UNIT_CPUTEMPERATURE AR_BASE_OID ".1.3.1.5.0" - -#define AR_OID_OUTLET_INDEX AR_BASE_OID ".1.2.2.1.1" -#define AR_OID_OUTLET_NAME AR_BASE_OID ".1.2.2.1.2" -#define AR_OID_OUTLET_STATUS AR_BASE_OID ".1.2.2.1.3" - -static info_lkp_t outlet_status_info[] = { - { -1, "error" }, - { 0, "off" }, - { 1, "on" }, - { 2, "cycling" }, /* transitional status */ - { 0, NULL } -}; - -#define DO_OFF 0 -#define DO_ON 1 -#define DO_CYCLE 2 - -#define AR_OID_OUTLET_COUNT AR_BASE_OID ".1.2.1.0" -#define AR_OID_OUTLET_CURRENT AR_BASE_OID ".1.2.2.1.4" -#define AR_OID_OUTLET_MAXCURRENT AR_BASE_OID ".1.2.2.1.5" -#define AR_OID_OUTLET_VOLTAGE AR_BASE_OID ".1.2.2.1.6" -#define AR_OID_OUTLET_ACTIVEPOWER AR_BASE_OID ".1.2.2.1.7" -#define AR_OID_OUTLET_APPARENTPOWER AR_BASE_OID ".1.2.2.1.8" -#define AR_OID_OUTLET_POWERFACTOR AR_BASE_OID ".1.2.2.1.9" - -/* Snmp2NUT lookup table for Eaton Revelation MIB */ -static snmp_info_t eaton_aphel_revelation_mib[] = { - /* Device page */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_MODEL_NAME, - "Eaton Powerware ePDU Managed", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_SERIAL, "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - - /* UPS page */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_MODEL_NAME, - "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, - "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_SERIAL, "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_FIRMREV, "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - { "ups.macaddr", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_UNIT_MACADDR, "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "ups.temperature", 0, 1, AR_OID_UNIT_CPUTEMPERATURE, NULL, 0, NULL, NULL }, - - /* Outlet page */ - { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "outlet.count", 0, 1, AR_OID_OUTLET_COUNT, "0", 0, NULL }, - { "outlet.current", 0, 0.001, AR_OID_UNIT_CURRENT ".0", NULL, 0, NULL, NULL }, - { "outlet.voltage", 0, 0.001, AR_OID_UNIT_VOLTAGE ".0", NULL, 0, NULL, NULL }, - { "outlet.realpower", 0, 1.0, AR_OID_UNIT_ACTIVEPOWER ".0", NULL, 0, NULL, NULL }, - { "outlet.power", 0, 1.0, AR_OID_UNIT_APPARENTPOWER ".0", NULL, 0, NULL, NULL }, - - /* outlet template definition - * Caution: the index of the data start at 0, while the name is +1 - * ie outlet.1 => .0 */ - { "outlet.%i.switchable", 0, 1, AR_OID_OUTLET_INDEX ".%i", "yes", SU_FLAG_STATIC | SU_OUTLET, NULL, NULL }, - { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL, NULL }, - { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_NAME ".%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_STATUS ".%i", NULL, SU_FLAG_OK | SU_OUTLET, &outlet_status_info[0], NULL }, - { "outlet.%i.current", 0, 0.001, AR_OID_OUTLET_CURRENT ".%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.current.maximum", 0, 0.001, AR_OID_OUTLET_MAXCURRENT ".%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.realpower", 0, 1.0, AR_OID_OUTLET_ACTIVEPOWER ".%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.voltage", 0, 1.0, AR_OID_OUTLET_VOLTAGE ".%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.powerfactor", 0, 0.01, AR_OID_OUTLET_POWERFACTOR ".%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.power", 0, 1.0, AR_OID_OUTLET_APPARENTPOWER ".%i", NULL, SU_OUTLET, NULL, NULL }, - - /* FIXME: - * - delay for startup/shutdown sequence - * - support for multiple Ambient sensors ( max. 8), starting at index '0' - * ambient.%i.temperature => .1.3.6.1.4.1.534.6.6.6.2.2.1.3.%i - * ambient.%i.humidity => .1.3.6.1.4.1.534.6.6.6.2.4.1.3.%i - */ - - /* Ambient page */ - /* We use critical levels, for both temperature and humidity, - * since warning levels are also available! */ - { "ambient.temperature", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.2.1.3.0", NULL, SU_FLAG_OK, NULL, NULL }, - { "ambient.temperature.low", 0, 1.0, "1.3.6.1.4.1.534.6.6.6.2.2.1.6.0", NULL, SU_FLAG_OK, NULL, NULL }, - { "ambient.temperature.high", 0, 1.0, "1.3.6.1.4.1.534.6.6.6.2.2.1.7.0", NULL, SU_FLAG_OK, NULL, NULL }, - { "ambient.humidity", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.3.0", NULL, SU_FLAG_OK, NULL, NULL }, - { "ambient.humidity.low", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.6.0", NULL, SU_FLAG_OK, NULL, NULL }, - { "ambient.humidity.high", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.7.0", NULL, SU_FLAG_OK, NULL, NULL }, - - /* instant commands. */ - /* Note that load.cycle might be replaced by / mapped on shutdown.reboot */ - /* no counterpart found! - { "outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL, NULL }, - { "outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL, NULL }, - { "outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL, NULL }, */ - { "outlet.%i.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, - { "outlet.%i.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, - { "outlet.%i.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, - - /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } -}; - -/* Eaton PDU-MIB - Marlin MIB - * ************************** */ - -#define EATON_MARLIN_MIB_VERSION "0.06" -#define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" -#define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" - -static info_lkp_t marlin_outlet_status_info[] = { - { 0, "off" }, - { 1, "on" }, - { 2, "pendingOff" }, /* transitional status */ - { 3, "pendingOn" }, /* transitional status */ - { 0, NULL } -}; - -/* Ugly hack: having the matching OID present means that the outlet is - * switchable. So, it should not require this value lookup */ -static info_lkp_t outlet_switchability_info[] = { - { -1, "yes" }, - { 0, "yes" }, - { 0, NULL } -}; - -/* Snmp2NUT lookup table for Eaton Marlin MIB */ -static snmp_info_t eaton_marlin_mib[] = { - /* Device page */ - { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0", - "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.4.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - /* FIXME: need RFC validation on this variable - * { "device.part", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.3.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, */ - - /* UPS page */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.6.6.7.1.2.1.2.0", - "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - - /* FIXME: use unitName.0 (ePDU)? - * { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, - "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, */ - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.4.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.5.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - /* TODO: - * The below possibly requires (?) the use of - * int snprint_hexstring(char *buf, size_t buf_len, const u_char *, size_t); - * { "ups.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - * + date reformating callback - * 2011-8-29,16:27:25.0,+1:0 - * Hex-STRING: 07 DB 08 1D 10 0C 36 00 2B 01 00 00 - * { "ups.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.8.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - * { "ups.time", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.1.2.1.8.0", - "", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - */ - - /* Input page */ - { "input.phases", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - /* inputType.0.1 singlePhase (1) iso.3.6.1.4.1.534.6.6.7.3.1.1.2.0.1 */ - { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.0.1", NULL, 0, NULL, NULL }, - { "input.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.0.1.1", NULL, 0, NULL, NULL }, - /* FIXME: check multiplier */ - { "input.current", 0, 0.01, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.1", NULL, 0, NULL, NULL }, - - /* Ambient page */ - /* We use critical levels, for both temperature and humidity, - * since warning levels are also available! */ - { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.4.0.1", NULL, SU_FLAG_OK, NULL, NULL }, - { "ambient.temperature.low", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.7.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, - { "ambient.temperature.high", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.1.1.9.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, - { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.4.0.1", NULL, SU_FLAG_OK, NULL, NULL }, - { "ambient.humidity.low", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.7.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, - { "ambient.humidity.high", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.7.2.1.9.0.1", NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL, NULL }, - - /* Outlet page */ - { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - { "outlet.count", 0, 1, ".1.3.6.1.4.1.534.6.6.7.1.2.1.22.0", "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - /* The below ones are the same as the input.* equivalent */ - { "outlet.frequency", 0, 0.1, ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.0.1", NULL, 0, NULL, NULL }, - { "outlet.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.0.1.1", NULL, 0, NULL, NULL }, - { "outlet.current", 0, 0.01, ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.1", NULL, 0, NULL, NULL }, - /* There is also a .2 available (ie .1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.2) */ - { "outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.0.1.2", NULL, 0, NULL, NULL }, - /* There is also a .2 available (ie .1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.2) */ - { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.0.1.1", NULL, 0, NULL, NULL }, - - /* outlet template definition - * Indexes start from 1, ie outlet.1 => .1 */ - /* Note: the first definition is used to determine the base index (ie 0 or 1) */ - { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.1.1.3.0.%i", NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, NULL, NULL }, - { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.2.0.%i", - NULL, SU_FLAG_OK | SU_OUTLET, &marlin_outlet_status_info[0], NULL }, - /* FIXME: or use ".1.3.6.1.4.1.534.6.6.7.6.1.1.2.0.1", though it's related to groups! */ - { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL, NULL }, - { "outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.4.1.3.0.%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.6.5.1.3.0.%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.voltage", 0, 0.001, ".1.3.6.1.4.1.534.6.6.7.6.3.1.2.0.%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.power", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.6.5.1.2.0.%i", NULL, SU_OUTLET, NULL, NULL }, - /* FIXME: handle non switchable units (only measurements), which do not expose this OID */ - { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.0.%i", "no", SU_FLAG_STATIC | SU_FLAG_OK, &outlet_switchability_info[0], NULL }, - - /* TODO: handle statistics - * outletWh.0.1 - * outletWhTimer.0.1 - */ - - /* instant commands. */ - /* Notes: - * - load.cycle might be replaced by / mapped on shutdown.reboot - * - outletControl{Off,On,Reboot}Cmd values: - * 0-n : Timer - * -1 : Cancel - * we currently use "0", so instant On | Off | Reboot... */ - /* no counterpart found! - { "outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL, NULL }, - { "outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL, NULL }, - { "outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL, NULL }, */ - - /* TODO: handle delays */ - { "outlet.%i.load.off", 0, 0, ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.0.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, - { "outlet.%i.load.on", 0, 0, ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.0.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, - { "outlet.%i.load.cycle", 0, 0, ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.0.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, - - /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } -}; - -mib2nut_info_t aphel_genesisII = { "aphel_genesisII", EATON_APHEL_MIB_VERSION, "", APHEL1_OID_MODEL_NAME, eaton_aphel_genesisII_mib, APHEL1_SYSOID }; -mib2nut_info_t aphel_revelation = { "aphel_revelation", EATON_APHEL_MIB_VERSION, "", APHEL2_OID_MODEL_NAME, eaton_aphel_revelation_mib, APHEL2_SYSOID }; -mib2nut_info_t eaton_marlin = { "eaton_epdu", EATON_MARLIN_MIB_VERSION, "", EATON_MARLIN_OID_MODEL_NAME, eaton_marlin_mib, EATON_MARLIN_SYSOID }; - diff --git a/drivers/eaton-mib.h b/drivers/eaton-mib.h deleted file mode 100644 index 4997066..0000000 --- a/drivers/eaton-mib.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef EATON_MIB_H -#define EATON_MIB_H - -#include "main.h" -#include "snmp-ups.h" - -extern mib2nut_info_t aphel_genesisII; -extern mib2nut_info_t aphel_revelation; -extern mib2nut_info_t eaton_marlin; - -#endif /* EATON_MIB_H */ diff --git a/drivers/eaton-pdu-genesis2-mib.c b/drivers/eaton-pdu-genesis2-mib.c new file mode 100644 index 0000000..22ff01e --- /dev/null +++ b/drivers/eaton-pdu-genesis2-mib.c @@ -0,0 +1,95 @@ +/* eaton-pdu-genesis2-mib.c - data to monitor Eaton ePDUs branded as: + * G1 Aphel based ePDUs (Basic) - GenesisII + * + * Copyright (C) 2008 - 2019 + * Arnaud Quette + * Arnaud Quette + * Copyright (C) 2015 - 2017 + * Jim Klimov + * + * Supported by Eaton + * and previously MGE Office Protection Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "eaton-pdu-genesis2-mib.h" + +#define EATON_APHEL_GENESIS2_MIB_VERSION "0.52" + +/* APHEL-GENESIS-II-MIB (monitored ePDU) + * ************************************* + * Note: There is also a basic XML interface, but not worth + * implementing in netxml-ups! + */ + +#define APHEL1_OID_MIB ".1.3.6.1.4.1.17373" +#define APHEL1_SYSOID APHEL1_OID_MIB +#define APHEL1_OID_MODEL_NAME ".1.3.6.1.4.1.17373.3.1.1.0" +#define APHEL1_OID_FIRMREV ".1.3.6.1.4.1.17373.3.1.2.0" +#define APHEL1_OID_DEVICE_NAME ".1.3.6.1.4.1.17373.3.1.3.0" +#define APHEL1_OID_UNIT_MACADDR ".1.3.6.1.4.1.17373.3.1.4.0" +/* needs concat ..0 */ +#define APHEL1_OID_OUTLET_CURRENT ".1.3.6.1.4.1.17373.3.2" + +/* Snmp2NUT lookup table for GenesisII MIB */ +static snmp_info_t eaton_aphel_genesisII_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* Device page */ + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.model", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_MODEL_NAME, + "Eaton Powerware ePDU Monitored", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_UNIT_MACADDR, "unknown", + 0, NULL }, + + /* UPS page */ + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_MODEL_NAME, + "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_DEVICE_NAME, + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, APHEL1_OID_FIRMREV, "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + + /* Outlet page */ + /* we can't use template since there is no counterpart to outlet.count */ + { "outlet.1.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".1.0", NULL, SU_FLAG_NEGINVALID, NULL }, + { "outlet.2.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".2.0", NULL, SU_FLAG_NEGINVALID, NULL }, + { "outlet.3.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".3.0", NULL, SU_FLAG_NEGINVALID, NULL }, + { "outlet.4.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".4.0", NULL, SU_FLAG_NEGINVALID, NULL }, + { "outlet.5.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".5.0", NULL, SU_FLAG_NEGINVALID, NULL }, + { "outlet.6.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".6.0", NULL, SU_FLAG_NEGINVALID, NULL }, + { "outlet.7.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".7.0", NULL, SU_FLAG_NEGINVALID, NULL }, + { "outlet.8.current", 0, 0.1, APHEL1_OID_OUTLET_CURRENT ".8.0", NULL, SU_FLAG_NEGINVALID, NULL }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + + +mib2nut_info_t aphel_genesisII = { "aphel_genesisII", EATON_APHEL_GENESIS2_MIB_VERSION, NULL, APHEL1_OID_MODEL_NAME, eaton_aphel_genesisII_mib, APHEL1_SYSOID, NULL }; diff --git a/drivers/eaton-pdu-genesis2-mib.h b/drivers/eaton-pdu-genesis2-mib.h new file mode 100644 index 0000000..df9dc0c --- /dev/null +++ b/drivers/eaton-pdu-genesis2-mib.h @@ -0,0 +1,32 @@ +/* eaton-pdu-genesis2-mib.h - subdriver to monitor Eaton ePDU SNMP devices with NUT + * + * Copyright (C) + * 2010 Arjen de Korte + * 2011 - 2012 Arnaud Quette + * 2017 Arnaud Quette + * 2017 Jim Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EATON_EPDU_GENESIS_MIB_H +#define EATON_EPDU_GENESIS_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t aphel_genesisII; + +#endif /* EATON_EPDU_GENESIS_MIB_H */ diff --git a/drivers/eaton-pdu-marlin-helpers.c b/drivers/eaton-pdu-marlin-helpers.c new file mode 100644 index 0000000..421045f --- /dev/null +++ b/drivers/eaton-pdu-marlin-helpers.c @@ -0,0 +1,91 @@ +/* eaton-pdu-marlin-helpers.c - helper routines for eaton-pdu-marlin-mib.c + * to monitor Eaton ePDUs branded as: + * G2 Marlin SW / MI / MO / MA + * G3 Shark SW / MI / MO / MA + * + * Copyright (C) 2017-2019 + * Arnaud Quette + * Copyright (C) 2017 + * Jim Klimov + * + * Supported by Eaton + * and previously MGE Office Protection Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "config.h" /* must be the first header */ + +#include +#include +#include + +#include "eaton-pdu-marlin-helpers.h" +#include "dstate.h" +#include "common.h" +/* Allow access to temperature_unit */ +#include "snmp-ups.h" + +/* Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount + * of "," separators+1 using an inline function */ +long marlin_device_count_fun(const char *daisy_dev_list) +{ + long count = 0, i; + + for (i = 0; daisy_dev_list[i] != '\0'; i++) { + if (daisy_dev_list[i] == ',') { + /* Each comma means a new device in the list */ + count ++; + } + } + if (i > 0 && (daisy_dev_list[i - 1] != ',') ) { + /* Non-empty string => at least one device, and no trailing commas */ + count ++; + } + + upsdebugx(3, "%s: counted devices in '%s', got %ld", + __func__, daisy_dev_list, count); + return count; +} + +/* Temperature unit consideration: + * only store the device unit, for converting to Celsius. + * Don't publish the device unit, since NUT will publish + * as Celsius in all cases */ +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) +{ + long snmp_value = *((long*)raw_snmp_value); + switch (snmp_value) { + case 0: + /* store the value, for temperature processing */ + temperature_unit = TEMPERATURE_KELVIN; + break; + case 1: + /* store the value, for temperature processing */ + temperature_unit = TEMPERATURE_CELSIUS; + break; + case 2: + /* store the value, for temperature processing */ + temperature_unit = TEMPERATURE_FAHRENHEIT; + break; + default: + /* store the value, for temperature processing */ + temperature_unit = TEMPERATURE_UNKNOWN; + break; + } + return "celsius"; +} diff --git a/drivers/eaton-pdu-marlin-helpers.h b/drivers/eaton-pdu-marlin-helpers.h new file mode 100644 index 0000000..fc9890e --- /dev/null +++ b/drivers/eaton-pdu-marlin-helpers.h @@ -0,0 +1,29 @@ +/* eaton-pdu-marlin-helpers.h - helper for subdriver to monitor certain + * Eaton ePDU SNMP devices with NUT + * + * Copyright (C) + * 2017-2019 Arnaud Quette + * 2017 Jim Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EATON_EPDU_MARLIN_HELPERS_H +#define EATON_EPDU_MARLIN_HELPERS_H + +long marlin_device_count_fun(const char *daisy_dev_list); +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value); + +#endif /* EATON_EPDU_MARLIN_HELPERS_H */ diff --git a/drivers/eaton-pdu-marlin-mib.c b/drivers/eaton-pdu-marlin-mib.c new file mode 100644 index 0000000..bed4bbe --- /dev/null +++ b/drivers/eaton-pdu-marlin-mib.c @@ -0,0 +1,1366 @@ +/* eaton-pdu-marlin-mib.c - data to monitor Eaton ePDUs branded as: + * G2 Marlin SW / MI / MO / MA + * G3 Shark SW / MI / MO / MA + * + * Copyright (C) 2008 - 2020 + * Arnaud Quette + * Arnaud Quette + * Copyright (C) 2015 - 2017 + * Jim Klimov + * + * Supported by Eaton + * and previously MGE Office Protection Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "eaton-pdu-marlin-mib.h" +#if WITH_SNMP_LKP_FUN +#include "eaton-pdu-marlin-helpers.h" +#endif + +/* Eaton PDU-MIB - Marlin MIB + * ************************** */ + +#define EATON_MARLIN_MIB_VERSION "0.69" +#define EATON_MARLIN_SYSOID ".1.3.6.1.4.1.534.6.6.7" +#define EATON_MARLIN_OID_MODEL_NAME ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" + +static info_lkp_t marlin_outlet_status_info[] = { + { 0, "off", NULL, NULL }, + { 1, "on", NULL, NULL }, + { 2, "pendingOff", NULL, NULL }, /* transitional status */ + { 3, "pendingOn", NULL, NULL }, /* transitional status */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_outletgroups_status_info[] = { + { 0, "off", NULL, NULL }, + { 1, "on", NULL, NULL }, + { 2, "rebooting", NULL, NULL }, /* transitional status */ + { 3, "mixed", NULL, NULL }, /* transitional status, not sure what it means! */ + { 0, NULL, NULL, NULL } +}; + +/* Ugly hack for older G2 ePDU: + * having the matching OID present means that the outlet/unit is + * switchable. So, it should not require this value lookup */ +static info_lkp_t g2_unit_outlet_switchability_info[] = { + { -1, "yes", NULL, NULL }, + { 0, "yes", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_outlet_switchability_info[] = { + { 1, "yes", NULL, NULL }, /* switchable */ + { 2, "no", NULL, NULL }, /* notSwitchable */ + { 0, NULL, NULL, NULL } +}; + +/* Overall outlets switchability info for the unit. + * This is refined per-outlet, depending on user configuration, + * possibly disabling switchability of some outlets */ +static info_lkp_t marlin_unit_switchability_info[] = { + { 0, "no", NULL, NULL }, /* unknown */ + { 1, "yes", NULL, NULL }, /* switched */ + { 2, "no", NULL, NULL }, /* advancedMonitored */ + { 3, "yes", NULL, NULL }, /* managed */ + { 4, "no", NULL, NULL }, /* monitored */ + { 0, NULL, NULL, NULL } +}; + +/* The physical type of outlet */ +static info_lkp_t marlin_outlet_type_info[] = { + { 0, "unknown", NULL, NULL }, + { 1, "iecC13", NULL, NULL }, + { 2, "iecC19", NULL, NULL }, + { 10, "uk", NULL, NULL }, + { 11, "french", NULL, NULL }, + { 12, "schuko", NULL, NULL }, + { 20, "nema515", NULL, NULL }, + { 21, "nema51520", NULL, NULL }, + { 22, "nema520", NULL, NULL }, + { 23, "nemaL520", NULL, NULL }, + { 24, "nemaL530", NULL, NULL }, + { 25, "nema615", NULL, NULL }, + { 26, "nema620", NULL, NULL }, + { 27, "nemaL620", NULL, NULL }, + { 28, "nemaL630", NULL, NULL }, + { 29, "nemaL715", NULL, NULL }, + { 30, "rf203p277", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_ambient_presence_info[] = { + { -1, "unknown", NULL, NULL }, + { 0, "no", NULL, NULL }, /* disconnected */ + { 1, "yes", NULL, NULL }, /* connected */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_emp002_ambient_presence_info[] = { + { 0, "unknown", NULL, NULL }, + { 2, "yes", NULL, NULL }, /* communicationOK */ + { 3, "no", NULL, NULL }, /* communicationLost */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_threshold_status_info[] = { + { 0, "good", NULL, NULL }, /* No threshold triggered */ + { 1, "warning-low", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "critical-low", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "warning-high", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "critical-high", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_threshold_frequency_status_info[] = { + { 0, "good", NULL, NULL }, /* No threshold triggered */ + { 1, "out-of-range", NULL, NULL }, /* Frequency out of range triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 0, "opened", NULL, NULL }, + { 1, "closed", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_threshold_voltage_alarms_info[] = { + { 0, "", NULL, NULL }, /* No threshold triggered */ + { 1, "low voltage warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "low voltage critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "high voltage warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "high voltage critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_threshold_current_alarms_info[] = { + { 0, "", NULL, NULL }, /* No threshold triggered */ + { 1, "low current warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "low current critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "high current warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "high current critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_threshold_frequency_alarm_info[] = { + { 0, "", NULL, NULL }, /* No threshold triggered */ + { 1, "frequency out of range!", NULL, NULL }, /* Frequency out of range triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_threshold_temperature_alarms_info[] = { + { 0, "", NULL, NULL }, /* No threshold triggered */ + { 1, "low temperature warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "low temperature critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "high temperature warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "high temperature critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_threshold_humidity_alarms_info[] = { + { 0, "", NULL, NULL }, /* No threshold triggered */ + { 1, "low humidity warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "low humidity critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "high humidity warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "high humidity critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_outlet_group_type_info[] = { + { 0, "unknown", NULL, NULL }, + { 1, "breaker1pole", NULL, NULL }, + { 2, "breaker2pole", NULL, NULL }, + { 3, "breaker3pole", NULL, NULL }, + { 4, "outlet-section", NULL, NULL }, + { 5, "user-defined", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_input_type_info[] = { + { 1, "1", NULL, NULL }, /* singlePhase */ + { 2, "2", NULL, NULL }, /* splitPhase */ + { 3, "3", NULL, NULL }, /* threePhaseDelta */ + { 4, "3", NULL, NULL }, /* threePhaseWye */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_outlet_group_phase_info[] = { + { 0, "unknown", NULL, NULL }, /* unknown */ + { 1, "1", NULL, NULL }, /* singlePhase */ + { 2, "1-N", NULL, NULL }, /* phase1toN */ + { 3, "2-N", NULL, NULL }, /* phase2toN */ + { 4, "3-N", NULL, NULL }, /* phase3toN */ + { 5, "1-2", NULL, NULL }, /* phase1to2 */ + { 6, "2-3", NULL, NULL }, /* phase2to3 */ + { 7, "3-1", NULL, NULL }, /* phase3to1 */ + { 0, NULL, NULL, NULL } +}; + +#if WITH_SNMP_LKP_FUN +/* Note: eaton_sensor_temperature_unit_fun() is defined in eaton-pdu-marlin-helpers.c + * and su_temperature_read_fun() is in snmp-ups.c + * Future work for DMF might provide same-named routines via LUA-C gateway. + */ + +#if WITH_SNMP_LKP_FUN_DUMMY +/* Temperature unit consideration */ +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "unknown"; +} +/* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ +const char *su_temperature_read_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "dummy"; +} +#endif // WITH_SNMP_LKP_FUN_DUMMY + +static info_lkp_t eaton_sensor_temperature_unit_info[] = { + { 0, "dummy", eaton_sensor_temperature_unit_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_sensor_temperature_read_info[] = { + { 0, "dummy", su_temperature_read_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +#else // if not WITH_SNMP_LKP_FUN: + +/* FIXME: For now, DMF codebase falls back to old implementation with static + * lookup/mapping tables for this, which can easily go into the DMF XML file. + */ +static info_lkp_t eaton_sensor_temperature_unit_info[] = { + { 0, "kelvin", NULL, NULL }, + { 1, "celsius", NULL, NULL }, + { 2, "fahrenheit", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +#endif // WITH_SNMP_LKP_FUN + +/* Extracted from powerware-mib.c ; try to commonalize */ +static info_lkp_t marlin_ambient_drycontacts_polarity_info[] = { + { 0, "normal-opened", NULL, NULL }, + { 1, "normal-closed", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t marlin_ambient_drycontacts_state_info[] = { + { 0, "inactive", NULL, NULL }, + { 1, "active", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +#if WITH_SNMP_LKP_FUN +/* Note: marlin_device_count_fun() is defined in eaton-pdu-marlin-helpers.c + * Future work for DMF might provide a same-named routine via LUA-C gateway. + */ + +# if WITH_SNMP_LKP_FUN_DUMMY +long marlin_device_count_fun(const char *daisy_dev_list) + { return 1; } +# endif /* WITH_SNMP_LKP_FUN_DUMMY */ + +static info_lkp_t marlin_device_count_info[] = { + { 1, "dummy", NULL, marlin_device_count_fun }, + { 0, NULL, NULL, NULL } +}; + +#else /* if not WITH_SNMP_LKP_FUN: */ + +/* FIXME: For now, DMF codebase falls back to old implementation with static + * lookup/mapping tables for this, which can easily go into the DMF XML file. + */ + +#endif /* WITH_SNMP_LKP_FUN */ + + +/* Snmp2NUT lookup table for Eaton Marlin MIB */ +static snmp_info_t eaton_marlin_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.1.0", + NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.4.0", + NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.2.1.1.6.0", + NULL, SU_FLAG_OK, NULL }, + + /* Device collection */ + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.model", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", + "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.4.%i", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.part", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.3.%i", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* For daisychain, there is only 1 physical interface! */ + { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.2.1.2.2.1.6.2", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + + /* Daisychained devices support */ + /* FIXME : Should this be a static value, or can we expect the amount of + * daisy-chained devices to change without restart of the driver by user? + * If this is a critical matter, should a detected change of amount of + * daisy-chained devices, outlet counts, etc. cause restart/reinit of + * this running driver instance? + */ +#if WITH_SNMP_LKP_FUN + /* Number of daisychained units is processed according to present units + * in the chain with new G3 firmware (02.00.0051, since autumn 2017): + * Take string "unitsPresent" (ex: "0,3,4,5"), and count the amount + * of "," separators+1 using an inline function */ + /* FIXME: inline func */ + { "device.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.1.0", + "0", SU_FLAG_STATIC | SU_FLAG_UNIQUE, + &marlin_device_count_info[0] /* devices_count */ }, +#endif + /* Notes: this older/fallback definition is used to: + * - estimate the number of devices, based on the below OID iteration capabilities + * - determine the base index of the SNMP OID (ie 0 or 1) */ + { "device.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", + "1", SU_FLAG_STATIC +#if WITH_SNMP_LKP_FUN + | SU_FLAG_UNIQUE +#endif + , NULL /* devices_count */ }, + + /* UPS collection */ + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "EATON", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, + "1.3.6.1.4.1.534.6.6.7.1.2.1.2.%i", + "Eaton Powerware ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + + /* FIXME: use unitName.0 (ePDU)? + * { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, */ + { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.4.%i", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* FIXME: this entry should be SU_FLAG_SEMI_STATIC */ + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.5.%i", + "", SU_FLAG_OK, NULL }, + { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, + NULL, + "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* FIXME: needs a date reformating callback + * 2011-8-29,16:27:25.0,+1:0 + * Hex-STRING: 07 DB 08 1D 10 0C 36 00 2B 01 00 00 + * { "ups.date", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.8.0", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + * { "ups.time", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.8.0", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + */ + + /* Input collection */ + /* Note: a larger ePDU can have several inputs. The "%i" iterators + * in key names are currently available for daisychain devs, outlets, + * and groups - but not for inputs. These would likely evolve later + * to "input.%i.something" with default (non-%i) same as .1 instance. + * At this time only a single-input (or first of several inputs) is + * supported by this mapping. + */ + /* Historically, some of these data were previously published as + * outlet.{realpower,...} + * However, it's more suitable and logic to have these on input.{...} + */ + /* Note: the below gives the number of input, not the number of phase(s)! */ + /* inputCount.0; Value (Integer): 1 + { "input.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.20.0", + NULL, SU_FLAG_STATIC, NULL }, */ + /* Note: for daisychain mode, we must handle phase(s) per device, + * not as a whole. In case of daisychain, support of the UNIQUE + * field is not yet implemented (FIXME) so the last resolved OID + * value wins. If a more-preferable OID is not implemented by device, + * this is ok - the previous available value remains in place. */ + /* inputType.%i.1 = INTEGER: singlePhase (1) */ + { "input.phases", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.2.%i.1", + NULL, SU_FLAG_STATIC, + &marlin_input_type_info[0] }, + + /* Frequency is measured globally */ + { "input.frequency", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", + NULL, 0, NULL }, + { "input.frequency.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.4.%i.1", + NULL, SU_FLAG_OK, + &marlin_threshold_frequency_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.4.%i.1", + NULL, SU_FLAG_OK, + &marlin_threshold_frequency_alarm_info[0] }, + + /* inputCurrentPercentLoad (measured globally) + * Current percent load, based on the rated current capacity */ + /* FIXME: input.load is mapped on input.L1.load for both single and 3phase !!! */ + { "input.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "input.L1.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "input.L2.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.2", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "input.L3.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.3", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + + /* FIXME: + * - Voltage is only measured per phase, as mV! + * so input.voltage == input.L1.voltage for both single and 3phase + * - As per NUT namespace (http://www.networkupstools.org/docs/developer-guide.chunked/apas01.html#_valid_contexts) + * Voltage has to be expressed either phase-phase or phase-neutral + * This is depending on OID inputVoltageMeasType + * INTEGER {singlePhase (1),phase1toN (2),phase2toN (3),phase3toN (4),phase1to2 (5),phase2to3 (6),phase3to1 (7) + * => RFC input.Lx.voltage.context */ + { "input.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", + NULL, 0, NULL }, + { "input.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, + { "input.voltage.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.voltage.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.6.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.voltage.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.7.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.voltage.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.8.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L1.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", + NULL, 0, NULL }, + { "input.L1.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, + { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.1", + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, + { "input.L1.voltage.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L1.voltage.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.6.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L1.voltage.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.7.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L1.voltage.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.8.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L2.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.2", + NULL, 0, NULL }, + { "input.L2.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.2", + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, + { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.2", + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, + { "input.L2.voltage.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L2.voltage.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.6.%i.1.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L2.voltage.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.7.%i.1.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L2.voltage.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.8.%i.1.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L3.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.3", + NULL, 0, NULL }, + { "input.L3.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.3", + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, + { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.4.%i.1.3", + NULL, SU_FLAG_OK, + &marlin_threshold_voltage_alarms_info[0] }, + { "input.L3.voltage.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.5.%i.1.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L3.voltage.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.6.%i.1.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L3.voltage.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.7.%i.1.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L3.voltage.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.8.%i.1.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* FIXME: + * - input.current is mapped on input.L1.current for both single and 3phase !!! */ + { "input.current", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.1", + NULL, 0, NULL }, + { "input.current.nominal", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.3.%i.1.1", + NULL, 0, NULL }, + { "input.current.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, + { "input.current.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.current.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.7.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.current.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.8.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.current.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L1.current", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.0.1.1", + NULL, 0, NULL }, + { "input.L1.current.nominal", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.3.%i.1.1", + NULL, 0, NULL }, + { "input.L1.current.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, + { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.1", + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, + { "input.L1.current.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L1.current.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.7.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L1.current.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.8.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L1.current.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.%i.1.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L2.current", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.2", + NULL, 0, NULL }, + { "input.L2.current.nominal", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.3.%i.1.2", + NULL, 0, NULL }, + { "input.L2.current.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.2", + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, + { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.2", + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, + { "input.L2.current.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L2.current.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.7.%i.1.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L2.current.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.8.%i.1.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L2.current.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.%i.1.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L3.current", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.3", + NULL, 0, NULL }, + { "input.L3.current.nominal", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.3.%i.1.3", + NULL, 0, NULL }, + { "input.L3.current.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.3", + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, + { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.5.%i.1.3", + NULL, SU_FLAG_OK, + &marlin_threshold_current_alarms_info[0] }, + { "input.L3.current.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.6.%i.1.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L3.current.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.7.%i.1.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L3.current.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.8.%i.1.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + { "input.L3.current.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.9.%i.1.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* Sum of all phases realpower, valid for Shark 1ph/3ph only */ + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.4.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, + /* Fallback 1: Sum of all phases realpower, valid for Marlin 3ph only */ + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, + /* Fallback 2: Sum of the phase realpower, valid for Marlin 1ph only */ + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "input.L1.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "input.L2.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.2", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "input.L3.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.3", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* Sum of all phases apparent power, valid for Shark 1ph/3ph only */ + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.3.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, + /* Fallback 1: Sum of all phases realpower, valid for Marlin 3ph only */ + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, + /* Fallback 2: Sum of the phase realpower, valid for Marlin 1ph only */ + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "input.L1.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "input.L2.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.2", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "input.L3.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.3", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + + /* Input feed: a feed (A or B) is tied to an input, and this + * sub-collection describes the properties of an actual cable. + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags */ + /* { "input.feed.%i.id", 0, 1, "???.%i.%i", NULL, SU_FLAG_NEGINVALID, NULL }, */ + /* Feed name(s) of the ePDU power input(s), can be set by user (FIXME: rename to .desc?) + * inputFeedName.0.1 = Value (OctetString): Feed A + */ + /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => refreshed from time to time or upon call to setvar */ +/* { "input.%i.feed.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + * ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.%i", + * NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + */ + { "input.feed.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.10.%i.1", + NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + /* Feed color (integer RGB) + * inputFeedColor.0.1 = Gauge32: 0 (black) + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream */ +/* { "input.%i.feed.color", 0, 1, + * ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.%i", + * NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + */ + { "input.feed.color", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.9.%i.1", + NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_TYPE_DAISY_1, NULL }, + /* inputPowerCapacity.0.1 = INTEGER: 2300 */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream */ + { "input.realpower.nominal", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.5.1.9.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + + /* Ambient collection */ + /* EMP001 (legacy) mapping */ + /* Note: this is still published, beside from the new daisychained version! */ + { "ambient.present", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.3.%i.1", + NULL, SU_FLAG_OK, + &marlin_ambient_presence_info[0] }, + { "ambient.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.5.%i.1", + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.5.%i.1", + NULL, SU_FLAG_OK, + &marlin_threshold_temperature_alarms_info[0] }, + { "ambient.temperature", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.4.%i.1", + NULL, SU_FLAG_OK, NULL }, + /* Low and high threshold use the respective critical levels */ + { "ambient.temperature.low", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.7.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.temperature.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.7.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.temperature.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.6.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.temperature.high", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.9.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.temperature.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.8.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.temperature.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.1.1.9.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.7.2.1.5.%i.1", + NULL, SU_FLAG_OK, + &marlin_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.7.2.1.5.%i.1", + NULL, SU_FLAG_OK, + &marlin_threshold_humidity_alarms_info[0] }, + { "ambient.humidity", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.2.1.4.%i.1", + NULL, SU_FLAG_OK, NULL }, + /* Low and high threshold use the respective critical levels */ + { "ambient.humidity.low", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.2.1.7.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.humidity.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.2.1.6.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.humidity.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.2.1.7.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.humidity.high", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.2.1.9.%i.1", NULL, + SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.humidity.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.2.1.8.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.humidity.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.6.7.7.2.1.9.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* Dry contacts on TH module */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.7.3.1.4.%i.1", + NULL, SU_FLAG_OK, + &marlin_ambient_drycontacts_info[0] }, + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.7.3.1.4.%i.2", + NULL, SU_FLAG_OK, + &marlin_ambient_drycontacts_info[0] }, + + + /* EMP002 (EATON EMP MIB) mapping, including daisychain support */ + /* Warning: indexes start at '1' not '0'! */ + /* sensorCount.0 */ + { "ambient.count", ST_FLAG_RW, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.1.0", + "0", SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* CommunicationStatus.n */ + { "ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_emp002_ambient_presence_info[0] }, + /* sensorName.n: OctetString EMPDT1H1C2 @1 */ + { "ambient.%i.name", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorManufacturer.n */ + { "ambient.%i.mfr", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorModel.n */ + { "ambient.%i.model", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorSerialNumber.n */ + { "ambient.%i.serial", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorUuid.n */ + { "ambient.%i.id", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorAddress.n */ + { "ambient.%i.address", 0, 1, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorMonitoredBy.n */ + { "ambient.%i.parent.serial", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.5.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* sensorFirmwareVersion.n */ + { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* temperatureUnit.1 + * MUST be before the temperature data reading! */ + { "ambient.%i.temperature.unit", 0, 1.0, + ".1.3.6.1.4.1.534.6.8.1.2.5.0", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &eaton_sensor_temperature_unit_info[0] }, + /* temperatureValue.n.1 */ + { "ambient.%i.temperature", 0, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, +#if WITH_SNMP_LKP_FUN + &eaton_sensor_temperature_read_info[0] +#else + NULL +#endif + }, + { "ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_temperature_alarms_info[0] }, + /* FIXME: ambient.n.temperature.{minimum,maximum} */ + /* temperatureThresholdLowCritical.n.1 */ + { "ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* temperatureThresholdLowWarning.n.1 */ + { "ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* temperatureThresholdHighWarning.n.1 */ + { "ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* temperatureThresholdHighCritical.n.1 */ + { "ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* humidityValue.n.1 */ + { "ambient.%i.humidity", 0, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_threshold_humidity_alarms_info[0] }, + /* FIXME: consider ambient.n.humidity.{minimum,maximum} */ + /* humidityThresholdLowCritical.n.1 */ + { "ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* humidityThresholdLowWarning.n.1 */ + { "ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* humidityThresholdHighWarning.n.1 */ + { "ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* humidityThresholdHighCritical.n.1 */ + { "ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* digitalInputName.n.{1,2} */ + { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, NULL }, + /* digitalInputPolarity.n */ + { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_polarity_info[0] }, + /* XUPS-MIB::xupsContactState.n */ + { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, + ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", + "", SU_AMBIENT_TEMPLATE | SU_TYPE_DAISY_MASTER_ONLY, + &marlin_ambient_drycontacts_state_info[0] }, + + /* Outlet collection */ + { "outlet.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.22.%i", + "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "outlet.id", 0, 1, + NULL, + "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, + NULL, + "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* UnitType + * used to depict the overall outlets switchability of the unit on G3 and newer ePDU*/ + { "outlet.switchable", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.10.%i", + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE, + &marlin_unit_switchability_info[0] }, + /* Ugly hack for older G2 ePDU: check the first outlet to determine unit switchability */ + { "outlet.switchable", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.1", + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_TYPE_DAISY_1, + &g2_unit_outlet_switchability_info[0] }, + /* The below ones are the same as the input.* equivalent */ + /* FIXME: transition period, TO BE REMOVED, moved to input.* */ + { "outlet.frequency", 0, 0.1, + ".1.3.6.1.4.1.534.6.6.7.3.1.1.3.%i.1", + NULL, 0, NULL }, + { "outlet.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.3.2.1.3.%i.1.1", + NULL, 0, NULL }, + { "outlet.current", 0, 0.01, + ".1.3.6.1.4.1.534.6.6.7.3.3.1.4.%i.1.1", + NULL, 0, NULL }, + { "outlet.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.4.%i.1.4", + NULL, 0, NULL }, + { "outlet.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.3.4.1.3.%i.1.4", + NULL, 0, NULL }, + + /* outlet template definition + * Indexes start from 1, ie outlet.1 => .1 */ + /* Note: the first definition is used to determine the base index (ie 0 or 1) */ + /* Outlet friendly name, which can be modified by the user + * outletName: = OctetString: "Outlet A16" + */ + /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => + * refreshed from time to time or upon call to setvar */ + { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.1.1.3.%i.%i", + NULL, SU_FLAG_SEMI_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + NULL }, + { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.2.%i.%i", + NULL, SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_outlet_status_info[0] }, + + /* Numeric identifier of the outlet, tied to the whole unit */ + /* NOTE: For daisychain devices ATM the last listed value presented by + * the SNMP device is kept by the driver - no SU_FLAG_UNIQUE here yet. + * Verified that a non-implemented OID does not publish empty values. */ + /* Fallback in firmwares issued before Sep 2017 is to use the + * outletID: Outlet physical name, related to its number in the group + * ex: first outlet of the second group (B) is B1, or can default to + * the outlet number (represented as string) and is a read-only string + * outletID.0.8 = Value (OctetString): "8" + */ + { "outlet.%i.id", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, + NULL }, + + /* Fallback in firmwares issued before Sep 2017 (outletID): */ + { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.1.1.2.%i.%i", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + NULL }, + /* Preferred: Outlet physical name OID in new G3 firmware (02.00.0051) + * is named outletDesignator (other MIBs outletPhysicalName) + * and is a read-only string provided by firmware + * outletDesignator.0.1 = Value (OctetString): "A1" + * outletPhysicalName.0.16 = Value (OctetString): "A16" + */ + { "outlet.%i.name", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.1.1.6.%i.%i", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, + NULL }, + + /* FIXME: the last part of the OID gives the group number (i.e. %i.1 means "group 1") + * Need to address that, without multiple declaration (%i.%i, SU_OUTLET | SU_OUTLET_GROUP)? */ + { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.1", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.2", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.3", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.4", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.5", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.6", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.current", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.4.1.3.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.4.1.4.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_status_info[0] }, + { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.4.1.4.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_current_alarms_info[0] }, + { "outlet.%i.current.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.4.1.5.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.current.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.4.1.6.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.current.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.4.1.7.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.current.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.4.1.8.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.6.5.1.3.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.3.1.2.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.3.1.3.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_status_info[0] }, + { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.3.1.3.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_threshold_voltage_alarms_info[0] }, + { "outlet.%i.voltage.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.3.1.4.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.voltage.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.3.1.5.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.voltage.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.3.1.6.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.voltage.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.6.3.1.7.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.6.5.1.2.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* outletControlSwitchable */ + { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.9.%i.%i", + "no", SU_OUTLET | SU_FLAG_UNIQUE | SU_TYPE_DAISY_1, + &marlin_outlet_switchability_info[0] }, + /* FIXME: handle non switchable units (only measurements), which do not expose this OID */ + { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + "no", SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, + &g2_unit_outlet_switchability_info[0] }, + { "outlet.%i.type", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.1.1.5.%i.%i", + "unknown", SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, + &marlin_outlet_type_info[0] }, + + /* TODO: handle statistics + * outletWh.0.1 + * outletWhTimer.0.1 + */ + + /* Outlet groups collection */ + { "outlet.group.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.21.%i", + "0", SU_FLAG_STATIC | SU_TYPE_DAISY_1, NULL }, + /* outlet groups template definition + * Indexes start from 1, ie outlet.group.1 => .1 */ + /* Note: the first definition is used to determine the base index (ie 0 or 1) */ + /* groupID.0.1 = OctetString: A */ + { "outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.2.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* User-friendly (writeable) description of the outlet group: + * groupName.0.1 = OctetString: Factory Group 1 + * groupName.0.2 = OctetString: Branch Circuit B + */ + /* FIXME: SU_FLAG_SEMI_STATIC or SU_FLAG_SETTING => + * refreshed from time to time or upon call to setvar */ + { "outlet.group.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.3.%i.%i", + NULL, SU_FLAG_SEMI_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, + /* Outlet-group physical name, a read-only string, + * is named groupDesignator (other MIBs groupPhysicalName) + * groupPhysicalName.0.1 = Value (OctetString): A + * groupDesignator.0.2 = Value (OctetString): B + */ + { "outlet.group.%i.name", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.8.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, + /* Outlet-group color: groupColor (other MIBs groupBkgColor) + * groupColor.0.1 = Value (Gauge32): 16051527 (0xF4ED47) + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream */ + { "outlet.group.%i.color", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.7.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, + /* groupType.0.1 = Integer: outletSection (4) */ + { "outlet.group.%i.type", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.4.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &marlin_outlet_group_type_info[0] }, + /* Phase to which an outlet-group is connected: + * We use the following OID, which gives the voltage measurement type + * groupVoltageMeasType.0.1; Value (Integer): singlePhase (1) + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream ; check type (number? string?) and flags (daisy?) */ + { "outlet.group.%i.phase", 0, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.3.1.2.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &marlin_outlet_group_phase_info[0] }, + /* groupControlStatus.0.1 = Integer: on (1) */ + { "outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.6.1.2.%i.%i", + NULL, SU_FLAG_OK | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &marlin_outletgroups_status_info[0] }, + /* groupChildCount.0.1 = Integer: 12 */ + { "outlet.group.%i.count", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.6.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupVoltage.0.1 = Integer: 243080 */ + { "outlet.group.%i.voltage", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.5.3.1.3.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupVoltageThStatus.0.1 = Integer: good (0) */ + { "outlet.group.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.3.1.4.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &marlin_threshold_status_info[0] }, + { "outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.3.1.4.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &marlin_threshold_voltage_alarms_info[0] }, + { "outlet.group.%i.voltage.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.5.3.1.5.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + { "outlet.group.%i.voltage.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.5.3.1.6.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + { "outlet.group.%i.voltage.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.5.3.1.7.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + { "outlet.group.%i.voltage.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.5.3.1.8.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupCurrent.0.1 = Integer: 0 */ + { "outlet.group.%i.current", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.5.4.1.3.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupCurrentCapacity.0.1 = Integer: 16000 */ + { "outlet.group.%i.current.nominal", 0, 0.001, + ".1.3.6.1.4.1.534.6.6.7.5.4.1.2.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupCurrentThStatus.0.1 = Integer: good (0) */ + { "outlet.group.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.4.1.4.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &marlin_threshold_status_info[0] }, + { "outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.5.4.1.4.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &marlin_threshold_current_alarms_info[0] }, + /* groupCurrentPercentLoad.0.1 = Integer: 0 */ + { "outlet.group.%i.load", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.5.4.1.10.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupCurrentThLowerWarning.0.1 = Integer: 0 */ + { "outlet.group.%i.current.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.5.4.1.5.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupCurrentThLowerCritical.0.1 = Integer: -1 */ + { "outlet.group.%i.current.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.5.4.1.6.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupCurrentThUpperWarning.0.1 = Integer: 12800 */ + { "outlet.group.%i.current.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.5.4.1.7.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupCurrentThUpperCritical.0.1 = Integer: 16000 */ + { "outlet.group.%i.current.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.534.6.6.7.5.4.1.8.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupWatts.0.1 = Integer: 2670 */ + { "outlet.group.%i.realpower", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.5.5.1.3.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupVA.0.1 = Integer: 3132 */ + { "outlet.group.%i.power", 0, 1.0, + ".1.3.6.1.4.1.534.6.6.7.5.5.1.2.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, + /* Input to which an outlet-group is connected + * groupInputIndex.0.1 = Integer: 1 + */ + /* FIXME: RFC on key name is needed when backporting to NUT upstream */ + { "outlet.group.%i.input", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.5.1.1.9.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + NULL }, + + /* instant commands. */ + /* Notes: + * - load.cycle might be replaced by / mapped on shutdown.reboot + * - outletControl{Off,On,Reboot}Cmd values: + * 0-n : Timer + * -1 : Cancel + * we currently use "0", so instant On | Off | Reboot... */ + /* no counterpart found! + { "outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", + NULL, SU_TYPE_CMD, NULL }, + { "outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", + NULL, SU_TYPE_CMD, NULL }, + { "outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", + NULL, SU_TYPE_CMD, NULL }, */ + + /* Delays handling: + * 0-n :Time in seconds until the group command is issued + * -1:Cancel a pending group-level Off/On/Reboot command */ + { "outlet.%i.load.off", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.load.on", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", + "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.load.cycle", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", + "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + + /* Per-outlet shutdown / startup delay (configuration point, not the timers) + * outletControlShutoffDelay.0.3 = INTEGER: 120 + * outletControlSequenceDelay.0.8 = INTEGER: 8 + * (by default each output socket startup is delayed by its number in seconds) + */ + { "outlet.%i.delay.shutdown", ST_FLAG_RW, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.10.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.delay.start", ST_FLAG_RW, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.7.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + { "outlet.%i.load.off.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.load.on.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.load.cycle.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.5.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + + /* Per-outlet shutdown / startup timers + * outletControlOffCmd.0.1 = INTEGER: -1 + * outletControlOnCmd.0.1 = INTEGER: -1 + */ + { "outlet.%i.timer.shutdown", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.3.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.timer.start", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.6.6.1.4.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + + /* Delays handling: + * 0-n :Time in seconds until the group command is issued + * -1:Cancel a pending group-level Off/On/Reboot command */ + /* groupControlOffCmd.0.1 = Integer: -1 */ + { "outlet.group.%i.load.off", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.5.6.1.3.%i.%i", + "0", SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupControl0nCmd.0.1 = Integer: -1 */ + { "outlet.group.%i.load.on", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.5.6.1.4.%i.%i", + "0", SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupControlRebootCmd.0.1 = Integer: -1 */ + { "outlet.group.%i.load.cycle", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.5.6.1.5.%i.%i", + "0", SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + { "outlet.group.%i.load.off.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.5.6.1.3.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupControl0nCmd.0.1 = Integer: -1 */ + { "outlet.group.%i.load.on.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.5.6.1.4.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* groupControlRebootCmd.0.1 = Integer: -1 */ + { "outlet.group.%i.load.cycle.delay", 0, 1, + ".1.3.6.1.4.1.534.6.6.7.5.6.1.5.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + + +mib2nut_info_t eaton_marlin = { "eaton_epdu", EATON_MARLIN_MIB_VERSION, NULL, EATON_MARLIN_OID_MODEL_NAME, eaton_marlin_mib, EATON_MARLIN_SYSOID, NULL }; diff --git a/drivers/eaton-pdu-marlin-mib.h b/drivers/eaton-pdu-marlin-mib.h new file mode 100644 index 0000000..9014bb7 --- /dev/null +++ b/drivers/eaton-pdu-marlin-mib.h @@ -0,0 +1,32 @@ +/* eaton-pdu-marlin-mib.h - subdriver to monitor Eaton ePDU SNMP devices with NUT + * + * Copyright (C) + * 2010 Arjen de Korte + * 2011 - 2012 Arnaud Quette + * 2017 Arnaud Quette + * 2017 Jim Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EATON_EPDU_MARLIN_MIB_H +#define EATON_EPDU_MARLIN_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t eaton_marlin; + +#endif /* EATON_EPDU_MARLIN_MIB_H */ diff --git a/drivers/eaton-pdu-pulizzi-mib.c b/drivers/eaton-pdu-pulizzi-mib.c new file mode 100644 index 0000000..65de73e --- /dev/null +++ b/drivers/eaton-pdu-pulizzi-mib.c @@ -0,0 +1,142 @@ +/* eaton-pdu-pulizzi-mib.c - data to monitor Eaton ePDUs branded as: + * G1 Pulizzi Monitored and Switched ePDUs + * + * Copyright (C) 2008 - 2017 + * Arnaud Quette + * Arnaud Quette + * Copyright (C) 2015 - 2017 + * Jim Klimov + * + * Supported by Eaton + * and previously MGE Office Protection Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "eaton-pdu-pulizzi-mib.h" + +/* Pulizzi Monitored ePDU (Basic model, SNMP only) + * FIXME: to be completed + * + * Warning: there are 2 versions: + * - SA built MI.mib (old MIB) + * #define PULIZZI1_OID_MIB ".1.3.6.1.4.1.20677.3.1.1" + * #define PULIZZI1_OID_MODEL_NAME ".1.3.6.1.4.1.20677.3.1.1.1.2.0" + * - Eaton-Powerware-Monitored-ePDU_1.0.E.mib (new MIB) Vertical SW + */ + + +/* Pulizzi Switched ePDU */ + +#define EATON_PULIZZI_SW_MIB_VERSION "0.5" + +#define PULIZZI_SW_OID_MIB ".1.3.6.1.4.1.20677.3.1.1" +#define PULIZZI_SW_OID_MODEL_NAME ".1.3.6.1.4.1.20677.2.1.1.0" + +/* Some buggy FW also report sysOID = ".1.3.6.1.4.1.20677.1" */ +#define EATON_PULIZZI_SWITCHED1_SYSOID ".1.3.6.1.4.1.20677.1" +#define EATON_PULIZZI_SWITCHED2_SYSOID ".1.3.6.1.4.1.20677.2" + + +static info_lkp_t pulizzi_sw_outlet_status_info[] = { + { 1, "on", NULL, NULL }, + { 2, "off", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* simply remap the above status to "yes" */ +static info_lkp_t pulizzi_sw_outlet_switchability_info[] = { + { 1, "yes", NULL, NULL }, + { 2, "yes", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* Snmp2NUT lookup table for Eaton Pulizzi Switched ePDU MIB */ +static snmp_info_t eaton_pulizzi_switched_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* Device page */ + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.model", ST_FLAG_STRING, SU_INFOSIZE, PULIZZI_SW_OID_MODEL_NAME, + "Switched ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.2.6.0", + "unknown", 0, NULL }, + + /* UPS page */ + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, PULIZZI_SW_OID_MODEL_NAME, + "Switched ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* FIXME: to be moved to the device collection! */ + { "ups.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.1.4.0", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.time", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.1.3.0", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + + /* Outlet page */ + /* Note: outlet.count is deduced, with guestimate_outlet_count() */ + { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + + { "outlet.current", 0, 1.0, ".1.3.6.1.4.1.20677.2.8.6.4.2.0", NULL, 0, NULL }, + { "outlet.voltage", 0, 1.0, ".1.3.6.1.4.1.20677.2.8.6.4.1.0", NULL, 0, NULL }, + { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.20677.2.8.6.4.3.0", NULL, 0, NULL }, + + /* outlet template definition + * Notes: + * - indexes start from 1, ie outlet.1 => .1 + * - the first definition is used to determine the base index (ie 0 or 1) + * - outlet.count is estimated, based on the below OID iteration capabilities */ + { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.6.1.%i.1.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, NULL }, + { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.6.3.%i.0", + NULL, SU_FLAG_OK | SU_OUTLET, &pulizzi_sw_outlet_status_info[0] }, + { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL }, + /* we use the same OID as outlet.n.status..., to expose switchability */ + { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.20677.2.6.3.%i.0", "yes", SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET, &pulizzi_sw_outlet_switchability_info[0] }, + /* FIXME: need to be added to the namespace! */ + { "outlet.%i.delay.reboot", ST_FLAG_RW, 1, ".1.3.6.1.4.1.20677.2.6.1.%i.5.0", NULL, SU_OUTLET, NULL }, + /* "outlet1SequenceTime" is used for global sequence */ + { "outlet.%i.delay.start", ST_FLAG_RW, 1, ".1.3.6.1.4.1.20677.2.6.1.%i.4.0", NULL, SU_OUTLET, NULL }, + + /* instant commands. */ + /* FIXME: not exposed as "outlet.load...", or otherwise specific processing applies (template instanciation) */ + { "load.on", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", "1", SU_TYPE_CMD, NULL }, + { "load.off", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", "2", SU_TYPE_CMD, NULL }, + { "load.on.delay", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", "3", SU_TYPE_CMD, NULL }, + { "load.off.delay", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.1.0", "4", SU_TYPE_CMD, NULL }, + + /* WARNING: outlet 1 => index 2, so SU_CMD_OFFSET! */ + { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.%i.0", "1", SU_TYPE_CMD | SU_OUTLET | SU_CMD_OFFSET, NULL }, + { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.%i.0", "2", SU_TYPE_CMD | SU_OUTLET | SU_CMD_OFFSET, NULL }, + { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.20677.2.6.2.%i.0", "3", SU_TYPE_CMD | SU_OUTLET | SU_CMD_OFFSET, NULL }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + + +/*mib2nut_info_t pulizzi_monitored = { "pulizzi_monitored", EATON_PULIZZI_MIB_VERSION, NULL, PULIZZI1_OID_MODEL_NAME, eaton_pulizzi_monitored_mib, PULIZZI1_OID_MIB };*/ +mib2nut_info_t pulizzi_switched1 = { "pulizzi_switched1", EATON_PULIZZI_SW_MIB_VERSION, NULL, EATON_PULIZZI_SWITCHED1_SYSOID, eaton_pulizzi_switched_mib, EATON_PULIZZI_SWITCHED1_SYSOID, NULL }; +mib2nut_info_t pulizzi_switched2 = { "pulizzi_switched2", EATON_PULIZZI_SW_MIB_VERSION, NULL, EATON_PULIZZI_SWITCHED1_SYSOID, eaton_pulizzi_switched_mib, EATON_PULIZZI_SWITCHED2_SYSOID, NULL }; diff --git a/drivers/eaton-pdu-pulizzi-mib.h b/drivers/eaton-pdu-pulizzi-mib.h new file mode 100644 index 0000000..5c08fb6 --- /dev/null +++ b/drivers/eaton-pdu-pulizzi-mib.h @@ -0,0 +1,33 @@ +/* eaton-pdu-pulizzi-mib.h - subdriver to monitor Eaton ePDU SNMP devices with NUT + * + * Copyright (C) + * 2010 Arjen de Korte + * 2011 - 2012 Arnaud Quette + * 2017 Arnaud Quette + * 2017 Jim Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EATON_EPDU_PULIZZI_MIB_H +#define EATON_EPDU_PULIZZI_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t pulizzi_switched1; +extern mib2nut_info_t pulizzi_switched2; + +#endif /* EATON_EPDU_PULIZZI_MIB_H */ diff --git a/drivers/eaton-pdu-revelation-mib.c b/drivers/eaton-pdu-revelation-mib.c new file mode 100644 index 0000000..8c1cd83 --- /dev/null +++ b/drivers/eaton-pdu-revelation-mib.c @@ -0,0 +1,182 @@ +/* eaton-pdu-revelation-mib.c - data to monitor Eaton ePDUs branded as: + * G1 Aphel based ePDUs (Complex) - Revelation + * + * Copyright (C) 2008 - 2017 + * Arnaud Quette + * Arnaud Quette + * Copyright (C) 2015 - 2017 + * Jim Klimov + * + * Supported by Eaton + * and previously MGE Office Protection Systems + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "eaton-pdu-revelation-mib.h" + +#define EATON_APHEL_REVELATION_MIB_VERSION "0.52" + +/* APHEL PDU-MIB - Revelation MIB (Managed ePDU) + * ********************************************* */ + +#define AR_BASE_OID ".1.3.6.1.4.1.534.6.6.6" +#define APHEL2_SYSOID AR_BASE_OID +#define APHEL2_OID_MODEL_NAME AR_OID_MODEL_NAME + +#define AR_OID_MODEL_NAME AR_BASE_OID ".1.1.12.0" +#define AR_OID_DEVICE_NAME AR_BASE_OID ".1.1.13.0" +#define AR_OID_FIRMREV AR_BASE_OID ".1.1.1.0" +#define AR_OID_SERIAL AR_BASE_OID ".1.1.2.0" +#define AR_OID_UNIT_MACADDR AR_BASE_OID ".1.1.6.0" + +#define AR_OID_UNIT_CURRENT AR_BASE_OID ".1.3.1.1" +#define AR_OID_UNIT_VOLTAGE AR_BASE_OID ".1.3.1.2" +#define AR_OID_UNIT_ACTIVEPOWER AR_BASE_OID ".1.3.1.3" +#define AR_OID_UNIT_APPARENTPOWER AR_BASE_OID ".1.3.1.4" +#define AR_OID_UNIT_CPUTEMPERATURE AR_BASE_OID ".1.3.1.5.0" + +#define AR_OID_OUTLET_INDEX AR_BASE_OID ".1.2.2.1.1" +#define AR_OID_OUTLET_NAME AR_BASE_OID ".1.2.2.1.2" +#define AR_OID_OUTLET_STATUS AR_BASE_OID ".1.2.2.1.3" + +static info_lkp_t revelation_outlet_status_info[] = { + { -1, "error", NULL, NULL }, + { 0, "off", NULL, NULL }, + { 1, "on", NULL, NULL }, + { 2, "cycling", NULL, NULL }, /* transitional status */ + { 0, NULL, NULL, NULL } +}; + +/* Ugly hack: having the matching OID present means that the outlet is + * switchable. So, it should not require this value lookup */ +static info_lkp_t revelation_outlet_switchability_info[] = { + { -1, "yes", NULL, NULL }, + { 0, "yes", NULL, NULL }, + { 1, "yes", NULL, NULL }, + { 2, "yes", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +#define DO_OFF "0" +#define DO_ON "1" +#define DO_CYCLE "2" + +#define AR_OID_OUTLET_COUNT AR_BASE_OID ".1.2.1.0" +#define AR_OID_OUTLET_CURRENT AR_BASE_OID ".1.2.2.1.4" +#define AR_OID_OUTLET_MAXCURRENT AR_BASE_OID ".1.2.2.1.5" +#define AR_OID_OUTLET_VOLTAGE AR_BASE_OID ".1.2.2.1.6" +#define AR_OID_OUTLET_ACTIVEPOWER AR_BASE_OID ".1.2.2.1.7" +#define AR_OID_OUTLET_APPARENTPOWER AR_BASE_OID ".1.2.2.1.8" +#define AR_OID_OUTLET_POWERFACTOR AR_BASE_OID ".1.2.2.1.9" + +/* Snmp2NUT lookup table for Eaton Revelation MIB */ +static snmp_info_t eaton_aphel_revelation_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* Device collection */ + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.model", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_MODEL_NAME, + "Eaton Powerware ePDU Managed", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_SERIAL, "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_UNIT_MACADDR, "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* hardwareRev.0 = Integer: 26 */ + /* FIXME: not compliant! to be RFC'ed */ + { "device.revision", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.6.6.1.1.7.0", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + + /* UPS collection */ + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "EATON | Powerware", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_MODEL_NAME, + "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_SERIAL, "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_FIRMREV, "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.temperature", 0, 1, AR_OID_UNIT_CPUTEMPERATURE, NULL, 0, NULL }, + + /* Outlet collection */ + { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "outlet.count", 0, 1, AR_OID_OUTLET_COUNT, "0", 0, NULL }, + { "outlet.current", 0, 0.001, AR_OID_UNIT_CURRENT ".0", NULL, 0, NULL }, + { "outlet.voltage", 0, 0.001, AR_OID_UNIT_VOLTAGE ".0", NULL, 0, NULL }, + { "outlet.realpower", 0, 1.0, AR_OID_UNIT_ACTIVEPOWER ".0", NULL, 0, NULL }, + { "outlet.power", 0, 1.0, AR_OID_UNIT_APPARENTPOWER ".0", NULL, 0, NULL }, + + /* outlet template definition + * Caution: the index of the data start at 0, while the name is +1 + * ie outlet.1 => .0 */ + { "outlet.%i.switchable", 0, 1, AR_OID_OUTLET_STATUS ".%i", "yes", SU_FLAG_STATIC | SU_OUTLET, &revelation_outlet_switchability_info[0] }, + { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL }, + { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_NAME ".%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_OUTLET_STATUS ".%i", NULL, SU_FLAG_OK | SU_OUTLET, &revelation_outlet_status_info[0] }, + { "outlet.%i.current", 0, 0.001, AR_OID_OUTLET_CURRENT ".%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.current.maximum", 0, 0.001, AR_OID_OUTLET_MAXCURRENT ".%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.realpower", 0, 1.0, AR_OID_OUTLET_ACTIVEPOWER ".%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.voltage", 0, 1.0, AR_OID_OUTLET_VOLTAGE ".%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.powerfactor", 0, 0.01, AR_OID_OUTLET_POWERFACTOR ".%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.power", 0, 1.0, AR_OID_OUTLET_APPARENTPOWER ".%i", NULL, SU_OUTLET, NULL }, + + /* FIXME: + * - delay for startup/shutdown sequence + * - support for multiple Ambient sensors ( max. 8), starting at index '0' + * ambient.%i.temperature => .1.3.6.1.4.1.534.6.6.6.2.2.1.3.%i + * ambient.%i.humidity => .1.3.6.1.4.1.534.6.6.6.2.4.1.3.%i + */ + + /* Ambient collection */ + /* We use critical levels, for both temperature and humidity, + * since warning levels are also available! */ + { "ambient.temperature", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.2.1.3.0", NULL, SU_FLAG_OK, NULL }, + { "ambient.temperature.low", 0, 1.0, "1.3.6.1.4.1.534.6.6.6.2.2.1.6.0", NULL, SU_FLAG_OK, NULL }, + { "ambient.temperature.high", 0, 1.0, "1.3.6.1.4.1.534.6.6.6.2.2.1.7.0", NULL, SU_FLAG_OK, NULL }, + { "ambient.humidity", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.3.0", NULL, SU_FLAG_OK, NULL }, + { "ambient.humidity.low", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.6.0", NULL, SU_FLAG_OK, NULL }, + { "ambient.humidity.high", 0, 1.0, ".1.3.6.1.4.1.534.6.6.6.2.4.1.7.0", NULL, SU_FLAG_OK, NULL }, + + /* instant commands. */ + /* Note that load.cycle might be replaced by / mapped on shutdown.reboot */ + /* no counterpart found! + { "outlet.load.off", 0, DO_OFF, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL }, + { "outlet.load.on", 0, DO_ON, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL }, + { "outlet.load.cycle", 0, DO_CYCLE, AR_OID_OUTLET_STATUS ".0", NULL, SU_TYPE_CMD, NULL }, */ + { "outlet.%i.load.off", 0, 1, AR_OID_OUTLET_STATUS ".%i", DO_OFF, SU_TYPE_CMD | SU_OUTLET, NULL }, + { "outlet.%i.load.on", 0, 1, AR_OID_OUTLET_STATUS ".%i", DO_ON, SU_TYPE_CMD | SU_OUTLET, NULL }, + { "outlet.%i.load.cycle", 0, 1, AR_OID_OUTLET_STATUS ".%i", DO_CYCLE, SU_TYPE_CMD | SU_OUTLET, NULL }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + + +mib2nut_info_t aphel_revelation = { "aphel_revelation", EATON_APHEL_REVELATION_MIB_VERSION, NULL, APHEL2_OID_MODEL_NAME, eaton_aphel_revelation_mib, APHEL2_SYSOID, NULL }; diff --git a/drivers/eaton-pdu-revelation-mib.h b/drivers/eaton-pdu-revelation-mib.h new file mode 100644 index 0000000..3cd2645 --- /dev/null +++ b/drivers/eaton-pdu-revelation-mib.h @@ -0,0 +1,32 @@ +/* eaton-pdu-revelation-mib.h - subdriver to monitor Eaton ePDU SNMP devices with NUT + * + * Copyright (C) + * 2010 Arjen de Korte + * 2011 - 2012 Arnaud Quette + * 2017 Arnaud Quette + * 2017 Jim Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EATON_EPDU_REVELATION_MIB_H +#define EATON_EPDU_REVELATION_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t aphel_revelation; + +#endif /* EATON_EPDU_REVELATION_MIB_H */ diff --git a/drivers/emerson-avocent-pdu-mib.c b/drivers/emerson-avocent-pdu-mib.c new file mode 100644 index 0000000..cffe555 --- /dev/null +++ b/drivers/emerson-avocent-pdu-mib.c @@ -0,0 +1,191 @@ +/* emerson-avocent-pdu-mib.c - subdriver to monitor Emerson Avocent PDUs with NUT + * + * Copyright (C) + * 2008-2018 Arnaud Quette + * 2009 Opengear + * 2017-2019 Eaton (Arnaud Quette ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#define OPENGEAR_MULTIPLE_BANKS 1 + +#include "emerson-avocent-pdu-mib.h" + +#define EMERSON_AVOCENT_MIB_VERSION "1.3" +#define EMERSON_AVOCENT_SYSOID ".1.3.6.1.4.1.10418.17.1.7" +#define EMERSON_AVOCENT_OID_MODEL_NAME ".1.3.6.1.4.1.10418.17.2.1.2.0" + +/* FIXME: Avocent PM's seem to have 3 temperature sensors (index 1, 2, 3) + * for the embedded temperature (equivalent to ups.temperature) */ +#define AVOCENT_OID_UNIT_TEMPERATURE ".1.3.6.1.4.1.10418.17.2.5.3.1.17.1.1" + +/* Same as above for humidity... */ +#define AVOCENT_OID_UNIT_HUMIDITY ".1.3.6.1.4.1.10418.17.2.5.3.1.24.1" + +#define AVOCENT_OID_OUTLET_COUNT ".1.3.6.1.4.1.10418.17.2.5.3.1.8.%i.%i" + +/* FIXME: This is actually pmPowerMgmtPDUTableCurrent1Value */ +#define AVOCENT_OID_UNIT_CURRENT ".1.3.6.1.4.1.10418.17.2.5.3.1.10.1.1" +/* FIXME: This is actually pmPowerMgmtPDUTableVoltage1Value */ +#define AVOCENT_OID_UNIT_VOLTAGE ".1.3.6.1.4.1.10418.17.2.5.3.1.31.1.1" +#define AVOCENT_OID_UNIT_MACADDR ".1.3.6.1.2.1.2.2.1.6.1" + +#ifdef OPENGEAR_MULTIPLE_BANKS +#define AVOCENT_OID_OUTLET_ID ".1.3.6.1.4.1.10418.17.2.5.5.1.3" +#define AVOCENT_OID_OUTLET_NAME ".1.3.6.1.4.1.10418.17.2.5.5.1.4" +#define AVOCENT_OID_OUTLET_STATUS ".1.3.6.1.4.1.10418.17.2.5.5.1.5" +/* This the actual value for the Current of the sensor. */ +#define AVOCENT_OID_OUTLET_LOAD ".1.3.6.1.4.1.10418.17.2.5.5.1.50" +#define AVOCENT_OID_OUTLET_CONTROL ".1.3.6.1.4.1.10418.17.2.5.5.1.6" +#else +#define AVOCENT_OID_OUTLET_ID ".1.3.6.1.4.1.10418.17.2.5.5.1.3.1.1" +#define AVOCENT_OID_OUTLET_NAME ".1.3.6.1.4.1.10418.17.2.5.5.1.4.1.1" +#define AVOCENT_OID_OUTLET_STATUS ".1.3.6.1.4.1.10418.17.2.5.5.1.5.1.1" +#define AVOCENT_OID_OUTLET_LOAD ".1.3.6.1.4.1.10418.17.2.5.5.1.50.1.1" +#define AVOCENT_OID_OUTLET_CONTROL ".1.3.6.1.4.1.10418.17.2.5.5.1.6.1.1" +#endif + +static info_lkp_t avocent_outlet_status_info[] = { + { 1, "off", NULL, NULL }, + { 2, "on", NULL, NULL }, +/* { 3, "offLocked", NULL, NULL }, + { 4, "onLocked", NULL, NULL }, + { 5, "offCycle", NULL, NULL }, + { 6, "onPendingOff", NULL, NULL }, + { 7, "offPendingOn", NULL, NULL }, + { 8, "onPendingCycle", NULL, NULL }, + { 9, "notSet", NULL, NULL }, + { 10, "onFixed", NULL, NULL }, + { 11, "offShutdown", NULL, NULL }, + { 12, "tripped", NULL, NULL },*/ + { 0, NULL, NULL, NULL } +}; + +static snmp_info_t emerson_avocent_pdu_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* Device page */ + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Avocent", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.5.3.1.5.1.%i", /* EMERSON_AVOCENT_OID_MODEL_NAME */ + "Avocent SNMP PDU", SU_FLAG_ABSENT | SU_FLAG_OK | SU_FLAG_NAINVALID, NULL }, + { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.1.4.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* Daisychained devices support + * Notes: this definition is used to: + * - estimate the number of devices, based on the below OID iteration capabilities + * - determine the base index of the SNMP OID (ie 0 or 1) */ + { "device.count", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.2.1.4.1", + "1", SU_FLAG_STATIC, NULL }, + + /* UPS page */ + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Avocent", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, EMERSON_AVOCENT_OID_MODEL_NAME, + "Avocent SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.1.1.0", + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.1.4.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.10418.17.2.1.7.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.macaddr", ST_FLAG_STRING, SU_INFOSIZE, AVOCENT_OID_UNIT_MACADDR, + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* Outlet page */ + { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "outlet.count", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.3.1.8.1.%i", "0", SU_FLAG_STATIC | SU_FLAG_ZEROINVALID | SU_FLAG_OK, NULL }, + + /* outlets */ + /* NOTE: there is a bug in Avocent FW: + * index '0' should not respond (and is not in subtree mode) but answers + * to unitary get, since OIDs start at index '1'. + *Use the status data below to test since '0' is not a supported value */ + { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.10418.17.2.5.5.1.5.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1 | SU_FLAG_ZEROINVALID, &avocent_outlet_status_info[0] }, + { "outlet.%i.id", 0, 1, + ".1.3.6.1.4.1.10418.17.2.5.5.1.3.1.%i.%i", NULL, SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.10418.17.2.5.5.1.4.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1 | SU_FLAG_NAINVALID, NULL }, + /* pmPowerMgmtOutletsTableCurrentValue.1.1.1; Value (Integer): 0 */ + { "outlet.%i.current", 0, 0.1, + ".1.3.6.1.4.1.10418.17.2.5.5.1.50.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pmPowerMgmtOutletsTableCurrentHighCritical.1.1.1; Value (Integer): 160 */ + { "outlet.%i.current.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.10418.17.2.5.5.1.100.1.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pmPowerMgmtOutletsTableCurrentHighWarning.1.1.1; Value (Integer): 120 */ + { "outlet.%i.current.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.10418.17.2.5.5.1.101.1.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pmPowerMgmtOutletsTableCurrentLowWarning.1.1.1; Value (Integer): 0 */ + { "outlet.%i.current.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.10418.17.2.5.5.1.102.1.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pmPowerMgmtOutletsTableCurrentLowCritical.1.1.1; Value (Integer): 0 */ + { "outlet.%i.current.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.10418.17.2.5.5.1.103.1.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pmPowerMgmtOutletsTablePowerValue.1.1.1; Value (Integer): 0 */ + { "outlet.%i.realpower", 0, 0.1, + ".1.3.6.1.4.1.10418.17.2.5.5.1.60.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pmPowerMgmtOutletsTableVoltageValue.1.1.1; Value (Integer): 238 */ + { "outlet.%i.voltage", 0, 1, + ".1.3.6.1.4.1.10418.17.2.5.5.1.70.1.%i.%i", NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + + /* TODO: handle statistics + * pmPowerMgmtOutletsTableEnergyValue.1.1.1; Value (Integer): 0 (Wh) + * pmPowerMgmtOutletsTableEnergyStartTime.1.1.1; Value (OctetString): 2018-02-13 10:40:09 + * pmPowerMgmtOutletsTableEnergyReset.1.1.1; Value (Integer): noAction (1) + */ + + /* Outlet groups collection */ + /* pmPowerMgmtNumberOfOutletGroup.0; Value (Integer): 0 */ + { "outlet.group.count", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.6.%i", + "0", SU_FLAG_STATIC | SU_TYPE_DAISY_1, NULL }, + + /* TODO: support for "Banks" (not sure to understand what is this?!) + * pmPowerMgmtTotalNumberOfBanks.0; Value (Integer): 6 + * pmPowerMgmtBanksTableName.1.1.1; Value (OctetString): 18-bf-ffP0_1_A + */ + + /* According to MIB Power Control values are: + * noAction(1), + * powerOn(2), + * powerOff(3), + * powerCycle(4), + * powerLock(5), + * powerUnlock(6) + */ + { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.5.1.6.1.%i.%i", "4", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.5.1.6.1.%i.%i", "3", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.10418.17.2.5.5.1.6.1.%i.%i", "2", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + +mib2nut_info_t emerson_avocent_pdu = { "emerson_avocent_pdu", EMERSON_AVOCENT_MIB_VERSION, NULL, EMERSON_AVOCENT_OID_MODEL_NAME, emerson_avocent_pdu_mib, EMERSON_AVOCENT_SYSOID, NULL }; diff --git a/drivers/emerson-avocent-pdu-mib.h b/drivers/emerson-avocent-pdu-mib.h new file mode 100644 index 0000000..fb98075 --- /dev/null +++ b/drivers/emerson-avocent-pdu-mib.h @@ -0,0 +1,31 @@ +/* emerson-avocent-pdu-mib.h - subdriver to monitor Emerson Avocent PDUs with NUT + * + * Copyright (C) + * 2008-2018 Arnaud Quette + * 2009 Opengear + * 2018 Eaton (Arnaud Quette ) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EMERSON_AVOCENT_PDU_MIB_H +#define EMERSON_AVOCENT_PDU_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t emerson_avocent_pdu; + +#endif /* EMERSON_AVOCENT_PDU_MIB_H */ diff --git a/drivers/etapro.c b/drivers/etapro.c index 6690348..85b88d5 100644 --- a/drivers/etapro.c +++ b/drivers/etapro.c @@ -52,9 +52,10 @@ #include "main.h" #include "serial.h" +#include "nut_stdint.h" #define DRIVER_NAME "ETA PRO driver" -#define DRIVER_VERSION "0.04" +#define DRIVER_VERSION "0.05" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -112,7 +113,10 @@ etapro_get_response(const char *resp_type) upslogx(LOG_ERR, "bad response format (%s)", tmp); return -1; } - return val; + if (val > INT_MAX) { + upslogx(LOG_WARNING, "got value too big in response"); + } + return (int)val; } static void @@ -190,7 +194,7 @@ static int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } diff --git a/drivers/ever-hid.c b/drivers/ever-hid.c new file mode 100644 index 0000000..9f6a674 --- /dev/null +++ b/drivers/ever-hid.c @@ -0,0 +1,787 @@ +/* ever-hid.c - subdriver to monitor EVER USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2012 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2013 Charles Lepple + * 2017 EVER Power Systems [https://ever.eu/] + * 2020 - 2022 Jim Klimov + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-usbhid-subdriver script. It must be customized. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" /* must be first */ + +#include "usbhid-ups.h" +#include "ever-hid.h" +#include "main.h" /* for getval() */ +#include "usb-common.h" + +#define EVER_HID_VERSION "Ever HID 0.1" +/* FIXME: experimental flag to be put in upsdrv_info */ + +/* Ever */ +#define EVER_VENDORID 0x2e51 + +/* ST Microelectronics */ +#define STMICRO_VENDORID 0x0483 + +/* USB IDs device table */ +static usb_device_id_t ever_usb_device_table[] = { + + { USB_DEVICE(STMICRO_VENDORID, 0xa113), NULL }, + { USB_DEVICE(EVER_VENDORID, 0xffff), NULL}, + { USB_DEVICE(EVER_VENDORID, 0x0000), NULL}, + + /* Terminating entry */ + { 0, 0, NULL } +}; + +/* --------------------------------------------------------------- */ +/* Vendor-specific usage table */ +/* --------------------------------------------------------------- */ + +static const char *ever_format_hardware_fun(double value) +{ + /*TODO - add exception handling for v1.0b0B */ + const char* hard_rev[27] = {"0", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}; + static char model[10]; + snprintf(model, sizeof(model), "rev.%sv%02u", + (&hard_rev[ ((unsigned int)value & 0xFF00)>>8 ])[0], + (unsigned int)value & 0xFF ); + return model; +} + +static const char *ever_format_version_fun(double value) +{ + /*upsdebugx(1, "UPS ups_firmware_conversion_fun VALUE: %d", (long)value ); */ + static char model[10]; + snprintf(model, sizeof(model), "v%X.%Xb%02d", + ((unsigned int)value & 0xF000)>>12, + ((unsigned int)value & 0xF00)>>8, + ((int)value & 0xFF) ); + return model; +} + +static const char *ever_mac_address_fun(double value) +{ + NUT_UNUSED_VARIABLE(value); + + int mac_adress_report_id = 210; + int len = reportbuf->len[mac_adress_report_id]; + const void *buf = reportbuf->data[mac_adress_report_id]; + + static char line[100]; + line[0] = '\0'; + int n = 0; /* number of characters currently in line */ + int i; /* number of bytes output from buffer */ + + /* skip first elemnt which is a report id */ + for (i = 1; i < len; i++) { + n = snprintfcat(line, sizeof(line), n ? ":%02x" : "%02x", + ((unsigned char *)buf)[i]); + } + + return line; +} + +static const char *ever_ip_address_fun(double value) +{ + NUT_UNUSED_VARIABLE(value); + + static int report_counter = 1; + int report_id = 211; + + if(report_counter == 1) + report_id = 211; /* notification dest ip */ + else if(report_counter == 2) + report_id = 230; /* ip address */ + else if(report_counter == 3) + report_id = 231; /* network mask */ + else if(report_counter == 4) + report_id = 232; /* default gateway */ + + report_counter== 4 ? report_counter=1 : report_counter++; + + int len = reportbuf->len[report_id]; + const void *buf = reportbuf->data[report_id]; + + static char line[100]; + line[0] = '\0'; + int n = 0; /* number of characters currently in line */ + int i; /* number of bytes output from buffer */ + + /*skip first element which is a report id */ + for (i = 1; i < len; i++) + { + n = snprintfcat(line, sizeof(line), n ? ".%d" : "%d", + ((unsigned char *)buf)[i]); + } + + return line; +} + +static const char *ever_packets_fun(double value) +{ + NUT_UNUSED_VARIABLE(value); + + static int report_counter = 1; + int report_id = 215; + + if(report_counter == 1 ) + report_id = 215; + else if(report_counter == 2 ) + report_id = 216; + else if(report_counter == 3 ) + report_id = 217; + else if(report_counter == 4 ) + report_id = 218; + + report_counter== 4 ? report_counter=1 : report_counter++; + + int len = reportbuf->len[report_id]; + const unsigned char *buf = reportbuf->data[report_id]; + + static char line[100]; + line[0] = '\0'; + + /*skip first elemnt which is a report id */ + + if(len < 5) + return ""; + + int res = (int)buf[1]; + res |= (int)buf[2] << 8; + res |= (int)buf[3] << 16; + res |= (int)buf[4] << 24; + + snprintf(line, sizeof(line), "%d", res); + return line; + +} + +static const char* ever_workmode_fun(double value) +{ + NUT_UNUSED_VARIABLE(value); + + int workmode_report_id = 74; + int workmode = -1; + const unsigned char *buf = reportbuf->data[workmode_report_id]; + + static char line[100]; + line[0] = '\0'; + + /*skip first element which is a report id */ + snprintfcat(line, sizeof(line), "%d", buf[1]); + + workmode = atoi(line); + + switch(workmode) + { + case 1: + return "UNKNOWN"; + + case 2: + return "STOP"; + + case 4: + return "ONLINE"; + + case 8: + return "ONBATTERY"; + + case 16: + return "WATCH"; + + case 32: + return "WAITING"; + + case 64: + return "EMERGENCY"; + + default: + return "UNKNOWN"; + } + +} + +static const char* ever_messages_fun(double value) +{ + NUT_UNUSED_VARIABLE(value); + + int messages_report_id = 75; + const unsigned char *buf = reportbuf->data[messages_report_id]; + + static char line[200]; + line[0] = '\0'; + + /*skip first element which is a report id */ + int messages = (int)buf[1]; + messages |= (int)buf[2] << 8; + + int n = 0; /* number of characters currently in line */ + + /* duplicate of ups.status: OB LB */ + /* + if(messages & 0x01) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "OVERLOAD"); + if(messages & 0x02) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "BATTERY_LOW"); + */ + if(messages & 0x04) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "BOOST"); + if(messages & 0x08) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "BUCK"); + if(messages & 0x10) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "BOOST_BLOCKED"); + if(messages & 0x20) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "BUCK_BLOCKED"); + if(messages & 0x40) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "CHARGING"); + if(messages & 0x80) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "FAN_ON"); + if(messages & 0x100) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "EPO_BLOCKED"); + if(messages & 0x200) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "NEED_REPLACMENT"); + if(messages & 0x400 || messages & 0x800) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "OVERHEAT"); + if(messages & 0x1000) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "WAITING_FOR_MIN_CHARGE"); + if(messages & 0x2000) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "MAINS_OUT_OF_RANGE"); + return line; +} + +static const char* ever_alarms_fun(double value) +{ + NUT_UNUSED_VARIABLE(value); + + int alarms_report_id = 76; + + const unsigned char *buf = reportbuf->data[alarms_report_id]; + + static char line[200]; + line[0] = '\0'; + + /*skip first element which is a report id */ + int alarms = (int)buf[1]; + alarms |= (int)buf[2] << 8; + + int n = 0; /* number of characters currently in line */ + + if(alarms & 0x01) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "OVERLOAD"); + if(alarms & 0x02) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "SHORT-CIRCUIT"); + if(alarms & 0x04 || alarms & 0x08) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "OVERHEAT"); + if(alarms & 0x10) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "EPO"); + if(alarms & 0x20) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "INERNAL_ERROR"); + if(alarms & 0x40) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "REVERSE_POWER_SUPPLY"); + if(alarms & 0x80) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "NO_INETERNAL_COMM"); + if(alarms & 0x100) + n = snprintfcat(line, sizeof(line), n ? " %s" : "%s", "CRITICAL_BATT_VOLTAGE"); + + return line; +} + +static const char* ever_on_off_fun(double value) +{ + NUT_UNUSED_VARIABLE(value); + + int workmode_report_id = 74; + int workmode = -1; + const unsigned char *buf = reportbuf->data[workmode_report_id]; + + static char line[100]; + line[0] = '\0'; + + /*skip first element which is a report id */ + snprintfcat(line, sizeof(line), "%d", buf[1]); + + workmode = atoi(line); + + if(workmode != 0x04 && workmode != 0x08) + return "off"; + + return "!off"; +} + +static info_lkp_t ever_format_hardware[] = { + { 0, NULL, ever_format_hardware_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t ever_format_version[] = { + { 0, NULL, ever_format_version_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t ever_mac_address[] = { + { 0, NULL, ever_mac_address_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t ever_ip_address[] = { + { 0, NULL, ever_ip_address_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t ever_packets[] = { + { 0, NULL, ever_packets_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t ever_workmode[] = { + { 0, NULL, ever_workmode_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t ever_messages[] = { + { 0, NULL, ever_messages_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t ever_alarms[] = { + { 0, NULL, ever_alarms_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t ever_on_off_info[] = { + { 0, NULL, ever_on_off_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + + +/* EVER usage table */ +static usage_lkp_t ever_usage_lkp[] = { + { "EVER1", 0x00000000 }, + { "EVER2", 0xff000000 }, + { "EVER3", 0xff000001 }, + { "EVER4", 0xff000002 }, + { "EVER5", 0xff000003 }, + { "EVER6", 0xff000004 }, + { "EVER7", 0xff000005 }, + { "EVER8", 0xff000006 }, + { "EVER9", 0xff000007 }, + { "EVER10", 0xff000008 }, + { "EVER11", 0xff000009 }, + { "EVER12", 0xff000010 }, + { "EVER13", 0xff000011 }, + { "EVER14", 0xff000012 }, + { "EVER15", 0xff000013 }, + { "EVER16", 0xff000014 }, + { "EVER17", 0xff000015 }, + { "EVER18", 0xff000016 }, + { "EVER19", 0xff000017 }, + { "EVER20", 0xff000018 }, + { "EVER21", 0xff000019 }, + { "EVER22", 0xff00001a }, + { "EVER23", 0xff00001b }, + { "EVER24", 0xff00001c }, + { "EVER25", 0xff00001d }, + { "EVER26", 0xff00001e }, + { "EVER27", 0xff00001f }, + { "EVER28", 0xff000020 }, + { "EVER29", 0xff000021 }, + { "EVER30", 0xff000022 }, + { "EVER31", 0xff000023 }, + { "EVER32", 0xff000030 }, + { "EVER33", 0xff000031 }, + { "EVER34", 0xff000032 }, + { "EVER35", 0xff000033 }, + { "EVER36", 0xff000034 }, + { "EVER37", 0xff000035 }, + { "EVER38", 0xff000036 }, + { "EVER39", 0xff000037 }, + { "EVER40", 0xff000038 }, + { "EVER41", 0xff000039 }, + { "EVER42", 0xff000040 }, + { "EVER43", 0xff000041 }, + { "EVER44", 0xff000042 }, + { "EVER45", 0xff000043 }, + { "EVER46", 0xff000044 }, + { "EVER47", 0xff000045 }, + { "EVER48", 0xff000046 }, + { "EVER49", 0xff000050 }, + { "EVER50", 0xff000051 }, + { "EVER51", 0xff000052 }, + { "EVER52", 0xff000053 }, + { "EVER53", 0xff000054 }, + { "EVER54", 0xff000060 }, + { "EVER55", 0xff000061 }, + { "EVER56", 0xff000062 }, + { "EVER57", 0xff000063 }, + { "EVER58", 0xff000064 }, + { "EVER59", 0xff000066 }, + { "EVER60", 0xff000067 }, + { "EVER61", 0xff000068 }, + { "EVER62", 0xff000069 }, + { "EVER63", 0xff00006a }, + { "EVER64", 0xff00006b }, + { "EVER65", 0xff00006c }, + { "EVER66", 0xff00006d }, + { "EVER67", 0xff00006e }, + { "EVER68", 0xff00006f }, + { "EVER69", 0xff000070 }, + { "EVER70", 0xff000071 }, + { "EVER71", 0xff000072 }, + { "EVER72", 0xff000073 }, + { "EVER73", 0xff000074 }, + { "EVER74", 0xff000075 }, + { "EVER75", 0xff000076 }, + { "EVER76", 0xff000077 }, + { "EVER77", 0xff000078 }, + { "EVER78", 0xff000079 }, + { "EVER79", 0xff00007a }, + { "EVER80", 0xff00007b }, + { "EVER81", 0xff00007c }, + { "EVER82", 0xff00007d }, + { "EVER83", 0xff00007e }, + { "EVER84", 0xff00007f }, + { "EVER85", 0xff000080 }, + { "EVER86", 0xff000081 }, + { "EVER87", 0xff000082 }, + { "EVER88", 0xff000083 }, + { "EVER89", 0xff000084 }, + { "EVER90", 0xff000085 }, + { "EVER91", 0xff000086 }, + { "EVER92", 0xff000087 }, + { "EVER93", 0xff000088 }, + { "EVER94", 0xff000089 }, + { "EVER95", 0xff00008a }, + { "EVER96", 0xff00008b }, + { "EVER97", 0xff000090 }, + { "EVER98", 0xff000091 }, + { "EVER99", 0xff000092 }, + { "EVER100", 0xff000093 }, + { "EVER101", 0xff000094 }, + { "EVER102", 0xff000095 }, + { "EVER103", 0xff000096 }, + { "EVER104", 0xff000097 }, + { NULL, 0 } +}; + +static usage_tables_t ever_utab[] = { + ever_usage_lkp, + hid_usage_lkp, + NULL, +}; + + +/* --------------------------------------------------------------- */ +/* HID2NUT lookup table */ +/* --------------------------------------------------------------- */ + +static hid_info_t ever_hid2nut[] = { + + /* Note: fields marked with "experimental." prefix were proposed without + * an exact match vs. docs/nut-names.txt definitions. PRs are welcome to + * analyze and map those values into standard NUT variable names, or to + * raise discussion on mailing lists and define new names via consensus. + * There is a lot of interesting info listed below. + * + * Note: mappings that were applied below (as committed 2022-02-09) may + * be wrong and are based mostly on cursory reading of original names. + * In particular, not sure if the skipped "id.*" fields were identifiers + * or some "internal device" etc. + */ + + /* experimental: "NUT variable names" do not currently have + * any battery.*id data points: */ + { "experimental.battery.batteryid", 0, 0, "UPS.BatterySystem.Battery.BatteryID", NULL, "%.0f", 0, NULL }, + { "experimental.battery.systemid", 0, 0, "UPS.BatterySystem.BatterySystemID", NULL, "%.0f", 0, NULL }, + { "experimental.battery.chargerid", 0, 0, "UPS.BatterySystem.Charger.ChargerID", NULL, "%.0f", 0, NULL }, + { "experimental.battery.input_flowid", 0, 0, "UPS.BatterySystem.Input.FlowID", NULL, "%.0f", 0, NULL }, + { "experimental.battery.input_id", 0, 0, "UPS.BatterySystem.Input.InputID", NULL, "%.0f", 0, NULL }, + { "experimental.battery.output_flowid", 0, 0, "UPS.BatterySystem.Output.FlowID", NULL, "%.0f", 0, NULL }, + { "experimental.battery.output_id", 0, 0, "UPS.BatterySystem.Output.OutputID", NULL, "%.0f", 0, NULL }, + + /* experimental: "NUT variable names" do not currently have + * any id (nor version) data points for FW/HW of components: */ + /* not implemented*/ + /* { "experimental.id.ups_type", 0, 0, "UPS.EVER1.EVER12", NULL, "%s", 0, ever_format_model }, */ + { "experimental.id.firmware_version_inverter", 0, 0, "UPS.EVER1.EVER13", NULL, "%s", 0, ever_format_version }, + { "experimental.id.firmware_version_interfaces", 0, 0, "UPS.EVER1.EVER14", NULL, "%s", 0, ever_format_version }, + { "experimental.id.hardware_version", 0, 0, "UPS.EVER1.EVER15", NULL, "%s", 0, ever_format_hardware }, + { "experimental.id.protocol_version_inverter", 0, 0, "UPS.EVER1.EVER16", NULL, "%s", 0, ever_format_version }, + { "experimental.id.protocol_version_interfaces", 0, 0, "UPS.EVER1.EVER17", NULL, "%s", 0, ever_format_version }, + + /* WAS: "experimental.inverter_info.heatsink_temperature" */ + { "ups.temperature", 0, 0, "UPS.EVER1.EVER42", NULL, "%s", 0, kelvin_celsius_conversion }, + /* WAS: "experimental.inverter_info.battery_temperature" */ + { "battery.temperature", 0, 0, "UPS.EVER1.EVER43", NULL, "%s", 0, kelvin_celsius_conversion }, + /* WAS: "experimental.ups_info.output_powerfactor" */ + { "powerfactor", 0, 0, "UPS.EVER1.EVER44", NULL, "%.0f", 0, NULL }, + + /* experimental: Should these be HU_TYPE_CMD entries? + * Or are they really settings? */ + { "experimental.control.ups_on", ST_FLAG_RW | ST_FLAG_NUMBER, 0, "UPS.EVER1.EVER45.EVER46", NULL, "%.0f", 0, NULL }, + { "experimental.control.clear_fault", ST_FLAG_RW | ST_FLAG_NUMBER, 0, "UPS.EVER1.EVER45.EVER47", NULL, "%.0f", 0, NULL }, + { "experimental.control.clear_battery_fault", ST_FLAG_RW | ST_FLAG_NUMBER, 0, "UPS.EVER1.EVER45.EVER48", NULL, "%.0f", 0, NULL }, + { "experimental.control.epo_blocked", ST_FLAG_RW | ST_FLAG_NUMBER, 0, "UPS.EVER1.EVER49.EVER50", NULL, "%.0f", 0, NULL }, + { "experimental.control.green_mode", ST_FLAG_RW | ST_FLAG_NUMBER, 0, "UPS.EVER1.EVER49.EVER51", NULL, "%.0f", 0, NULL }, + { "experimental.control.button_sound", ST_FLAG_RW | ST_FLAG_NUMBER, 0, "UPS.EVER1.EVER49.EVER52", NULL, "%.0f", 0, NULL }, + { "experimental.control.audible_alarm", ST_FLAG_RW | ST_FLAG_NUMBER, 0, "UPS.EVER1.EVER49.EVER53", NULL, "%.0f", 0, NULL }, + + /* WAS: "experimental.config.output_voltage" */ + { "output.voltage", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.EVER1.EVER54", NULL, "%.0f", 0, NULL }, + + /* not implemented*/ + /* + { "experimental.config.min_output_voltage", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.EVER1.EVER55", NULL, "%.0f", 0, NULL }, + { "experimental.config.max_output_voltage", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.EVER1.EVER56", NULL, "%.0f", 0, NULL }, + { "experimental.config.min_output_frequency", ST_FLAG_RW | ST_FLAG_NUMBER, 0, "UPS.EVER1.EVER57", NULL, "%.1f", 0, NULL }, + { "experimental.config.max_output_frequency", ST_FLAG_RW | ST_FLAG_NUMBER, 0, "UPS.EVER1.EVER58", NULL, "%.1f", 0, NULL }, + */ + /* experimental: what units is this counted in? + * is "ups.load.high" a suitable mapping here? or "battery.voltage.high"? + */ + { "experimental.config.overload_clearance_threshold", ST_FLAG_RW | ST_FLAG_STRING, 2, "UPS.EVER1.EVER59", NULL, "%.0f", 0, NULL }, + { "experimental.config.stb_charge", ST_FLAG_RW | ST_FLAG_STRING, 2, "UPS.EVER1.EVER60", NULL, "%.0f", 0, NULL }, + /* WAS: "experimental.config.number_of_ebms" + * Should this be a string? rw? + */ + { "battery.packs.external", ST_FLAG_RW | ST_FLAG_STRING, 1, "UPS.EVER1.EVER61", NULL, "%.0f", 0, NULL }, + + { "experimental.statistics.mains_loss_counter", 0, 0, "UPS.EVER1.EVER62", NULL, "%.0f", 0, NULL }, + { "experimental.statistics.lowering_AVR_trigger_counter", 0, 0, "UPS.EVER1.EVER63", NULL, "%.0f", 0, NULL }, + { "experimental.statistics.rising_AVR_trigger_counter", 0, 0, "UPS.EVER1.EVER64", NULL, "%.0f", 0, NULL }, + { "experimental.statistics.overload_counter", 0, 0, "UPS.EVER1.EVER65", NULL, "%.0f", 0, NULL }, + { "experimental.statistics.short_circuit_counter", 0, 0, "UPS.EVER1.EVER66", NULL, "%.0f", 0, NULL }, + { "experimental.statistics.discharge_counter", 0, 0, "UPS.EVER1.EVER67", NULL, "%.0f", 0, NULL }, + { "experimental.statistics.overheat_counter", 0, 0, "UPS.EVER1.EVER68", NULL, "%.0f", 0, NULL }, + { "experimental.statistics.mains_operation_time", 0, 0, "UPS.EVER1.EVER69", NULL, "%.0f", 0, NULL }, + { "experimental.statistics.autonomous_operation_time", 0, 0, "UPS.EVER1.EVER70", NULL, "%.0f", 0, NULL }, + { "experimental.statistics.overload_operation_time", 0, 0, "UPS.EVER1.EVER71", NULL, "%.0f", 0, NULL }, + + { "experimental.networkcard.mac_address", 0, 0, "UPS.EVER1.EVER72", NULL, "%s", 0, ever_mac_address }, + { "experimental.networkcard.notification_destination_ip", 0, 0, "UPS.EVER1.EVER73", NULL, "%s", 0, ever_ip_address }, + { "experimental.networkcard.send_packets", 0, 0, "UPS.EVER1.EVER77", NULL, "%s", 0, ever_packets }, + { "experimental.networkcard.received_packets", 0, 0, "UPS.EVER1.EVER78", NULL, "%s", 0, ever_packets }, + { "experimental.networkcard.send_packets_err", 0, 0, "UPS.EVER1.EVER79", NULL, "%s", 0, ever_packets }, + { "experimental.networkcard.received_packets_err", 0, 0, "UPS.EVER1.EVER80", NULL, "%s", 0, ever_packets }, + { "experimental.networkcard.config_dhcp_enabled", 0, 0, "UPS.EVER1.EVER85.EVER86", NULL, "%.0f", 0, NULL }, + { "experimental.networkcard.config_ethernet_enabled", 0, 0, "UPS.EVER1.EVER85.EVER87", NULL, "%.0f", 0, NULL }, + { "experimental.networkcard.config_http_enabled", 0, 0, "UPS.EVER1.EVER85.EVER88", NULL, "%.0f", 0, NULL }, + { "experimental.networkcard.config_snmp_enabled", 0, 0, "UPS.EVER1.EVER85.EVER89", NULL, "%.0f", 0, NULL }, + { "experimental.networkcard.config_snmp_trap_enabled", 0, 0, "UPS.EVER1.EVER85.EVER90", NULL, "%.0f", 0, NULL }, + { "experimental.networkcard.config_readonly", 0, 0, "UPS.EVER1.EVER85.EVER91", NULL, "%.0f", 0, NULL }, + { "experimental.networkcard.config_restart_eth", 0, 0, "UPS.EVER1.EVER85.EVER96", NULL, "%.0f", 0, NULL }, + { "experimental.networkcard.ip_address", 0, 0, "UPS.EVER1.EVER93", NULL, "%s", 0, ever_ip_address }, + { "experimental.networkcard.network_mask", 0, 0, "UPS.EVER1.EVER94", NULL, "%s", 0, ever_ip_address }, + { "experimental.networkcard.default_gateway", 0, 0, "UPS.EVER1.EVER95", NULL, "%s", 0, ever_ip_address }, + + /* WAS: "experimental.id.config_active_power" */ + { "ups.realpower.nominal", 0, 0, "UPS.Flow.ConfigActivePower", NULL, "%.0f", 0, NULL }, + /* WAS: "experimental.id.config_apparent_power" + * Other HID subdrivers use "ups.power.nominal" mostly (often HU_FLAG_STATIC); + * once of each: "ups.realpower.nominal", "ups.realpower". + * Is this even a run-time value or a hardware property? + */ + { "ups.power.nominal", 0, 0, "UPS.Flow.ConfigApparentPower", NULL, "%.0f", 0, NULL }, + + /* WAS: "experimental.ups.config_frequency" + * Here and next: is this about input or output?.. + * Other drivers have "input.frequency.nominal" on numbered Flows + * As a "nominal", should it be HU_FLAG_SEMI_STATIC or HU_FLAG_STATIC maybe? + * Note there are non-nominal values in "powerconverter" below, + * so the questions here may be somewhat irrelevant... + */ + { "output.frequency.nominal", 0, 0, "UPS.Flow.ConfigFrequency", NULL, "%.0f", 0, NULL }, + /* WAS: "experimental.ups.config_voltage" */ + { "output.voltage.nominal", 0, 0, "UPS.Flow.ConfigVoltage", NULL, "%.0f", 0, NULL }, + { "experimental.ups.flow_id", 0, 0, "UPS.Flow.FlowID", NULL, "%.0f", 0, NULL }, + + /* NOTE: NUT variable names define "outlet.n.*" names for numbering all + * separately manageable outlets; the numberless value (or outlet.0.*) + * is reserved to represent common properties of all outlets, if there + * are more than one outlet (group). + * Mapping below arbitrarily assigns n=1 but really this should be tied + * to actual outlet counts (see %i mappings in other drivers). + */ + /* WAS: experimental.outlet.outlet_id */ + { "outlet.1.id", 0, 0, "UPS.OutletSystem.Outlet.OutletID", NULL, "%.0f", 0, NULL }, + /* WAS: */ + { "experimental.outlet.1.present", 0, 0, "UPS.OutletSystem.Outlet.PresentStatus.Present", NULL, "%.0f", 0, yes_no_info }, + { "outlet.1.switchable", 0, 0, "UPS.OutletSystem.Outlet.PresentStatus.Switchable", NULL, "%.0f", 0, yes_no_info }, + /* WAS: experimental.outlet.switch_on_off */ + { "outlet.1.status", 0, 0, "UPS.OutletSystem.Outlet.PresentStatus.SwitchOn/Off", NULL, "%.0f", 0, NULL }, + { "experimental.outlet.1.undefined", 0, 0, "UPS.OutletSystem.Outlet.PresentStatus.Undefined", NULL, "%.0f", 0, NULL }, + { "experimental.outlet.1.system_id", 0, 0, "UPS.OutletSystem.OutletSystemID", NULL, "%.0f", 0, NULL }, + /* experimental: Should these be HU_TYPE_CMD entries? + * Or are they really settings? */ + { "experimental.outlet.1.switch_off_control", ST_FLAG_RW | ST_FLAG_NUMBER, 0, "UPS.OutletSystem.Outlet.SwitchOffControl", NULL, "%.0f", 0, NULL }, + { "experimental.outlet.1.switch_on_control", ST_FLAG_RW | ST_FLAG_NUMBER, 0, "UPS.OutletSystem.Outlet.SwitchOnControl", NULL, "%.0f", 0, NULL }, + + { "experimental.powerconverter.input_flow_id", 0, 0, "UPS.PowerConverter.Input.FlowID", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powerconverter.input_frequency */ + { "input.frequency", 0, 0, "UPS.PowerConverter.Input.Frequency", NULL, "%.0f", 0, NULL }, + { "experimental.powerconverter.input_id", 0, 0, "UPS.PowerConverter.Input.InputID", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powerconverter.input_voltage */ + { "input.voltage", 0, 0, "UPS.PowerConverter.Input.Voltage", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powerconverter.output_active_power */ + { "ups.realpower", 0, 0, "UPS.PowerConverter.Output.ActivePower", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powerconverter.output_apparent_power */ + { "ups.power", 0, 0, "UPS.PowerConverter.Output.ApparentPower", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powerconverter.output_current */ + { "output.current", 0, 0, "UPS.PowerConverter.Output.Current", NULL, "%.0f", 0, NULL }, + { "experimental.powerconverter.output_flowid", 0, 0, "UPS.PowerConverter.Output.FlowID", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powerconverter.output_frequency */ + { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", NULL, "%.0f", 0, NULL }, + { "experimental.powerconverter.output_id", 0, 0, "UPS.PowerConverter.Output.OutputID", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powerconverter.output_percent_load + * Note: several original readings map into "ups.load", first served wins + */ + { "ups.load", 0, 0, "UPS.PowerConverter.Output.PercentLoad", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powerconverter.output_voltage */ + { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.0f", 0, NULL }, + { "experimental.powerconverter.powerconverterid", 0, 0, "UPS.PowerConverter.PowerConverterID", NULL, "%.0f", 0, NULL }, + { "experimental.powersummary.capacity_granularity_1", 0, 0, "UPS.PowerSummary.CapacityGranularity1", NULL, "%.0f", 0, NULL }, + { "experimental.powersummary.capacity_granularity_2", 0, 0, "UPS.PowerSummary.CapacityGranularity2", NULL, "%.0f", 0, NULL }, + /* WAS: */ + { "experimental.powersummary.capacity_mode", 0, 0, "UPS.PowerSummary.CapacityMode", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powersummary.delay_before_shutdown */ + { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL }, + /* WAS: experimental.powersummary.design_capacity */ + { "battery.capacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", 0, NULL }, + { "experimental.powersummary.flow_id", 0, 0, "UPS.PowerSummary.FlowID", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powersummary.full_charge_capacity */ + { "battery.capacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powersummary.idevice_chemistry */ + { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powersummary.percent_load + * Note: several original readings map into "ups.load", first served wins + */ + { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", NULL, "%.0f", 0, NULL }, + { "experimental.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powersummary.remaining_capacity */ + { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powersummary.remaining_time_limit */ + { "battery.runtime.low", ST_FLAG_RW | ST_FLAG_NUMBER, 0, "UPS.PowerSummary.RemainingTimeLimit", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powersummary.run_time_to_empty */ + { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powersummary.voltage */ + { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.0f", 0, NULL }, + /* WAS: experimental.powersummary.delay_before_shutdown */ + { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, + + /* not implemented*/ + /* { "unmapped.ups.powersummary.powersummaryid", 0, 0, "UPS.PowerSummary.PowerSummaryID", NULL, "%.0f", 0, NULL }, */ + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.AwaitingPower", NULL, NULL, HU_FLAG_QUICK_POLL, awaitingpower_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BatteryPresent", NULL, NULL, 0, nobattery_info }, + + /* not implemented*/ + /* { "experimental.ups.presentstatus.belowremainingcapacitylimit", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, "%.0f", 0, NULL }, */ + /* { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Boost", NULL, NULL, 0, boost_info }, */ + /* { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Buck", NULL, NULL, 0, trim_info }, */ + /* { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, */ + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.CommunicationLost", NULL, NULL, 0, commfault_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, + /* not implemented*/ + /* { "experimental.ups.powersummary.presentstatus.good", 0, 0, "UPS.PowerSummary.PresentStatus.Good", NULL, "%.0f", 0, NULL }, */ + /* { "experimental.ups.powersummary.presentstatus.internalfailure", 0, 0, "UPS.PowerSummary.PresentStatus.InternalFailure", NULL, "%.0f", 0, NULL }, */ + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, HU_FLAG_QUICK_POLL, overload_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.OverTemperature", NULL, NULL, 0, overheat_info }, + /* not implemented*/ + /* { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, 0, lowbatt_info }, */ + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, + + { "BOOL", 0, 0, "UPS.EVER1.EVER18.EVER19", NULL, NULL, 0, overload_info }, + { "BOOL", 0, 0, "UPS.EVER1.EVER18.EVER20", NULL, NULL, 0, lowbatt_info }, + { "BOOL", 0, 0, "UPS.EVER1.EVER18.EVER21", NULL, NULL, 0, boost_info }, + { "BOOL", 0, 0, "UPS.EVER1.EVER18.EVER22", NULL, NULL, 0, trim_info }, + { "BOOL", 0, 0, "UPS.EVER1.EVER18.EVER25", NULL, NULL, 0, charging_info }, + { "BOOL", 0, 0, "UPS.EVER1.EVER18.EVER28", NULL, NULL, 0, replacebatt_info }, + { "BOOL", 0, 0, "UPS.EVER1.EVER32.EVER33", NULL, NULL, 0, overload_info }, + { "BOOL", 0, 0, "UPS.EVER1.EVER32.EVER40", NULL, "%.0f", 0, commfault_info }, + { "BOOL", 0, 0, "UPS.EVER1.EVER97.EVER102", NULL, "%s", 0, ever_on_off_info }, + + /* ever workmodes, messages & alarms */ + { "experimental.status.workmode", 0, 0, "UPS.EVER1.EVER97.EVER98", NULL, "%s", 0, ever_workmode }, + { "experimental.status.messages", 0, 0, "UPS.EVER1.EVER18.EVER28", NULL, NULL, 0, ever_messages }, + { "experimental.status.alarms", 0, 0, "UPS.EVER1.EVER32.EVER33", NULL, NULL, 0, ever_alarms }, + + /* instant commands */ + /* experimental: With the same fields here, are the commands different? + * Per NUT command names, should be: documented load.off stays off, like + * shutdown.stayoff, but shutdown.return may return if wall power comes back! + * In many drivers, similar command with "-1" instead of DEFAULT_OFFDELAY + * serves as a shutdown.stop (to abort a pending shutdown). + */ + { "load.off.delay", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, + { "shutdown.return", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } +}; + +static const char *ever_format_model(HIDDevice_t *hd) { + return hd->Product; +} + +static const char *ever_format_mfr(HIDDevice_t *hd) { + return hd->Vendor ? hd->Vendor : "Ever"; +} + +static const char *ever_format_serial(HIDDevice_t *hd) { + return hd->Serial; +} + +/* this function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ +static int ever_claim(HIDDevice_t *hd) +{ + int status = is_usb_device_supported(ever_usb_device_table, hd); + + switch (status) + { + case POSSIBLY_SUPPORTED: + /* by default, reject, unless the productid option is given */ + if (getval("productid")) { + return 1; + } + possibly_supported("Ever", hd); + return 0; + + case SUPPORTED: + return 1; + + case NOT_SUPPORTED: + default: + return 0; + } +} + +subdriver_t ever_subdriver = { + EVER_HID_VERSION, + ever_claim, + ever_utab, + ever_hid2nut, + ever_format_model, + ever_format_mfr, + ever_format_serial, + fix_report_desc, +}; diff --git a/drivers/ever-hid.h b/drivers/ever-hid.h new file mode 100644 index 0000000..bf47bc1 --- /dev/null +++ b/drivers/ever-hid.h @@ -0,0 +1,32 @@ +/* everhid-hid.h - subdriver to monitor Everhid USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2009 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2017 EVER Power Systems [https://ever.eu/] + * 2020 - 2022 Jim Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef EVER_HID_H +#define EVER_HID_H + +#include "usbhid-ups.h" + +extern subdriver_t ever_subdriver; + +#endif /* EVER_HID_H */ diff --git a/drivers/everups.c b/drivers/everups.c index 7cd9328..63ccea8 100644 --- a/drivers/everups.c +++ b/drivers/everups.c @@ -1,4 +1,4 @@ -/* everups.c - support for Ever UPS models +/* everups.c - support for Ever UPS models (serial) Copyright (C) 2001 Bartek Szady @@ -20,8 +20,8 @@ #include "main.h" #include "serial.h" -#define DRIVER_NAME "Ever UPS driver" -#define DRIVER_VERSION "0.03" +#define DRIVER_NAME "Ever UPS driver (serial)" +#define DRIVER_VERSION "0.04" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -64,9 +64,9 @@ static int InitUpsType(void) static const char *GetTypeUpsName(void) { - switch(upstype) - { - case 67: return "NET 500-DPC"; + switch(upstype) + { + case 67: return "NET 500-DPC"; case 68: return "NET 700-DPC"; case 69: return "NET 1000-DPC"; case 70: return "NET 1400-DPC"; @@ -95,7 +95,7 @@ void upsdrv_updateinfo(void) unsigned long acuV; unsigned long lineV; double fVal; - + if (!Code(2)) { upslog_with_errno(LOG_INFO, "Code failed"); dstate_datastale(); @@ -106,7 +106,7 @@ void upsdrv_updateinfo(void) ser_get_char(upsfd, recBuf, 3, 0); if ((recBuf[0] & 1) !=0) standby=1; - else + else battery=(recBuf[0] &4) !=0; if (Code(1)) { /*Accumulator voltage value*/ ser_send_char(upsfd, 189); diff --git a/drivers/explore-hid.c b/drivers/explore-hid.c index 40154e1..f9e8690 100644 --- a/drivers/explore-hid.c +++ b/drivers/explore-hid.c @@ -25,7 +25,7 @@ #include "usbhid-ups.h" #include "explore-hid.h" -#define EXPLORE_HID_VERSION "EXPLORE HID 0.1" +#define EXPLORE_HID_VERSION "EXPLORE HID 0.2" static usage_tables_t explore_utab[] = { hid_usage_lkp, @@ -57,11 +57,13 @@ static const char *explore_format_serial(HIDDevice_t *hd) { /* this function allows the subdriver to "claim" a device: return 1 if * the device is supported by this subdriver, else 0. */ static int explore_claim(HIDDevice_t *hd) { - if (testvar("explore")) { - return 1; - } else { - return 0; - } + NUT_UNUSED_VARIABLE(hd); + + if (testvar("explore")) { + return 1; + } else { + return 0; + } } subdriver_t explore_subdriver = { @@ -72,4 +74,5 @@ subdriver_t explore_subdriver = { explore_format_model, explore_format_mfr, explore_format_serial, + fix_report_desc, }; diff --git a/drivers/gamatronic.c b/drivers/gamatronic.c index acc56a3..b0be56d 100644 --- a/drivers/gamatronic.c +++ b/drivers/gamatronic.c @@ -2,7 +2,9 @@ * * SEC UPS Driver ported to the new NUT API for Gamatronic UPS Usage. * - * Copyright (C) + * TODO: Replace lots of printf() by upslogx() or upsdebugx() below! + * + * Copyright (C) * 2001 John Marley * 2002 Jules Taplin * 2002 Eric Lawson @@ -24,13 +26,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ - + #include "main.h" -#include "serial.h" +#include "serial.h" #include "gamatronic.h" +#include "nut_stdint.h" #define DRIVER_NAME "Gamatronic UPS driver" -#define DRIVER_VERSION "0.02" +#define DRIVER_VERSION "0.03" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -45,274 +48,279 @@ upsdrv_info_t upsdrv_info = { { NULL } }; -#define ENDCHAR '\r' -#define IGNCHARS "" -#define SER_WAIT_SEC 1 /* allow 3.0 sec for ser_get calls */ +#define ENDCHAR '\r' +#define IGNCHARS "" +#define SER_WAIT_SEC 1 /* allow 3.0 sec for ser_get calls */ #define SER_WAIT_USEC 0 -int sec_upsrecv (char *buf) +static int sec_upsrecv (char *buf) { + char lenbuf[4]; + int ret; - char lenbuf[4]; - int ret; - - - ser_get_line(upsfd, buf, 140, ENDCHAR, IGNCHARS,SER_WAIT_SEC, SER_WAIT_USEC); - if (buf[0] == SEC_MSG_STARTCHAR){ - switch (buf[1]){ - case SEC_NAK: - return(-1); - case SEC_ACK: - return(0); - case SEC_DATAMSG: - strncpy(lenbuf,buf+2,3); - ret = atoi(lenbuf); - if (ret > 0){ - strcpy(buf,buf+5); - return(ret);} - else return (-2); - default: - return(-2); + ser_get_line(upsfd, buf, 140, ENDCHAR, IGNCHARS,SER_WAIT_SEC, SER_WAIT_USEC); + if (buf[0] == SEC_MSG_STARTCHAR) { + switch (buf[1]) { + case SEC_NAK: + return(-1); + case SEC_ACK: + return(0); + case SEC_DATAMSG: + strncpy(lenbuf, buf+2, 3); + lenbuf[3] = '\0'; + ret = atoi(lenbuf); + if (ret > 0) { + strcpy(buf,buf+5); + return(ret); + } + else return (-2); + default: + return(-2); } - } - else - { return (-2); } + } + else + return (-2); } -int sec_cmd(const char mode, const char *command, char *msgbuf, int *buflen) +static ssize_t sec_cmd(const char mode, const char *command, char *msgbuf, ssize_t *buflen) { - char msg[140]; - int ret; + char msg[140]; + ssize_t ret; - memset(msg, 0, sizeof(msg)); + memset(msg, 0, sizeof(msg)); - /* create the message string */ - if (*buflen > 0) { - snprintf(msg, sizeof(msg), "%c%c%03d%s%s", SEC_MSG_STARTCHAR, - mode, (*buflen)+3, command, msgbuf); - } - else { - snprintf(msg, sizeof(msg), "%c%c003%s", SEC_MSG_STARTCHAR, - mode, command); - } - upsdebugx(1, "PC-->UPS: \"%s\"",msg); - ret = ser_send(upsfd, "%s", msg); - - upsdebugx(1, " send returned: %d",ret); + /* create the message string */ + if (*buflen > 0) { + snprintf(msg, sizeof(msg), "%c%c%03zd%s%s", SEC_MSG_STARTCHAR, + mode, (*buflen)+3, command, msgbuf); + } + else { + snprintf(msg, sizeof(msg), "%c%c003%s", SEC_MSG_STARTCHAR, + mode, command); + } + upsdebugx(1, "PC-->UPS: \"%s\"",msg); + ret = ser_send(upsfd, "%s", msg); - if (ret == -1) return -1; + upsdebugx(1, " send returned: %zd",ret); - ret = sec_upsrecv(msg); + if (ret == -1) return -1; + ret = sec_upsrecv(msg); - if (ret < 0) return -1; + if (ret < 0) return -1; - if (ret >= 0) { - strncpy(msgbuf, msg, ret); + strncpy(msgbuf, msg, (size_t)ret); upsdebugx(1, "UPS<--PC: \"%s\"",msg); - } -/* *(msgbuf+ret) = '\0';*/ - *buflen = ret; - return ret; +/* + *(msgbuf+ret) = '\0'; +*/ + + *buflen = ret; + return ret; } -void addquery(const char *cmd, int field, int varnum, int pollflag) +static void addquery(const char *cmd, int field, int varnum, int pollflag) { - int q; + int q; - for (q=0; q 0) { - - if (strcmp(sec_varlist[sqv(q,f)].value, r) != 0 ) { +static void sec_poll ( int pollflag ) { + ssize_t msglen; + int f, q; + char retbuf[140], *n, *r; - snprintf(sec_varlist[sqv(q,f)].value, - sizeof(sec_varlist[sqv(q,f)].value), "%s", r); - - sec_setinfo(sqv(q,f), r); + for (q=0; q 0) { + if (strcmp(sec_varlist[sqv(q,f)].value, r) != 0) { +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic ignored "-Wformat-truncation" +#endif + /* NOTE: We intentionally limit the amount + * of characters picked from "r" buffer + * into respectively sized "*.value" + */ + int len = snprintf(sec_varlist[sqv(q,f)].value, + sizeof(sec_varlist[sqv(q,f)].value), "%s", r); + + if (len < 0) { + upsdebugx(1, "%s: got an error while extracting value", __func__); + } + + if ((intmax_t)len > (intmax_t)sizeof(sec_varlist[sqv(q,f)].value) + || (intmax_t)strnlen(r, sizeof(retbuf)) > (intmax_t)sizeof(sec_varlist[sqv(q,f)].value) + ) { + upsdebugx(1, "%s: value was truncated", __func__); + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic pop +#endif + sec_setinfo(sqv(q,f), r); + } + + /* If SEC VAR is alarm and it's on, add it to the alarm property */ + if (sec_varlist[sqv(q,f)].flags & FLAG_ALARM && strcmp(r,"1")== 0) { + alarm_set(sec_varlist[sqv(q,f)].name); + } + } + + if (n == NULL) break; + r = n+1; } - - /* If SEC VAR is alarm and its on, add it to the alarm property */ - - if (sec_varlist[sqv(q,f)].flags & FLAG_ALARM && strcmp(r,"1")== 0) { - alarm_set(sec_varlist[sqv(q,f)].name); } - - } - - - if (n == NULL) break; - r = n+1; - } } - } +} void upsdrv_initinfo(void) { - int msglen, v; - char *a,*p,avail_list[300]; - - /* find out which variables/commands this UPS supports */ - msglen = 0; - sec_cmd(SEC_POLLCMD, SEC_AVAILP1, avail_list, &msglen); - p = avail_list + msglen; - if (p != avail_list) *p++ = ','; - msglen = 0; - sec_cmd(SEC_POLLCMD, SEC_AVAILP2, p, &msglen); - *(p+msglen) = '\0'; - - - if (strlen(avail_list) == 0){ - fatalx(EXIT_FAILURE, "No available variables found!");} - a = avail_list; - while ((p = strtok(a, ",")) != NULL) { - a = NULL; - v = atoi(p); - /* don't bother adding a write-only variable */ - if (sec_varlist[v].flags == FLAG_WONLY) continue; - addquery(sec_varlist[v].cmd, sec_varlist[v].field, v, sec_varlist[v].poll); - } - - /* poll one time values */ - - sec_poll(FLAG_POLLONCE); - - printf("UPS: %s %s\n", dstate_getinfo("ups.mfr"), dstate_getinfo("ups.model")); - - + ssize_t msglen; + int v; + char *a, *p, avail_list[300]; + + /* find out which variables/commands this UPS supports */ + msglen = 0; + sec_cmd(SEC_POLLCMD, SEC_AVAILP1, avail_list, &msglen); + p = avail_list + msglen; + if (p != avail_list) *p++ = ','; + msglen = 0; + sec_cmd(SEC_POLLCMD, SEC_AVAILP2, p, &msglen); + *(p+msglen) = '\0'; + + if (strlen(avail_list) == 0) { + fatalx(EXIT_FAILURE, "No available variables found!"); + } + a = avail_list; + while ((p = strtok(a, ",")) != NULL) { + a = NULL; + v = atoi(p); + /* don't bother adding a write-only variable */ + if (sec_varlist[v].flags == FLAG_WONLY) continue; + addquery(sec_varlist[v].cmd, sec_varlist[v].field, v, sec_varlist[v].poll); + } + + /* poll one time values */ + sec_poll(FLAG_POLLONCE); + + printf("UPS: %s %s\n", dstate_getinfo("ups.mfr"), dstate_getinfo("ups.model")); } void upsdrv_updateinfo(void) { - - alarm_init(); /* poll status values values */ sec_poll(FLAG_POLL); alarm_commit(); update_pseudovars(); dstate_dataok(); - } void upsdrv_shutdown(void) { - int msg_len; + ssize_t msglen; char msgbuf[SMALLBUF]; - msg_len = snprintf(msgbuf, sizeof(msgbuf), "-1"); - sec_cmd(SEC_SETCMD, SEC_SHUTDOWN, msgbuf, &msg_len); + msglen = snprintf(msgbuf, sizeof(msgbuf), "-1"); + sec_cmd(SEC_SETCMD, SEC_SHUTDOWN, msgbuf, &msglen); - msg_len = snprintf(msgbuf, sizeof(msgbuf), "1"); - sec_cmd(SEC_SETCMD, SEC_AUTORESTART, msgbuf, &msg_len); + msglen = snprintf(msgbuf, sizeof(msgbuf), "1"); + sec_cmd(SEC_SETCMD, SEC_AUTORESTART, msgbuf, &msglen); - msg_len = snprintf(msgbuf, sizeof(msgbuf), "2"); - sec_cmd(SEC_SETCMD, SEC_SHUTTYPE,msgbuf, &msg_len); + msglen = snprintf(msgbuf, sizeof(msgbuf), "2"); + sec_cmd(SEC_SETCMD, SEC_SHUTTYPE,msgbuf, &msglen); - msg_len = snprintf(msgbuf, sizeof(msgbuf), "5"); - sec_cmd(SEC_SETCMD, SEC_SHUTDOWN, msgbuf, &msg_len); + msglen = snprintf(msgbuf, sizeof(msgbuf), "5"); + sec_cmd(SEC_SETCMD, SEC_SHUTDOWN, msgbuf, &msglen); } /* -static int instcmd(const char *cmdname, const char *extra) +int instcmd(const char *cmdname, const char *extra) { if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); @@ -338,40 +346,39 @@ void upsdrv_makevartable(void) /* addvar(VAR_VALUE, "foo", "Override foo setting"); */ } -void setup_serial(const char *port) +static void setup_serial(const char *port) { - char temp[140]; - int i,ret; - + char temp[140]; + int i; + ssize_t ret; - /* Detect the ups baudrate */ - - - for (i=0; i<5; i++) { - - ser_set_speed(upsfd, device_path,baud_rates[i].rate); - ret = ser_send(upsfd, "^P003MAN"); - ret = sec_upsrecv(temp); - if (ret >= -1) break; + /* Detect the ups baudrate */ + for (i=0; i<5; i++) { + ser_set_speed(upsfd, device_path, baud_rates[i].rate); + ret = ser_send(upsfd, "^P003MAN"); + ret = sec_upsrecv(temp); + if (ret >= -1) break; + } - } - if (i == 5) { - printf("Can't talk to UPS on port %s!\n",port); - printf("Check the cabling and portname and try again\n"); - printf("Please note that this driver only support UPS Models with SEC Protorol\n"); - ser_close(upsfd, device_path); - exit (1); - } -printf("Connected to UPS on %s baudrate: %d\n",port, baud_rates[i].name); + if (i == 5) { + printf("Can't talk to UPS on port %s!\n",port); + printf("Check the cabling and portname and try again\n"); + printf("Please note that this driver only support UPS Models with SEC Protocol\n"); + ser_close(upsfd, device_path); + exit (1); + } + else + printf("Connected to UPS on %s baudrate: %zu\n", + port, baud_rates[i].name); } void upsdrv_initups(void) { - upsfd = ser_open(device_path); - setup_serial(device_path); + upsfd = ser_open(device_path); + setup_serial(device_path); /* upsfd = ser_open(device_path); */ /* ser_set_speed(upsfd, device_path, B1200); */ - + /* probe ups type */ /* to get variables and flags from the command line, use this: @@ -399,12 +406,10 @@ void upsdrv_initups(void) /* the upsh handlers can't be done here, as they get initialized * shortly after upsdrv_initups returns to main. */ - - } void upsdrv_cleanup(void) { /* free(dynamic_mem); */ - ser_close(upsfd, device_path); + ser_close(upsfd, device_path); } diff --git a/drivers/gamatronic.h b/drivers/gamatronic.h index 85c3290..7760284 100644 --- a/drivers/gamatronic.h +++ b/drivers/gamatronic.h @@ -2,13 +2,13 @@ * * SEC UPS Driver ported to the new NUT API for Gamatronic UPS Usage. * - * Copyright (C) + * Copyright (C) * 2001 John Marley * 2002 Jules Taplin * 2002 Eric Lawson * 2005 Arnaud Quette * 2005 Nadav Moskovitch - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or @@ -24,7 +24,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ - + +#ifndef NUT_GAMATRONIC_H_SEEN +#define NUT_GAMATRONIC_H_SEEN 1 + #define SEC_MSG_STARTCHAR '^' #define SEC_POLLCMD 'P' #define SEC_SETCMD 'S' @@ -63,10 +66,11 @@ #define FLAG_POLL 0 /* For commands that polled normaly */ #define FLAG_POLLONCE 1 /* For commands that only polled once */ + /* Some baud rates for setup_serial() */ -struct { - int rate; - int name; +static struct { + speed_t rate; /* Value like B19200 defined in termios.h; note: NOT the bitrate numerically */ + size_t name; /* Actual rate... WHY is this "name" - number to print interactively? */ } baud_rates[] = { { B1200, 1200 }, { B2400, 2400 }, @@ -80,7 +84,7 @@ struct { /* macro for checking whether a variable is supported */ -struct { +typedef struct { const char *setcmd; /* INFO_x define from shared.h */ const char *name; /* Human readable text (also in shared-tables.h) */ int unit; /* Variable should be divided by this */ @@ -90,108 +94,110 @@ struct { int poll; /* poll flag */ int flags; /* Flags for addinfo() */ char value[SEC_MAX_VARSIZE]; -} sec_varlist[] = { - { "", "", 0, "", 0, 0, 0, 0 }, - /*setcmd name unit cmd field size poll flags */ - { "", "Awaiting Power ", 1, SEC_ALARMSTAT, 13, 2, 0, FLAG_ALARM}, - { "", "Bypass Bad ", 1, SEC_ALARMSTAT, 5, 2, 0, FLAG_ALARM}, - { "", "Charger Failure ", 1, SEC_ALARMSTAT, 8, 2, 0, FLAG_ALARM}, - { "", "Fan Failure ", 1, SEC_ALARMSTAT, 10, 2, 0, FLAG_ALARM}, - { "", "Fuse Failure ", 1, SEC_ALARMSTAT, 11, 2, 0, FLAG_ALARM}, - { "", "General Fault ", 1, SEC_ALARMSTAT, 12, 2, 0, FLAG_ALARM}, - { "", "Input Bad ", 1, SEC_ALARMSTAT, 2, 2, 0, FLAG_ALARM}, - { "", "Output Bad ", 1, SEC_ALARMSTAT, 3, 2, 0, FLAG_ALARM}, - { "", "Output Off ", 1, SEC_ALARMSTAT, 6, 2, 0, FLAG_ALARM}, - { "", "Overload ", 1, SEC_ALARMSTAT, 4, 2, 0, FLAG_ALARM}, - { "", "Shutdown Imminent ", 1, SEC_ALARMSTAT, 15, 2, 0, FLAG_ALARM}, - { "", "Shutdown Pending ", 1, SEC_ALARMSTAT, 14, 2, 0, FLAG_ALARM}, - { "", "System Off ", 1, SEC_ALARMSTAT, 9, 2, 0, FLAG_ALARM}, - { "", "Temperature ", 1, SEC_ALARMSTAT, 1, 2, 0, FLAG_ALARM}, - { "", "UPS Shutdown ", 1, SEC_ALARMSTAT, 7, 2, 0, FLAG_ALARM}, - { "", "Audible Alarm", 1, SEC_NOMINAL, 8, 4, FLAG_POLLONCE, FLAG_RW}, - { "", "Auto Restart", 1, SEC_AUTORESTART, 1, 2, FLAG_POLLONCE, FLAG_RW}, - { "", "Battery Charge", 1, SEC_BATTSTAT, 3, 4, 0, 0}, - { "", "Battery Condition", 1, SEC_BATTSTAT, 1, 3, 0, 0}, - { "battery.current", "Battery Current", 10, SEC_BATTSTAT, 8, 9999, 0, 0 }, - { "battery.date", "Battery Installed", 1, SEC_NOMINAL, 11, 8, FLAG_POLLONCE, FLAG_STRING}, - { "", "Battery Status", 1, SEC_BATTSTAT, 2, 3, 0, 0 }, - { "battery.temperature", "Battery Temperature", 1, SEC_BATTSTAT, 9, 99, 0, 0 }, - { "battery.voltage", "Battery Voltage", 10, SEC_BATTSTAT, 7, 9999, 0, 0 }, - { "", "Bypass Current 1", 10, SEC_BYPASSSTAT, 4, 9999, 0, 0 }, - { "", "Bypass Current 2", 10, SEC_BYPASSSTAT, 7, 9999, 0, 0 }, - { "", "Bypass Current 3", 10, SEC_BYPASSSTAT, 10, 9999, 0, 0 }, - { "", "Bypass Frequency", 10, SEC_BYPASSSTAT, 1, 999, 0, 0 }, - { "", "Bypass Num Lines", 1, SEC_BYPASSSTAT, 2, 9, 0, 0 }, - { "", "Bypass Power 1", 1, SEC_BYPASSSTAT, 5, 99999, 0, 0 }, - { "", "Bypass Power 2", 1, SEC_BYPASSSTAT, 8, 99999, 0, 0 }, - { "", "Bypass Power 3", 1, SEC_BYPASSSTAT, 11, 99999, 0, 0 }, - { "", "Bypass Voltage 1", 10, SEC_BYPASSSTAT, 3, 9999, 0, 0 }, - { "", "Bypass Voltage 2", 10, SEC_BYPASSSTAT, 6, 9999, 0, 0 }, - { "", "Bypass Voltage 3", 10, SEC_BYPASSSTAT, 9, 9999, 0, 0 }, - { "battery.charge", "Estimated Charge", 1, SEC_BATTSTAT, 6, 999, 0, 0 }, - { "battery.runtime.low", "Estimated Minutes", 60, SEC_BATTSTAT, 5, 999, 0, FLAG_MULTI }, - { "input.transfer.high", "High Volt Xfer Pt", 1, SEC_NOMINAL, 10, 999, FLAG_POLLONCE, FLAG_STRING}, - { "ups.id", "Identification", 1, SEC_UPSID, 1, 64, FLAG_POLLONCE, FLAG_STRING}, - { "", "Input Current 1", 10, SEC_INPUTSTAT, 5, 9999, 0, 0 }, - { "", "Input Current 2", 10, SEC_INPUTSTAT, 9, 9999, 0, 0 }, - { "", "Input Current 3", 10, SEC_INPUTSTAT, 13, 9999, 0, 0 }, - { "input.frequency", "Input Frequency 1", 10, SEC_INPUTSTAT, 3, 999, 0, 0 }, - { "", "Input Frequency 2", 10, SEC_INPUTSTAT, 7, 999, 0, 0 }, - { "", "Input Frequency 3", 10, SEC_INPUTSTAT, 11, 999, 0, 0 }, - { "", "Input Line Bads", 1, SEC_INPUTSTAT, 1, 999, 0, 0 }, - { "", "Input Num Lines", 1, SEC_INPUTSTAT, 2, 9, 0, 0 }, - { "", "Input Power 1", 1, SEC_INPUTSTAT, 6, 99999, 0, 0 }, - { "", "Input Power 2", 1, SEC_INPUTSTAT, 10, 99999, 0, 0 }, - { "", "Input Power 3", 1, SEC_INPUTSTAT, 14, 99999, 0, 0 }, - { "input.voltage", "Input Voltage 1", 10, SEC_INPUTSTAT, 4, 9999, 0, 0 }, - { "", "Input Voltage 2", 10, SEC_INPUTSTAT, 8, 9999, 0, 0 }, - { "", "Input Voltage 3", 10, SEC_INPUTSTAT, 12, 9999, 0, 0 }, - { "input.transfer.low", "Low Volt Xfer Pt", 1, SEC_NOMINAL, 9, 999, FLAG_POLLONCE, FLAG_STRING}, - { "ups.mfr", "Manufacturer", 1, SEC_MFR, 1, 32, FLAG_POLLONCE, FLAG_STRING}, - { "ups.model", "Model", 1, SEC_MOD, 1, 64, FLAG_POLLONCE, FLAG_STRING}, - { "", "Nominal Battery Life", 1, SEC_NOMINAL, 12, 99999, FLAG_POLLONCE, FLAG_STRING}, - { "", "Nominal Input Frequency", 10, SEC_NOMINAL, 2, 999, FLAG_POLLONCE, FLAG_RW}, - { "input.voltage.nominal", "Nominal Input Voltage", 1, SEC_NOMINAL, 1, 999, FLAG_POLLONCE, FLAG_STRING}, - { "", "Nominal Low Battery Time", 1, SEC_NOMINAL, 7, 99, FLAG_POLLONCE, FLAG_STRING}, - { "", "Nominal Output Frequency", 10, SEC_NOMINAL, 4, 999, FLAG_POLLONCE, FLAG_RW}, - { "", "Nominal Output Power", 1, SEC_NOMINAL, 6, 99999, FLAG_POLLONCE, FLAG_STRING}, - { "", "Nominal Output Voltage", 1, SEC_NOMINAL, 3, 999, FLAG_POLLONCE, FLAG_STRING}, - { "ups.power.nominal", "Nominal VA Rating", 1, SEC_NOMINAL, 5, 99999, FLAG_POLLONCE, FLAG_STRING}, - { "output.current", "Output Current 1", 10, SEC_OUTPUTSTAT, 5, 9999, 0, 0 }, - { "", "Output Current 2", 10, SEC_OUTPUTSTAT, 9, 9999, 0, 0 }, - { "", "Output Current 3", 10, SEC_OUTPUTSTAT, 13, 9999, 0, 0 }, - { "output.frequency", "Output Frequency", 10, SEC_OUTPUTSTAT, 2, 999, 0, 0 }, - { "ups.load", "Output Load 1", 1, SEC_OUTPUTSTAT, 7, 999, 0, 0 }, - { "", "Output Load 2", 1, SEC_OUTPUTSTAT, 11, 999, 0, 0 }, - { "", "Output Load 3", 1, SEC_OUTPUTSTAT, 15, 999, 0, 0 }, - { "", "Output Num Lines", 1, SEC_OUTPUTSTAT, 3, 9, 0, 0 }, - { "", "Output Power 1", 1, SEC_OUTPUTSTAT, 6, 99999, 0, 0 }, - { "", "Output Power 2", 1, SEC_OUTPUTSTAT, 10, 99999, 0, 0 }, - { "", "Output Power 3", 1, SEC_OUTPUTSTAT, 14, 99999, 0, 0 }, - { "", "Output Source", 1, SEC_OUTPUTSTAT, 1, 6, 0, 0}, - { "output.voltage", "Output Voltage 1", 10, SEC_OUTPUTSTAT, 4, 9999, 0, 0 }, - { "", "Output Voltage 2", 10, SEC_OUTPUTSTAT, 8, 9999, 0, 0 }, - { "", "Output Voltage 3", 10, SEC_OUTPUTSTAT, 12, 9999, 0, 0 }, - { "", "Reboot With Duration", 1, SEC_REBOOT, 1, 9999999, FLAG_POLLONCE, FLAG_WONLY}, - { "battery.runtime", "Seconds on Battery", 1, SEC_BATTSTAT, 4, 99999, 0, 0 }, - { "", "Shutdown Type", 1, SEC_SHUTTYPE, 1, 2, FLAG_POLLONCE, FLAG_RW}, - { "ups.delay.shutdown", "Shutdown After Delay", 1, SEC_STARTDELAY, 1, 9999999, FLAG_POLLONCE, FLAG_WONLY}, - { "ups.firmware", "Software Version", 1, SEC_VERSION, 1, 32, FLAG_POLLONCE, FLAG_STRING}, - { "", "Startup After Delay", 1, SEC_STARTDELAY, 1, 9999999, FLAG_POLLONCE, FLAG_WONLY}, - { "", "Test Results Detail", 1, SEC_TESTRESULT, 2, 64, FLAG_POLLONCE, FLAG_STRING}, - { "", "Test Results Summary", 1, SEC_TESTRESULT, 1, 6, FLAG_POLLONCE, 0}, - { "", "Test Type", 1, SEC_TEST, 1, 5, FLAG_POLLONCE, FLAG_WONLY}, - { "", "Baud Rate", 1, SEC_BAUDRATE, 1, 19200, FLAG_POLLONCE, FLAG_RW}, +} sec_varlist_t; + +static sec_varlist_t sec_varlist[] = { + { "", "", 0, "", 0, 0, 0, 0, "" }, + /*setcmd name unit cmd field size poll flags value */ + { "", "Awaiting Power ", 1, SEC_ALARMSTAT, 13, 2, 0, FLAG_ALARM, ""}, + { "", "Bypass Bad ", 1, SEC_ALARMSTAT, 5, 2, 0, FLAG_ALARM, ""}, + { "", "Charger Failure ", 1, SEC_ALARMSTAT, 8, 2, 0, FLAG_ALARM, ""}, + { "", "Fan Failure ", 1, SEC_ALARMSTAT, 10, 2, 0, FLAG_ALARM, ""}, + { "", "Fuse Failure ", 1, SEC_ALARMSTAT, 11, 2, 0, FLAG_ALARM, ""}, + { "", "General Fault ", 1, SEC_ALARMSTAT, 12, 2, 0, FLAG_ALARM, ""}, + { "", "Input Bad ", 1, SEC_ALARMSTAT, 2, 2, 0, FLAG_ALARM, ""}, + { "", "Output Bad ", 1, SEC_ALARMSTAT, 3, 2, 0, FLAG_ALARM, ""}, + { "", "Output Off ", 1, SEC_ALARMSTAT, 6, 2, 0, FLAG_ALARM, ""}, + { "", "Overload ", 1, SEC_ALARMSTAT, 4, 2, 0, FLAG_ALARM, ""}, + { "", "Shutdown Imminent ", 1, SEC_ALARMSTAT, 15, 2, 0, FLAG_ALARM, ""}, + { "", "Shutdown Pending ", 1, SEC_ALARMSTAT, 14, 2, 0, FLAG_ALARM, ""}, + { "", "System Off ", 1, SEC_ALARMSTAT, 9, 2, 0, FLAG_ALARM, ""}, + { "", "Temperature ", 1, SEC_ALARMSTAT, 1, 2, 0, FLAG_ALARM, ""}, + { "", "UPS Shutdown ", 1, SEC_ALARMSTAT, 7, 2, 0, FLAG_ALARM, ""}, + { "", "Audible Alarm", 1, SEC_NOMINAL, 8, 4, FLAG_POLLONCE, FLAG_RW, ""}, + { "", "Auto Restart", 1, SEC_AUTORESTART, 1, 2, FLAG_POLLONCE, FLAG_RW, ""}, + { "", "Battery Charge", 1, SEC_BATTSTAT, 3, 4, 0, 0, ""}, + { "", "Battery Condition", 1, SEC_BATTSTAT, 1, 3, 0, 0, ""}, + { "battery.current", "Battery Current", 10, SEC_BATTSTAT, 8, 9999, 0, 0, ""}, + { "battery.date", "Battery Installed", 1, SEC_NOMINAL, 11, 8, FLAG_POLLONCE, FLAG_STRING, ""}, + { "", "Battery Status", 1, SEC_BATTSTAT, 2, 3, 0, 0, ""}, + { "battery.temperature", "Battery Temperature", 1, SEC_BATTSTAT, 9, 99, 0, 0, ""}, + { "battery.voltage", "Battery Voltage", 10, SEC_BATTSTAT, 7, 9999, 0, 0, ""}, + { "", "Bypass Current 1", 10, SEC_BYPASSSTAT, 4, 9999, 0, 0, ""}, + { "", "Bypass Current 2", 10, SEC_BYPASSSTAT, 7, 9999, 0, 0, ""}, + { "", "Bypass Current 3", 10, SEC_BYPASSSTAT, 10, 9999, 0, 0, ""}, + { "", "Bypass Frequency", 10, SEC_BYPASSSTAT, 1, 999, 0, 0, ""}, + { "", "Bypass Num Lines", 1, SEC_BYPASSSTAT, 2, 9, 0, 0, ""}, + { "", "Bypass Power 1", 1, SEC_BYPASSSTAT, 5, 99999, 0, 0, ""}, + { "", "Bypass Power 2", 1, SEC_BYPASSSTAT, 8, 99999, 0, 0, ""}, + { "", "Bypass Power 3", 1, SEC_BYPASSSTAT, 11, 99999, 0, 0, ""}, + { "", "Bypass Voltage 1", 10, SEC_BYPASSSTAT, 3, 9999, 0, 0, ""}, + { "", "Bypass Voltage 2", 10, SEC_BYPASSSTAT, 6, 9999, 0, 0, ""}, + { "", "Bypass Voltage 3", 10, SEC_BYPASSSTAT, 9, 9999, 0, 0, ""}, + { "battery.charge", "Estimated Charge", 1, SEC_BATTSTAT, 6, 999, 0, 0, ""}, + { "battery.runtime.low", "Estimated Minutes", 60, SEC_BATTSTAT, 5, 999, 0, FLAG_MULTI, ""}, + { "input.transfer.high", "High Volt Xfer Pt", 1, SEC_NOMINAL, 10, 999, FLAG_POLLONCE, FLAG_STRING, ""}, + { "ups.id", "Identification", 1, SEC_UPSID, 1, 64, FLAG_POLLONCE, FLAG_STRING, ""}, + { "", "Input Current 1", 10, SEC_INPUTSTAT, 5, 9999, 0, 0, ""}, + { "", "Input Current 2", 10, SEC_INPUTSTAT, 9, 9999, 0, 0, ""}, + { "", "Input Current 3", 10, SEC_INPUTSTAT, 13, 9999, 0, 0, ""}, + { "input.frequency", "Input Frequency 1", 10, SEC_INPUTSTAT, 3, 999, 0, 0, ""}, + { "", "Input Frequency 2", 10, SEC_INPUTSTAT, 7, 999, 0, 0, ""}, + { "", "Input Frequency 3", 10, SEC_INPUTSTAT, 11, 999, 0, 0, ""}, + { "", "Input Line Bads", 1, SEC_INPUTSTAT, 1, 999, 0, 0, ""}, + { "", "Input Num Lines", 1, SEC_INPUTSTAT, 2, 9, 0, 0, ""}, + { "", "Input Power 1", 1, SEC_INPUTSTAT, 6, 99999, 0, 0, ""}, + { "", "Input Power 2", 1, SEC_INPUTSTAT, 10, 99999, 0, 0, ""}, + { "", "Input Power 3", 1, SEC_INPUTSTAT, 14, 99999, 0, 0, ""}, + { "input.voltage", "Input Voltage 1", 10, SEC_INPUTSTAT, 4, 9999, 0, 0, ""}, + { "", "Input Voltage 2", 10, SEC_INPUTSTAT, 8, 9999, 0, 0, ""}, + { "", "Input Voltage 3", 10, SEC_INPUTSTAT, 12, 9999, 0, 0, ""}, + { "input.transfer.low", "Low Volt Xfer Pt", 1, SEC_NOMINAL, 9, 999, FLAG_POLLONCE, FLAG_STRING, ""}, + { "ups.mfr", "Manufacturer", 1, SEC_MFR, 1, 32, FLAG_POLLONCE, FLAG_STRING, ""}, + { "ups.model", "Model", 1, SEC_MOD, 1, 64, FLAG_POLLONCE, FLAG_STRING, ""}, + { "", "Nominal Battery Life", 1, SEC_NOMINAL, 12, 99999, FLAG_POLLONCE, FLAG_STRING, ""}, + { "", "Nominal Input Frequency", 10, SEC_NOMINAL, 2, 999, FLAG_POLLONCE, FLAG_RW, ""}, + { "input.voltage.nominal", "Nominal Input Voltage", 1, SEC_NOMINAL, 1, 999, FLAG_POLLONCE, FLAG_STRING, ""}, + { "", "Nominal Low Battery Time", 1, SEC_NOMINAL, 7, 99, FLAG_POLLONCE, FLAG_STRING, ""}, + { "", "Nominal Output Frequency", 10, SEC_NOMINAL, 4, 999, FLAG_POLLONCE, FLAG_RW, ""}, + { "", "Nominal Output Power", 1, SEC_NOMINAL, 6, 99999, FLAG_POLLONCE, FLAG_STRING, ""}, + { "", "Nominal Output Voltage", 1, SEC_NOMINAL, 3, 999, FLAG_POLLONCE, FLAG_STRING, ""}, + { "ups.power.nominal", "Nominal VA Rating", 1, SEC_NOMINAL, 5, 99999, FLAG_POLLONCE, FLAG_STRING, ""}, + { "output.current", "Output Current 1", 10, SEC_OUTPUTSTAT, 5, 9999, 0, 0, ""}, + { "", "Output Current 2", 10, SEC_OUTPUTSTAT, 9, 9999, 0, 0, ""}, + { "", "Output Current 3", 10, SEC_OUTPUTSTAT, 13, 9999, 0, 0, ""}, + { "output.frequency", "Output Frequency", 10, SEC_OUTPUTSTAT, 2, 999, 0, 0, ""}, + { "ups.load", "Output Load 1", 1, SEC_OUTPUTSTAT, 7, 999, 0, 0, ""}, + { "", "Output Load 2", 1, SEC_OUTPUTSTAT, 11, 999, 0, 0, ""}, + { "", "Output Load 3", 1, SEC_OUTPUTSTAT, 15, 999, 0, 0, ""}, + { "", "Output Num Lines", 1, SEC_OUTPUTSTAT, 3, 9, 0, 0, ""}, + { "", "Output Power 1", 1, SEC_OUTPUTSTAT, 6, 99999, 0, 0, ""}, + { "", "Output Power 2", 1, SEC_OUTPUTSTAT, 10, 99999, 0, 0, ""}, + { "", "Output Power 3", 1, SEC_OUTPUTSTAT, 14, 99999, 0, 0, ""}, + { "", "Output Source", 1, SEC_OUTPUTSTAT, 1, 6, 0, 0, ""}, + { "output.voltage", "Output Voltage 1", 10, SEC_OUTPUTSTAT, 4, 9999, 0, 0, ""}, + { "", "Output Voltage 2", 10, SEC_OUTPUTSTAT, 8, 9999, 0, 0, ""}, + { "", "Output Voltage 3", 10, SEC_OUTPUTSTAT, 12, 9999, 0, 0, ""}, + { "", "Reboot With Duration", 1, SEC_REBOOT, 1, 9999999, FLAG_POLLONCE, FLAG_WONLY, ""}, + { "battery.runtime", "Seconds on Battery", 1, SEC_BATTSTAT, 4, 99999, 0, 0, ""}, + { "", "Shutdown Type", 1, SEC_SHUTTYPE, 1, 2, FLAG_POLLONCE, FLAG_RW, ""}, + { "ups.delay.shutdown", "Shutdown After Delay", 1, SEC_STARTDELAY, 1, 9999999, FLAG_POLLONCE, FLAG_WONLY, ""}, + { "ups.firmware", "Software Version", 1, SEC_VERSION, 1, 32, FLAG_POLLONCE, FLAG_STRING, ""}, + { "", "Startup After Delay", 1, SEC_STARTDELAY, 1, 9999999, FLAG_POLLONCE, FLAG_WONLY, ""}, + { "", "Test Results Detail", 1, SEC_TESTRESULT, 2, 64, FLAG_POLLONCE, FLAG_STRING, ""}, + { "", "Test Results Summary", 1, SEC_TESTRESULT, 1, 6, FLAG_POLLONCE, 0, ""}, + { "", "Test Type", 1, SEC_TEST, 1, 5, FLAG_POLLONCE, FLAG_WONLY, ""}, + { "", "Baud Rate", 1, SEC_BAUDRATE, 1, 19200, FLAG_POLLONCE, FLAG_RW, ""}, }; /* a type for the supported variables */ #define SEC_QUERYLIST_LEN 17 #define SEC_MAXFIELDS 16 -#define SEC_POLL 1 -#define SEC_POLLONCE 0 +#define SEC_POLL 1 +#define SEC_POLLONCE 0 -struct { +static struct { const char *command; /* sec command */ int varnum[SEC_MAXFIELDS]; /* sec variable number for each field */ int pollflag; @@ -199,3 +205,4 @@ struct { #define sqv(a,b) sec_querylist[a].varnum[b] +#endif /* NUT_GAMATRONIC_H_SEEN */ diff --git a/drivers/generic_modbus.c b/drivers/generic_modbus.c new file mode 100644 index 0000000..478e838 --- /dev/null +++ b/drivers/generic_modbus.c @@ -0,0 +1,1099 @@ +/* generic_modbus.c - Driver for generic UPS connected via modbus RIO + * + * Copyright (C) + * 2021 Dimitris Economou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "generic_modbus.h" +#include +#include + +#define DRIVER_NAME "NUT Generic Modbus driver" +#define DRIVER_VERSION "0.03" + +/* variables */ +static modbus_t *mbctx = NULL; /* modbus memory context */ +static sigattr_t sigar[NUMOF_SIG_STATES]; /* array of ups signal attributes */ +static int errcnt = 0; /* modbus access error counter */ + +static char *device_mfr = DEVICE_MFR; /* device manufacturer */ +static char *device_model = DEVICE_MODEL; /* device model */ +static int ser_baud_rate = BAUD_RATE; /* serial port baud rate */ +static char ser_parity = PARITY; /* serial port parity */ +static int ser_data_bit = DATA_BIT; /* serial port data bit */ +static int ser_stop_bit = STOP_BIT; /* serial port stop bit */ +static int rio_slave_id = MODBUS_SLAVE_ID; /* set device ID to default value */ +static int FSD_pulse_duration = SHTDOWN_PULSE_DURATION; /* set the FSD pulse duration */ +static uint32_t mod_resp_to_s = MODRESP_TIMEOUT_s; /* set the modbus response time out (s) */ +static uint32_t mod_resp_to_us = MODRESP_TIMEOUT_us; /* set the modbus response time out (us) */ +static uint32_t mod_byte_to_s = MODBYTE_TIMEOUT_s; /* set the modbus byte time out (us) */ +static uint32_t mod_byte_to_us = MODBYTE_TIMEOUT_us; /* set the modbus byte time out (us) */ + + +/* get config vars set by -x or defined in ups.conf driver section */ +void get_config_vars(void); + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port); + +/* reconnect upon communication error */ +void modbus_reconnect(void); + +/* modbus register read function */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data); + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg); + +/* read signal status */ +int get_signal_state(devstate_t state); + +/* count the time elapsed since start */ +long time_elapsed(struct timeval *start); + +int register_write(modbus_t *mb, int addr, regtype_t type, void *data); + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Dimitris Economou \n", + DRV_BETA, + {NULL} +}; + +/* + * driver functions + */ + +/* initialize ups driver information */ +void upsdrv_initinfo(void) { + upsdebugx(2, "upsdrv_initinfo"); + + /* set device information */ + dstate_setinfo("device.mfr", "%s", device_mfr); + dstate_setinfo("device.model", "%s", device_model); + + /* register instant commands */ + if (sigar[FSD_T].addr != NOTUSED) { + dstate_addcmd("load.off"); + } + + /* set callback for instant commands */ + upsh.instcmd = upscmd; +} + +/* open serial connection and connect to modbus RIO */ +void upsdrv_initups(void) +{ + int rval; + upsdebugx(2, "upsdrv_initups"); + + get_config_vars(); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: error(%s)", modbus_strerror(errno)); + } + + /* set modbus response timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { + /* Older libmodbus API (with timeval), and we have + * checked at configure time that we can put uint32_t + * into its fields. They are probably "long" on many + * systems as respectively time_t and suseconds_t - + * but that is not guaranteed; for more details see + * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_time.h.html + */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_resp_to_s; + to.tv_usec = mod_resp_to_us; + /* void */ modbus_set_response_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#else +# error "Can not use libmodbus API for timeouts" +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ + + /* set modbus byte time out */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_byte_to_s; + to.tv_usec = mod_byte_to_us; + /* void */ modbus_set_byte_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ +} + +/* update UPS signal state */ +void upsdrv_updateinfo(void) +{ + int rval; + int online = -1; /* keep online state */ + errcnt = 0; + + upsdebugx(2, "upsdrv_updateinfo"); + status_init(); /* initialize ups.status update */ + alarm_init(); /* initialize ups.alarm update */ + + /* + * update UPS status regarding MAINS state either via OL | OB. + * if both statuses are mapped to contacts then only OL is evaluated. + */ + if (sigar[OL_T].addr != NOTUSED) { + rval = get_signal_state(OL_T); + upsdebugx(2, "OL value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OL_T].noro)) { + status_set("OL"); + online = 1; + } else { + status_set("OB"); + online = 0; + + /* if DISCHRG state is not mapped to a contact and UPS is on + * batteries set status to DISCHRG state */ + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + + } + } else if (sigar[OB_T].addr != NOTUSED) { + rval = get_signal_state(OB_T); + upsdebugx(2, "OB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[OB_T].noro)) { + status_set("OB"); + online = 0; + if (sigar[DISCHRG_T].addr == NOTUSED) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } else { + status_set("OL"); + online = 1; + } + } + + /* + * update UPS status regarding CHARGING state via HB. HB is usually + * mapped to "ready" contact when closed indicates a charging state > 85% + */ + if (sigar[HB_T].addr != NOTUSED) { + rval = get_signal_state(HB_T); + upsdebugx(2, "HB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[HB_T].noro)) { + status_set("HB"); + dstate_setinfo("battery.charger.status", "resting"); + } else if (online == 1 && sigar[CHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } else if (online == 0 && sigar[DISCHRG_T].addr == NOTUSED && errcnt == 0) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* + * update UPS status regarding DISCHARGING state via LB. LB is mapped + * to "battery low" contact. + */ + if (sigar[LB_T].addr != NOTUSED) { + rval = get_signal_state(LB_T); + upsdebugx(2, "LB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[LB_T].noro)) { + status_set("LB"); + alarm_set("Low Battery (Charge)"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[RB_T].addr != NOTUSED) { + rval = get_signal_state(RB_T); + upsdebugx(2, "RB value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[RB_T].noro)) { + status_set("RB"); + alarm_set("Replace Battery"); + } + } + + /* + * update UPS status regarding battery HEALTH state via RB. RB is mapped + * to "replace battery" contact + */ + if (sigar[CHRG_T].addr != NOTUSED) { + rval = get_signal_state(CHRG_T); + upsdebugx(2, "CHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[CHRG_T].noro)) { + status_set("CHRG"); + dstate_setinfo("battery.charger.status", "charging"); + } + } else if (sigar[DISCHRG_T].addr != NOTUSED) { + rval = get_signal_state(DISCHRG_T); + upsdebugx(2, "DISCHRG value: %d", rval); + if (rval == -1) { + errcnt++; + } else if (rval == (1 ^ sigar[DISCHRG_T].noro)) { + status_set("DISCHRG"); + dstate_setinfo("battery.charger.status", "discharging"); + } + } + + /* check for communication errors */ + if (errcnt == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } else { + upsdebugx(2,"Communication errors: %d", errcnt); + dstate_datastale(); + } +} + +/* shutdown UPS */ +void upsdrv_shutdown(void) +{ + int rval; + int cnt = FSD_REPEAT_CNT; /* shutdown repeat counter */ + struct timeval start; + long etime; + + /* retry sending shutdown command on error */ + while ((rval = upscmd("load.off", NULL)) != STAT_INSTCMD_HANDLED && cnt > 0) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for an increasing time interval before sending shutdown command */ + while ((etime = time_elapsed(&start)) < ( FSD_REPEAT_INTRV / cnt)); + upsdebugx(2,"ERROR: load.off failed, wait for %lims, retries left: %d\n", etime, cnt - 1); + cnt--; + } + switch (rval) { + case STAT_INSTCMD_FAILED: + case STAT_INSTCMD_INVALID: + fatalx(EXIT_FAILURE, "shutdown failed"); + case STAT_INSTCMD_UNKNOWN: + fatalx(EXIT_FAILURE, "shutdown not supported"); + default: + break; + } + upslogx(LOG_INFO, "shutdown command executed"); +} + +/* print driver usage info */ +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "device_mfr", "device manufacturer"); + addvar(VAR_VALUE, "device_model", "device model"); + addvar(VAR_VALUE, "ser_baud_rate", "serial port baud rate"); + addvar(VAR_VALUE, "ser_parity", "serial port parity"); + addvar(VAR_VALUE, "ser_data_bit", "serial port data bit"); + addvar(VAR_VALUE, "ser_stop_bit", "serial port stop bit"); + addvar(VAR_VALUE, "rio_slave_id", "RIO modbus slave ID"); + addvar(VAR_VALUE, "mod_resp_to_s", "modbus response timeout (s)"); + addvar(VAR_VALUE, "mod_resp_to_us", "modbus response timeout (us)"); + addvar(VAR_VALUE, "mod_byte_to_s", "modbus byte timeout (s)"); + addvar(VAR_VALUE, "mod_byte_to_us", "modbus byte timeout (us)"); + addvar(VAR_VALUE, "OL_addr", "modbus address for OL state"); + addvar(VAR_VALUE, "OB_addr", "modbus address for OB state"); + addvar(VAR_VALUE, "LB_addr", "modbus address for LB state"); + addvar(VAR_VALUE, "HB_addr", "modbus address for HB state"); + addvar(VAR_VALUE, "RB_addr", "modbus address for RB state"); + addvar(VAR_VALUE, "CHRG_addr", "modbus address for CHRG state"); + addvar(VAR_VALUE, "DISCHRG_addr", "modbus address for DISCHRG state"); + addvar(VAR_VALUE, "FSD_addr", "modbus address for FSD command"); + addvar(VAR_VALUE, "OL_regtype", "modbus register type for OL state"); + addvar(VAR_VALUE, "OB_regtype", "modbus register type for OB state"); + addvar(VAR_VALUE, "LB_regtype", "modbus register type for LB state"); + addvar(VAR_VALUE, "HB_regtype", "modbus register type for HB state"); + addvar(VAR_VALUE, "RB_regtype", "modbus register type for RB state"); + addvar(VAR_VALUE, "CHRG_regtype", "modbus register type for CHRG state"); + addvar(VAR_VALUE, "DISCHRG_regtype", "modbus register type for DISCHRG state"); + addvar(VAR_VALUE, "FSD_regtype", "modbus register type for FSD command"); + addvar(VAR_VALUE, "OL_noro", "NO/NC configuration for OL state"); + addvar(VAR_VALUE, "OB_noro", "NO/NC configuration for OB state"); + addvar(VAR_VALUE, "LB_noro", "NO/NC configuration for LB state"); + addvar(VAR_VALUE, "HB_noro", "NO/NC configuration for HB state"); + addvar(VAR_VALUE, "RB_noro", "NO/NC configuration for RB state"); + addvar(VAR_VALUE, "CHRG_noro", "NO/NC configuration for CHRG state"); + addvar(VAR_VALUE, "DISCHRG_noro", "NO/NC configuration for DISCHRG state"); + addvar(VAR_VALUE, "FSD_noro", "NO/NC configuration for FSD state"); + addvar(VAR_VALUE, "FSD_pulse_duration", "FSD pulse duration"); +} + +/* close modbus connection and free modbus context allocated memory */ +void upsdrv_cleanup(void) +{ + if (mbctx != NULL) { + modbus_close(mbctx); + modbus_free(mbctx); + } +} + +/* + * driver support functions + */ + +/* Read a modbus register */ +int register_read(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + rval = modbus_read_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_B: + rval = modbus_read_input_bits(mb, addr, 1, (uint8_t *)data); + *(uint *)data = *(uint *)data & mask8; + break; + case INPUT_R: + rval = modbus_read_input_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + case HOLDING: + rval = modbus_read_registers(mb, addr, 1, (uint16_t *)data); + *(uint *)data = *(uint *)data & mask16; + break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: + upsdebugx(2,"ERROR: register_read: invalid register type %d\n", type); + break; +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_read: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* write a modbus register */ +int register_write(modbus_t *mb, int addr, regtype_t type, void *data) +{ + int rval = -1; + + /* register bit masks */ + uint mask8 = 0x000F; + uint mask16 = 0x00FF; + + switch (type) { + case COIL: + *(uint *)data = *(uint *)data & mask8; + rval = modbus_write_bit(mb, addr, *(uint8_t *)data); + break; + case HOLDING: + *(uint *)data = *(uint *)data & mask16; + rval = modbus_write_register(mb, addr, *(uint16_t *)data); + break; + + case INPUT_B: + case INPUT_R: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upsdebugx(2,"ERROR: register_write: invalid register type %d\n", type); + break; + } + if (rval == -1) { + upslogx(LOG_ERR,"ERROR:(%s) modbus_read: addr:0x%x, type:%8s, path:%s\n", + modbus_strerror(errno), + addr, + (type == COIL) ? "COIL" : + (type == INPUT_B) ? "INPUT_B" : + (type == INPUT_R) ? "INPUT_R" : "HOLDING", + device_path + ); + + /* on BROKEN PIPE error try to reconnect */ + if (errno == EPIPE) { + upsdebugx(2, "register_write: error(%s)", modbus_strerror(errno)); + modbus_reconnect(); + } + } + upsdebugx(3, "register addr: 0x%x, register type: %d read: %d",addr, type, *(uint *)data); + return rval; +} + +/* returns the time elapsed since start in milliseconds */ +long time_elapsed(struct timeval *start) +{ + long rval; + struct timeval end; + + rval = gettimeofday(&end, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "time_elapsed: %s", strerror(errno)); + } + if (start->tv_usec < end.tv_usec) { + suseconds_t nsec = (end.tv_usec - start->tv_usec) / 1000000 + 1; + end.tv_usec -= 1000000 * nsec; + end.tv_sec += nsec; + } + if (start->tv_usec - end.tv_usec > 1000000) { + suseconds_t nsec = (start->tv_usec - end.tv_usec) / 1000000; + end.tv_usec += 1000000 * nsec; + end.tv_sec -= nsec; + } + rval = (end.tv_sec - start->tv_sec) * 1000 + (end.tv_usec - start->tv_usec) / 1000; + + return rval; +} + +/* instant command triggered by upsd */ +int upscmd(const char *cmd, const char *arg) +{ + int rval; + int data; + struct timeval start; + long etime; + + if (!strcasecmp(cmd, "load.off")) { + if (sigar[FSD_T].addr != NOTUSED && + (sigar[FSD_T].type == COIL || sigar[FSD_T].type == HOLDING) + ) { + data = 1 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(2, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d", sigar[FSD_T].addr, data); + rval = STAT_INSTCMD_HANDLED; + } + + /* if pulse has been defined and rising edge was successful */ + if (FSD_pulse_duration != NOTUSED && rval == STAT_INSTCMD_HANDLED) { + rval = gettimeofday(&start, NULL); + if (rval < 0) { + upslogx(LOG_ERR, "upscmd: gettimeofday: %s", strerror(errno)); + } + + /* wait for FSD_pulse_duration ms */ + while ((etime = time_elapsed(&start)) < FSD_pulse_duration); + + data = 0 ^ sigar[FSD_T].noro; + rval = register_write(mbctx, sigar[FSD_T].addr, sigar[FSD_T].type, &data); + if (rval == -1) { + upslogx(LOG_ERR, "ERROR:(%s) modbus_write_register: addr:0x%08x, regtype: %d, path:%s\n", + modbus_strerror(errno), + sigar[FSD_T].addr, + sigar[FSD_T].type, + device_path + ); + upslogx(LOG_NOTICE, "load.off: failed (communication error) [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_FAILED; + } else { + upsdebugx(2, "load.off: addr: 0x%x, data: %d, elapsed time: %lims", + sigar[FSD_T].addr, + data, + etime + ); + rval = STAT_INSTCMD_HANDLED; + } + } + } else { + upslogx(LOG_NOTICE,"load.off: failed (FSD address undefined or invalid register type) [%s] [%s]", + cmd, + arg + ); + rval = STAT_INSTCMD_FAILED; + } + } else { + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmd, arg); + rval = STAT_INSTCMD_UNKNOWN; + } + return rval; +} + +/* read signal state from modbus RIO, returns 0|1 state or -1 on communication error */ +int get_signal_state(devstate_t state) +{ + int rval = -1; + int reg_val; + regtype_t rtype = 0; /* register type */ + int addr = -1; /* register address */ + + /* assign register address and type */ + switch (state) { + case OL_T: + addr = sigar[OL_T].addr; + rtype = sigar[OL_T].type; + break; + case OB_T: + addr = sigar[OB_T].addr; + rtype = sigar[OB_T].type; + break; + case LB_T: + addr = sigar[LB_T].addr; + rtype = sigar[LB_T].type; + break; + case HB_T: + addr = sigar[HB_T].addr; + rtype = sigar[HB_T].type; + break; + case RB_T: + addr = sigar[RB_T].addr; + rtype = sigar[RB_T].type; + break; + case CHRG_T: + addr = sigar[CHRG_T].addr; + rtype = sigar[CHRG_T].type; + break; + case DISCHRG_T: + addr = sigar[DISCHRG_T].addr; + rtype = sigar[DISCHRG_T].type; + break; + + case BYPASS_T: + case CAL_T: + case FSD_T: + case OFF_T: + case OVER_T: + case TRIM_T: + case BOOST_T: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + break; + } + + rval = register_read(mbctx, addr, rtype, ®_val); + if (rval > -1) { + rval = reg_val; + } + upsdebugx(3, "get_signal_state: state: %d", reg_val); + return rval; +} + +/* get driver configuration parameters */ +void get_config_vars() +{ + int i; /* local index */ + + /* initialize sigar table */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + sigar[i].addr = NOTUSED; + sigar[i].noro = 0; /* ON corresponds to 1 (closed contact) */ + } + + /* check if device manufacturer is set ang get the value */ + if (testvar("device_mfr")) { + device_mfr = getval("device_mfr"); + } + upsdebugx(2, "device_mfr %s", device_mfr); + + /* check if device model is set ang get the value */ + if (testvar("device_model")) { + device_model = getval("device_model"); + } + upsdebugx(2, "device_model %s", device_model); + + /* check if serial baud rate is set ang get the value */ + if (testvar("ser_baud_rate")) { + ser_baud_rate = (int)strtol(getval("ser_baud_rate"), NULL, 10); + } + upsdebugx(2, "ser_baud_rate %d", ser_baud_rate); + + /* check if serial parity is set ang get the value */ + if (testvar("ser_parity")) { + /* Dereference the char* we get */ + char *sp = getval("ser_parity"); + if (sp) { + /* TODO? Sanity-check the char we get? */ + ser_parity = *sp; + } else { + upsdebugx(2, "Could not determine ser_parity, will keep default"); + } + } + upsdebugx(2, "ser_parity %c", ser_parity); + + /* check if serial data bit is set ang get the value */ + if (testvar("ser_data_bit")) { + ser_data_bit = (int)strtol(getval("ser_data_bit"), NULL, 10); + } + upsdebugx(2, "ser_data_bit %d", ser_data_bit); + + /* check if serial stop bit is set ang get the value */ + if (testvar("ser_stop_bit")) { + ser_stop_bit = (int)strtol(getval("ser_stop_bit"), NULL, 10); + } + upsdebugx(2, "ser_stop_bit %d", ser_stop_bit); + + /* check if device ID is set ang get the value */ + if (testvar("rio_slave_id")) { + rio_slave_id = (int)strtol(getval("rio_slave_id"), NULL, 10); + } + upsdebugx(2, "rio_slave_id %d", rio_slave_id); + + /* check if response time out (s) is set ang get the value */ + if (testvar("mod_resp_to_s")) { + mod_resp_to_s = (uint32_t)strtol(getval("mod_resp_to_s"), NULL, 10); + } + upsdebugx(2, "mod_resp_to_s %d", mod_resp_to_s); + + /* check if response time out (us) is set ang get the value */ + if (testvar("mod_resp_to_us")) { + mod_resp_to_us = (uint32_t) strtol(getval("mod_resp_to_us"), NULL, 10); + if (mod_resp_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_resp_to_us %d", mod_resp_to_us); + } + } + upsdebugx(2, "mod_resp_to_us %d", mod_resp_to_us); + + /* check if byte time out (s) is set ang get the value */ + if (testvar("mod_byte_to_s")) { + mod_byte_to_s = (uint32_t)strtol(getval("mod_byte_to_s"), NULL, 10); + } + upsdebugx(2, "mod_byte_to_s %d", mod_byte_to_s); + + /* check if byte time out (us) is set ang get the value */ + if (testvar("mod_byte_to_us")) { + mod_byte_to_us = (uint32_t) strtol(getval("mod_byte_to_us"), NULL, 10); + if (mod_byte_to_us > 999999) { + fatalx(EXIT_FAILURE, "get_config_vars: Invalid mod_byte_to_us %d", mod_byte_to_us); + } + } + upsdebugx(2, "mod_byte_to_us %d", mod_byte_to_us); + + /* check if OL address is set and get the value */ + if (testvar("OL_addr")) { + sigar[OL_T].addr = (int)strtol(getval("OL_addr"), NULL, 0); + if (testvar("OL_noro")) { + sigar[OL_T].noro = (int)strtol(getval("OL_noro"), NULL, 10); + if (sigar[OL_T].noro != 1) { + sigar[OL_T].noro = 0; + } + } + } + + /* check if OL register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OL_regtype")) { + sigar[OL_T].type = (unsigned int)strtol(getval("OL_regtype"), NULL, 10); + if (sigar[OL_T].type < COIL || sigar[OL_T].type > HOLDING) { + sigar[OL_T].type = INPUT_B; + } + } else { + sigar[OL_T].type = INPUT_B; + } + + /* check if OB address is set and get the value */ + if (testvar("OB_addr")) { + sigar[OB_T].addr = (int)strtol(getval("OB_addr"), NULL, 0); + } + if (testvar("OB_noro")) { + sigar[OB_T].noro = (int)strtol(getval("OB_noro"), NULL, 10); + if (sigar[OB_T].noro != 1) { + sigar[OB_T].noro = 0; + } + } + + /* check if OB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("OB_regtype")) { + sigar[OB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[OB_T].type < COIL || sigar[OB_T].type > HOLDING) { + sigar[OB_T].type = INPUT_B; + } + } else { + sigar[OB_T].type = INPUT_B; + } + + /* check if LB address is set and get the value */ + if (testvar("LB_addr")) { + sigar[LB_T].addr = (int)strtol(getval("LB_addr"), NULL, 0); + if (testvar("LB_noro")) { + sigar[LB_T].noro = (int)strtol(getval("LB_noro"), NULL, 10); + if (sigar[LB_T].noro != 1) { + sigar[LB_T].noro = 0; + } + } + } + + /* check if LB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("LB_regtype")) { + sigar[LB_T].type = (unsigned int)strtol(getval("OB_regtype"), NULL, 10); + if (sigar[LB_T].type < COIL || sigar[LB_T].type > HOLDING) { + sigar[LB_T].type = INPUT_B; + } + } else { + sigar[LB_T].type = INPUT_B; + } + + /* check if HB address is set and get the value */ + if (testvar("HB_addr")) { + sigar[HB_T].addr = (int)strtol(getval("HB_addr"), NULL, 0); + if (testvar("HB_noro")) { + sigar[HB_T].noro = (int)strtol(getval("HB_noro"), NULL, 10); + if (sigar[HB_T].noro != 1) { + sigar[HB_T].noro = 0; + } + } + } + + /* check if HB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("HB_regtype")) { + sigar[HB_T].type = (unsigned int)strtol(getval("HB_regtype"), NULL, 10); + if (sigar[HB_T].type < COIL || sigar[HB_T].type > HOLDING) { + sigar[HB_T].type = INPUT_B; + } + } else { + sigar[HB_T].type = INPUT_B; + } + + /* check if RB address is set and get the value */ + if (testvar("RB_addr")) { + sigar[RB_T].addr = (int)strtol(getval("RB_addr"), NULL, 0); + if (testvar("RB_noro")) { + sigar[RB_T].noro = (int)strtol(getval("RB_noro"), NULL, 10); + if (sigar[RB_T].noro != 1) { + sigar[RB_T].noro = 0; + } + } + } + + /* check if RB register type is set and get the value otherwise set to INPUT_B */ + if (testvar("RB_regtype")) { + sigar[RB_T].type = (unsigned int)strtol(getval("RB_regtype"), NULL, 10); + if (sigar[RB_T].type < COIL || sigar[RB_T].type > HOLDING) { + sigar[RB_T].type = INPUT_B; + } + } else { + sigar[RB_T].type = INPUT_B; + } + + /* check if CHRG address is set and get the value */ + if (testvar("CHRG_addr")) { + sigar[CHRG_T].addr = (int)strtol(getval("CHRG_addr"), NULL, 0); + if (testvar("CHRG_noro")) { + sigar[CHRG_T].noro = (int)strtol(getval("CHRG_noro"), NULL, 10); + if (sigar[CHRG_T].noro != 1) { + sigar[CHRG_T].noro = 0; + } + } + } + + /* check if CHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("CHRG_regtype")) { + sigar[CHRG_T].type = (unsigned int)strtol(getval("CHRG_regtype"), NULL, 10); + if (sigar[CHRG_T].type < COIL || sigar[CHRG_T].type > HOLDING) { + sigar[CHRG_T].type = INPUT_B; + } + } else { + sigar[CHRG_T].type = INPUT_B; + } + + /* check if DISCHRG address is set and get the value */ + if (testvar("DISCHRG_addr")) { + sigar[DISCHRG_T].addr = (int)strtol(getval("DISCHRG_addr"), NULL, 0); + if (testvar("DISCHRG_noro")) { + sigar[DISCHRG_T].noro = (int)strtol(getval("DISCHRG_noro"), NULL, 10); + if (sigar[DISCHRG_T].noro != 1) { + sigar[DISCHRG_T].noro = 0; + } + } + } + + /* check if DISCHRG register type is set and get the value otherwise set to INPUT_B */ + if (testvar("DISCHRG_regtype")) { + sigar[DISCHRG_T].type = (unsigned int)strtol(getval("DISCHRG_regtype"), NULL, 10); + if (sigar[DISCHRG_T].type < COIL || sigar[DISCHRG_T].type > HOLDING) { + sigar[DISCHRG_T].type = INPUT_B; + } + } else { + sigar[DISCHRG_T].type = INPUT_B; + } + + /* check if FSD address is set and get the value */ + if (testvar("FSD_addr")) { + sigar[FSD_T].addr = (int)strtol(getval("FSD_addr"), NULL, 0); + if (testvar("FSD_noro")) { + sigar[FSD_T].noro = (int)strtol(getval("FSD_noro"), NULL, 10); + if (sigar[FSD_T].noro != 1) { + sigar[FSD_T].noro = 0; + } + } + } + + /* check if FSD register type is set and get the value otherwise set to COIL */ + if (testvar("FSD_regtype")) { + sigar[FSD_T].type = (unsigned int)strtol(getval("FSD_regtype"), NULL, 10); + if (sigar[FSD_T].type < COIL || sigar[FSD_T].type > HOLDING) { + sigar[FSD_T].type = COIL; + } + } else { + sigar[FSD_T].type = COIL; + } + + /* check if FSD pulse duration is set and get the value */ + if (testvar("FSD_pulse_duration")) { + FSD_pulse_duration = (int) strtol(getval("FSD_pulse_duration"), NULL, 10); + } + upsdebugx(2, "FSD_pulse_duration %d", FSD_pulse_duration); + + /* debug loop over signal array */ + for (i = 0; i < NUMOF_SIG_STATES; i++) { + if (sigar[i].addr != NOTUSED) { + char *signame; + switch (i) { + case OL_T: + signame = "OL"; + break; + case OB_T: + signame = "OB"; + break; + case LB_T: + signame = "LB"; + break; + case HB_T: + signame = "HB"; + break; + case RB_T: + signame = "RB"; + break; + case FSD_T: + signame = "FSD"; + break; + case CHRG_T: + signame = "CHRG"; + break; + case DISCHRG_T: + signame = "DISCHRG"; + break; + default: + signame = "NOTUSED"; + break; + } + upsdebugx(2, "%s, addr:0x%x, type:%d", signame, sigar[i].addr, sigar[i].type); + } + } +} + +/* create a new modbus context based on connection type (serial or TCP) */ +modbus_t *modbus_new(const char *port) +{ + modbus_t *mb; + char *sp; + if (strstr(port, "/dev/tty") != NULL) { + mb = modbus_new_rtu(port, ser_baud_rate, ser_parity, ser_data_bit, ser_stop_bit); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_rtu: Unable to open serial port context\n"); + } + } else if ((sp = strchr(port, ':')) != NULL) { + char *tcp_port = xmalloc(sizeof(sp)); + strcpy(tcp_port, sp + 1); + *sp = '\0'; + mb = modbus_new_tcp(port, (int)strtoul(tcp_port, NULL, 10)); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + free(tcp_port); + } else { + mb = modbus_new_tcp(port, 502); + if (mb == NULL) { + upslogx(LOG_ERR, "modbus_new_tcp: Unable to connect to %s\n", port); + } + } + return mb; +} + +/* reconnect to modbus server upon connection error */ +void modbus_reconnect(void) +{ + int rval; + + upsdebugx(2, "modbus_reconnect, trying to reconnect to modbus server"); + + /* clear current modbus context */ + modbus_close(mbctx); + modbus_free(mbctx); + + /* open communication port */ + mbctx = modbus_new(device_path); + if (mbctx == NULL) { + fatalx(EXIT_FAILURE, "modbus_new_rtu: Unable to open communication port context"); + } + + /* set slave ID */ + rval = modbus_set_slave(mbctx, rio_slave_id); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_slave: Invalid modbus slave ID %d", rio_slave_id); + } + + /* connect to modbus device */ + if (modbus_connect(mbctx) == -1) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + + /* set modbus response timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_response_timeout(mbctx, mod_resp_to_s, mod_resp_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_response_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_resp_to_s; + to.tv_usec = mod_resp_to_us; + /* void */ modbus_set_response_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ + + /* set modbus byte timeout */ +#if (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32) || (defined NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields) + rval = modbus_set_byte_timeout(mbctx, mod_byte_to_s, mod_byte_to_us); + if (rval < 0) { + modbus_free(mbctx); + fatalx(EXIT_FAILURE, "modbus_set_byte_timeout: error(%s)", modbus_strerror(errno)); + } +#elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields) + { /* see comments above */ + struct timeval to; + memset(&to, 0, sizeof(struct timeval)); + to.tv_sec = mod_byte_to_s; + to.tv_usec = mod_byte_to_us; + /* void */ modbus_set_byte_timeout(mbctx, &to); + } +/* #elif (defined NUT_MODBUS_TIMEOUT_ARG_timeval) // some un-castable type in fields */ +#endif /* NUT_MODBUS_TIMEOUT_ARG_* */ +} diff --git a/drivers/generic_modbus.h b/drivers/generic_modbus.h new file mode 100644 index 0000000..3a6b8a5 --- /dev/null +++ b/drivers/generic_modbus.h @@ -0,0 +1,111 @@ +/* generic_modbus.h - Driver for generic UPS connected via modbus RIO + * + * Copyright (C) + * 2021 Dimitris Economou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUT_GENERIC_MODBUS_H +#define NUT_GENERIC_MODBUS_H + +/* UPS device details */ +#define DEVICE_MFR "UNKNOWN" +#define DEVICE_MODEL "unknown" + +/* serial access parameters */ +#define BAUD_RATE 9600 +#define PARITY 'N' +#define DATA_BIT 8 +#define STOP_BIT 1 + +/* + * modbus response and byte timeouts + * us: 1 - 999999 + */ +#define MODRESP_TIMEOUT_s 0 +#define MODRESP_TIMEOUT_us 200000 +#define MODBYTE_TIMEOUT_s 0 +#define MODBYTE_TIMEOUT_us 50000 + + +/* modbus access parameters */ +#define MODBUS_SLAVE_ID 5 + +/* shutdown repeat on error */ +#define FSD_REPEAT_CNT 3 + +/* shutdown repeat interval in ms */ +#define FSD_REPEAT_INTRV 1500 + +/* definition of register type */ +enum regtype { + COIL = 0, + INPUT_B, + INPUT_R, + HOLDING +}; +typedef enum regtype regtype_t; + +/* UPS device state enum */ +enum devstate { + OL_T = 0, + OB_T, + LB_T, + HB_T, + RB_T, + CHRG_T, + DISCHRG_T, + BYPASS_T, + CAL_T, + OFF_T, + OVER_T, + TRIM_T, + BOOST_T, + FSD_T +}; +typedef enum devstate devstate_t; + +/* UPS state signal attributes */ +struct sigattr { + int addr; /* register address */ + regtype_t type; /* register type */ + int noro; /* 1: normally open contact 0: normally closed contact. */ + /* noro is used to indicate the logical ON or OFF in regard + of the contact state. if noro is set to 1 then ON corresponds + to an open contact */ +}; +typedef struct sigattr sigattr_t; + +#define NUMOF_SIG_STATES 14 +#define NOTUSED -1 + +/* define the duration of the shutdown pulse */ +#define SHTDOWN_PULSE_DURATION NOTUSED + +/* + * associate PULS signals to NUT device states + * + * Ready contact <--> 1:HB, 0:CHRG + * Buffering contact <--> 1:OB, 0:OL + * Battery-low <--> 1:LB + * Replace Battery <--> 1:RB + * Inhibit buffering <--> 1:FSD + */ + + +#endif /* NUT_GENERIC_MODBUS_H */ diff --git a/drivers/genericups.c b/drivers/genericups.c index bc8ddc4..d6568dd 100644 --- a/drivers/genericups.c +++ b/drivers/genericups.c @@ -17,14 +17,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" /* must be first */ + #include #include "main.h" #include "serial.h" #include "genericups.h" +#include "nut_stdint.h" #define DRIVER_NAME "Generic contact-closure UPS driver" -#define DRIVER_VERSION "1.36" +#define DRIVER_VERSION "1.38" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -39,19 +42,30 @@ upsdrv_info_t upsdrv_info = { static void parse_output_signals(const char *value, int *line) { + int old_line = *line; /* parse signals the serial port can output */ *line = 0; + upsdebugx(4, "%s: enter", __func__); + + /* Note: for future drivers, please use strtok() or similar tokenizing + * methods, such that it is easier to spot configuration mistakes. With + * this code, a misspelled control line may go unnoticed. I'd fix it + * The Right Way (tm), but these UPSes are ancient. + */ if (strstr(value, "DTR") && !strstr(value, "-DTR")) { + upsdebugx(3, "%s: override DTR", __func__); *line |= TIOCM_DTR; } if (strstr(value, "RTS") && !strstr(value, "-RTS")) { + upsdebugx(3, "%s: override RTS", __func__); *line |= TIOCM_RTS; } if (strstr(value, "ST")) { + upsdebugx(3, "%s: override ST", __func__); *line |= TIOCM_ST; } @@ -70,20 +84,37 @@ static void parse_output_signals(const char *value, int *line) if (strstr(value, "DSR")) { fatalx(EXIT_FAILURE, "Can't override output with DSR (not an output)"); } + + if (strstr(value, "NULL") || strstr(value, "none")) { + upsdebugx(3, "%s: disable", __func__); + *line = 0; + } + + if(*line == old_line) { + upslogx(LOG_NOTICE, "%s: output overrides specified, but no effective difference - check for typos?", __func__); + } + + upsdebugx(4, "%s: exit", __func__); } static void parse_input_signals(const char *value, int *line, int *val) { /* parse signals the serial port can input */ + int old_line = *line, old_val = *val; *line = 0; *val = 0; + upsdebugx(4, "%s: enter", __func__); + if (strstr(value, "CTS")) { *line |= TIOCM_CTS; if (!strstr(value, "-CTS")) { + upsdebugx(3, "%s: override CTS (active low)", __func__); *val |= TIOCM_CTS; + } else { + upsdebugx(3, "%s: override CTS", __func__); } } @@ -91,7 +122,10 @@ static void parse_input_signals(const char *value, int *line, int *val) *line |= TIOCM_CD; if (!strstr(value, "-DCD")) { + upsdebugx(3, "%s: override DCD (active low)", __func__); *val |= TIOCM_CD; + } else { + upsdebugx(3, "%s: override DCD", __func__); } } @@ -99,7 +133,10 @@ static void parse_input_signals(const char *value, int *line, int *val) *line |= TIOCM_RNG; if (!strstr(value, "-RNG")) { + upsdebugx(3, "%s: override RNG (active low)", __func__); *val |= TIOCM_RNG; + } else { + upsdebugx(3, "%s: override RNG", __func__); } } @@ -107,7 +144,10 @@ static void parse_input_signals(const char *value, int *line, int *val) *line |= TIOCM_DSR; if (!strstr(value, "-DSR")) { + upsdebugx(3, "%s: override DSR (active low)", __func__); *val |= TIOCM_DSR; + } else { + upsdebugx(3, "%s: override DSR", __func__); } } @@ -122,6 +162,18 @@ static void parse_input_signals(const char *value, int *line, int *val) if (strstr(value, "ST")) { fatalx(EXIT_FAILURE, "Can't override input with ST (not an input)"); } + + if (strstr(value, "NULL") || strstr(value, "none")) { + *line = 0; + *val = 0; + upsdebugx(3, "%s: disable", __func__); + } + + if((*line == old_line) && (*val == old_val)) { + upslogx(LOG_NOTICE, "%s: input overrides specified, but no effective difference - check for typos?", __func__); + } + + upsdebugx(4, "%s: exit", __func__); } void upsdrv_initinfo(void) @@ -149,12 +201,22 @@ void upsdrv_initinfo(void) parse_input_signals(v, &upstab[upstype].line_bl, &upstab[upstype].val_bl); upsdebugx(2, "parse_input_signals: LB overridden with %s\n", v); } + + if ((v = getval("RB")) != NULL) { + parse_input_signals(v, &upstab[upstype].line_rb, &upstab[upstype].val_rb); + upsdebugx(2, "parse_input_signals: RB overridden with %s\n", v); + } + + if ((v = getval("BYPASS")) != NULL) { + parse_input_signals(v, &upstab[upstype].line_bypass, &upstab[upstype].val_bypass); + upsdebugx(2, "parse_input_signals: BYPASS overridden with %s\n", v); + } } /* normal idle loop - keep up with the current state of the UPS */ void upsdrv_updateinfo(void) { - int flags, ol, bl, ret; + int flags, ol, bl, rb, bypass, ret; ret = ioctl(upsfd, TIOCMGET, &flags); @@ -165,8 +227,13 @@ void upsdrv_updateinfo(void) return; } + /* Always online when OL is disabled */ ol = ((flags & upstab[upstype].line_ol) == upstab[upstype].val_ol); - bl = ((flags & upstab[upstype].line_bl) == upstab[upstype].val_bl); + + /* Always have the flags cleared when other status flags are disabled */ + bl = upstab[upstype].line_bl != 0 && ((flags & upstab[upstype].line_bl) == upstab[upstype].val_bl); + rb = upstab[upstype].line_rb != 0 && ((flags & upstab[upstype].line_rb) == upstab[upstype].val_rb); + bypass = upstab[upstype].line_bypass != 0 && ((flags & upstab[upstype].line_bypass) == upstab[upstype].val_bypass); status_init(); @@ -180,9 +247,17 @@ void upsdrv_updateinfo(void) status_set("OB"); /* on battery */ } + if (rb) { + status_set("RB"); /* replace battery */ + } + + if (bypass) { + status_set("BYPASS"); /* battery bypass */ + } + status_commit(); - upsdebugx(5, "ups.status: %s %s\n", ol ? "OL" : "OB", bl ? "BL" : ""); + upsdebugx(5, "ups.status: %s %s %s %s\n", ol ? "OL" : "OB", bl ? "BL" : "", rb ? "RB" : "", bypass ? "BYPASS" : ""); ser_comm_good(); dstate_dataok(); @@ -261,14 +336,37 @@ void upsdrv_shutdown(void) } if (getval("sdtime")) { - int sdtime; + long sdtime; sdtime = strtol(getval("sdtime"), (char **) NULL, 10); - upslogx(LOG_INFO, "Holding shutdown signal for %d seconds...\n", + upslogx(LOG_INFO, "Holding shutdown signal for %ld seconds...\n", sdtime); - sleep(sdtime); + if (sdtime > 0) { +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + /* Different platforms, different sizes, none fits all... */ + if (sizeof(long) > sizeof(unsigned int) && sdtime < (long)UINT_MAX) { +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic pop +#endif + sleep((unsigned int)sdtime); + } else { + sleep(UINT_MAX); + } + } } } @@ -286,6 +384,8 @@ void upsdrv_makevartable(void) addvar(VAR_VALUE, "CP", "Override cable power setting"); addvar(VAR_VALUE, "OL", "Override on line signal"); addvar(VAR_VALUE, "LB", "Override low battery signal"); + addvar(VAR_VALUE, "RB", "Override replace battery signal"); + addvar(VAR_VALUE, "BYPASS", "Override battery bypass signal"); addvar(VAR_VALUE, "SD", "Override shutdown setting"); addvar(VAR_VALUE, "sdtime", "Hold time for shutdown value (seconds)"); } @@ -304,7 +404,7 @@ void upsdrv_initups(void) } /* don't hang up on last close */ - tio.c_cflag &= ~HUPCL; + tio.c_cflag &= ~((tcflag_t)HUPCL); if (tcsetattr(upsfd, TCSANOW, &tio)) { fatal_with_errno(EXIT_FAILURE, "tcsetattr"); diff --git a/drivers/genericups.h b/drivers/genericups.h index eb1977e..f4c3a45 100644 --- a/drivers/genericups.h +++ b/drivers/genericups.h @@ -17,13 +17,18 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -struct { +#ifndef NUT_GENERICUPS_H_SEEN +#define NUT_GENERICUPS_H_SEEN 1 + +static struct { const char *mfr; /* value for INFO_MFR */ const char *model; /* value for INFO_MODEL */ const char *desc; /* used in -h listing */ int line_norm; int line_ol, val_ol; int line_bl, val_bl; + int line_rb, val_rb; + int line_bypass, val_bypass; int line_sd; } upstab[] = { @@ -34,6 +39,8 @@ struct { TIOCM_DTR | TIOCM_RTS, /* cable power: DTR + RTS */ TIOCM_CTS, 0, /* online: CTS off */ TIOCM_CD, TIOCM_CD, /* low battery: CD on */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_RTS /* shutdown: RTS */ }, @@ -44,6 +51,8 @@ struct { TIOCM_DTR, /* cable power: DTR */ TIOCM_RNG, 0, /* online: RNG off */ TIOCM_CD, TIOCM_CD, /* low battery: CD on */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_RTS /* shutdown: RTS */ }, @@ -54,9 +63,11 @@ struct { TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, 0, /* online: CTS off */ TIOCM_CD, TIOCM_CD, /* low battery: CD on */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_DTR|TIOCM_RTS /* shutdown: DTR + RTS */ }, - + /* Type 3 */ { "PowerTech", "Comp1000", @@ -64,6 +75,8 @@ struct { TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, 0, /* online: CTS off */ TIOCM_CD, TIOCM_CD, /* low battery: CD on */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_DTR | TIOCM_RTS /* shutdown: DTR + RTS */ }, @@ -74,6 +87,8 @@ struct { TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ 0 /* shutdown: none */ }, @@ -84,6 +99,8 @@ struct { TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_DTR | TIOCM_RTS /* shutdown: DTR + RTS */ }, @@ -94,16 +111,20 @@ struct { TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_RTS /* shutdown: set RTS */ }, /* Type 7 */ { "CyberPower", "Power99", - "CyberPower Power99", + "CyberPower Power99", TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_DTR /* shutdown: set DTR */ }, @@ -114,6 +135,8 @@ struct { TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ -1 /* shutdown: unknown */ }, @@ -124,6 +147,8 @@ struct { 0, /* cable power: none */ TIOCM_CD, 0, /* online: CD off */ TIOCM_CTS, TIOCM_CTS, /* low battery: CTS on */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_RTS /* shutdown: RTS */ }, @@ -134,6 +159,8 @@ struct { TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_DTR /* shutdown: DTR */ }, @@ -144,6 +171,8 @@ struct { TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, 0, /* online: CTS off */ TIOCM_CD, 0, /* low battery: CD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_ST /* shutdown: ST */ }, @@ -154,9 +183,11 @@ struct { TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, 0, /* online: CTS off */ TIOCM_CD, TIOCM_CD, /* low battery: CD on */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_DTR /* shutdown: raise DTR */ }, - + /* Type 13 */ { "RPT", "Repoteck", @@ -164,6 +195,8 @@ struct { TIOCM_DTR | TIOCM_RTS, /* cable power: DTR + RTS */ TIOCM_CD, TIOCM_CD, /* On-line : DCD on */ TIOCM_CTS, 0, /* Battery low: CTS off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_ST /* shutdown: TX BREA */ }, @@ -174,6 +207,8 @@ struct { TIOCM_DTR, /* cable power: DTR */ TIOCM_CD, TIOCM_CD, /* online: CD on */ TIOCM_CTS, 0, /* low battery: CTS off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_RTS /* shutdown: raise RTS */ }, @@ -184,6 +219,8 @@ struct { TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_ST /* shutdown: ST (break) */ }, @@ -194,6 +231,8 @@ struct { TIOCM_DTR | TIOCM_RTS, /* cable power: DTR + RTS */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ -1 /* shutdown: unknown */ }, @@ -204,6 +243,8 @@ struct { TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ -1 /* shutdown: unknown */ }, @@ -214,6 +255,8 @@ struct { TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, TIOCM_CD, /* low battery: CAR on */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ -1 /* shutdown: none */ }, @@ -224,6 +267,8 @@ struct { TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: DCD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_RTS /* shutdown: set RTS */ }, @@ -235,6 +280,8 @@ struct { TIOCM_DTR, /* cable power: DTR */ TIOCM_CTS, 0, /* online: CTS off */ TIOCM_CD, TIOCM_CD, /* low battery: CD on */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_ST /* shutdown: ST (break) */ }, @@ -246,9 +293,11 @@ struct { TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_RTS | TIOCM_DTR /* shutdown: RTS+DTR */ }, - + /* Type 22 (duplicate from 7)*/ { "Gamatronic Electronic Industries", "Generic Alarm UPS", @@ -256,9 +305,23 @@ struct { TIOCM_RTS, /* cable power: RTS */ TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ TIOCM_CD, 0, /* low battery: CD off */ + 0, 0, /* replace battery: none */ + 0, 0, /* battery bypass: none */ TIOCM_DTR /* shutdown: DTR */ }, + /* Type 23 */ + { "Generic", + "Generic FTTx Battery Backup", + "FTTx (Fiber to the x) battery backup with 4-wire telemetry interface", + TIOCM_RTS, /* cable power: RTS */ + TIOCM_CTS, TIOCM_CTS, /* online: CTS on */ + TIOCM_CD, 0, /* low battery: CD off */ + TIOCM_RI, 0, /* replace battery: RI off */ + TIOCM_DSR, 0, /* battery bypass: DSR off */ + 0 /* shutdown: none */ + }, + /* add any new entries directly above this line */ { NULL, @@ -267,6 +330,10 @@ struct { 0, 0, 0, 0, 0, + 0, 0, + 0, 0, 0 } }; + +#endif /* NUT_GENERICUPS_H_SEEN */ diff --git a/drivers/hidparser.c b/drivers/hidparser.c index 1fb4a5e..0d7b53f 100644 --- a/drivers/hidparser.c +++ b/drivers/hidparser.c @@ -22,29 +22,30 @@ * * -------------------------------------------------------------------------- */ +#include "config.h" /* must be first */ + #include #include -#include "config.h" #include "hidparser.h" -#include "nut_stdint.h" /* for int8_t, int16_t, int32_t */ - -/* to be implemented for DEBUG purpose */ -/* previously: #define ERROR(x) if(x) __asm { int 3 }; */ -#define ERROR(x) +#include "nut_stdint.h" /* for int8_t, int16_t, int32_t */ +#include "common.h" /* for fatalx() */ static const uint8_t ItemSize[4] = { 0, 1, 2, 4 }; /* * HIDParser struct * -------------------------------------------------------------------------- */ +/* FIXME? Should this structure remain with reasonable fixed int types, + * or changed to align with libusb API version and usb_ctrl_* typedefs? + */ typedef struct { const unsigned char *ReportDesc; /* Report Descriptor */ - int ReportDescSize; /* Size of Report Descriptor */ + size_t ReportDescSize; /* Size of Report Descriptor */ uint16_t Pos; /* Store current pos in descriptor */ uint8_t Item; /* Store current Item */ - long Value; /* Store current Value */ + uint32_t Value; /* Store current Value */ HIDData_t Data; /* Store current environment */ @@ -59,7 +60,7 @@ typedef struct { /* return 1 + the position of the leftmost "1" bit of an int, or 0 if none. */ -static inline unsigned int hibit(unsigned int x) +static inline unsigned int hibit(unsigned long x) { unsigned int res = 0; @@ -94,7 +95,7 @@ static void ResetLocalState(HIDParser_t* pParser) /* * GetReportOffset * - * Return pointer on current offset value for Report designed by + * Return pointer on current offset value for Report designed by * ReportID/ReportType * -------------------------------------------------------------------------- */ static uint8_t *GetReportOffset(HIDParser_t* pParser, const uint8_t ReportID, const uint8_t ReportType) @@ -124,10 +125,10 @@ static uint8_t *GetReportOffset(HIDParser_t* pParser, const uint8_t ReportID, co } /* - * FormatValue(long Value, uint8_t Size) + * FormatValue(uint32_t Value, uint8_t Size) * Format Value to fit with long format with respect of negative values * -------------------------------------------------------------------------- */ -static long FormatValue(long Value, uint8_t Size) +static long FormatValue(uint32_t Value, uint8_t Size) { switch(Size) { @@ -138,7 +139,7 @@ static long FormatValue(long Value, uint8_t Size) case 4: return (long)(int32_t)Value; default: - return Value; + return (long)Value; } } @@ -146,36 +147,25 @@ static long FormatValue(long Value, uint8_t Size) * HIDParse(HIDParser_t* pParser, HIDData_t *pData) * * Analyse Report descriptor stored in HIDParser struct and store local and - * global context. + * global context. * Return in pData the last object found. * Return -1 when there is no other Item to parse, 1 if a new object was found * or 0 if a continuation of a previous object was found. * -------------------------------------------------------------------------- */ static int HIDParse(HIDParser_t *pParser, HIDData_t *pData) { - int Found = -1; + int Found = -1, i; while ((Found < 0) && (pParser->Pos < pParser->ReportDescSize)) { /* Get new pParser->Item if current pParser->Count is empty */ if (pParser->Count == 0) { pParser->Item = pParser->ReportDesc[pParser->Pos++]; pParser->Value = 0; -#if WORDS_BIGENDIAN - { - int i; - unsigned long valTmp = 0; - - for (i = 0; i < ItemSize[pParser->Item & SIZE_MASK]; i++) { - memcpy(&valTmp, &pParser->ReportDesc[(pParser->Pos)+i], 1); - pParser->Value += valTmp >> ((3-i)*8); - valTmp = 0; - } + for (i = 0; i < ItemSize[pParser->Item & SIZE_MASK]; i++) { + pParser->Value += (uint32_t)(pParser->ReportDesc[(pParser->Pos)+i]) << (8*i); } -#else - memcpy(&pParser->Value, &pParser->ReportDesc[pParser->Pos], ItemSize[pParser->Item & SIZE_MASK]); -#endif /* Pos on next item */ - pParser->Pos += ItemSize[pParser->Item & SIZE_MASK]; + pParser->Pos += ItemSize[pParser->Item & SIZE_MASK]; } switch (pParser->Item & ITEM_MASK) @@ -190,7 +180,7 @@ static int HIDParse(HIDParser_t *pParser, HIDData_t *pData) if ((pParser->Item & SIZE_MASK) > 2) { pParser->UsageTab[pParser->UsageSize] = pParser->Value; } else { - pParser->UsageTab[pParser->UsageSize] = (pParser->UPage << 16) | (pParser->Value & 0xFFFF); + pParser->UsageTab[pParser->UsageSize] = ((HIDNode_t)(pParser->UPage) << 16) | (pParser->Value & 0xFFFF); } /* Increment Usage stack size */ @@ -204,10 +194,10 @@ static int HIDParse(HIDParser_t *pParser, HIDData_t *pData) /* Unstack UPage/Usage from UsageTab (never remove the last) */ if (pParser->UsageSize > 0) { - int i; - - for (i = 0; i < pParser->UsageSize; i++) { - pParser->UsageTab[i] = pParser->UsageTab[i+1]; + int j; + + for (j = 0; j < pParser->UsageSize; j++) { + pParser->UsageTab[j] = pParser->UsageTab[j+1]; } /* Remove Usage */ @@ -225,15 +215,15 @@ static int HIDParse(HIDParser_t *pParser, HIDData_t *pData) case ITEM_END_COLLECTION : pParser->Data.Path.Size--; - + /* Remove Index if any */ if((pParser->Data.Path.Node[pParser->Data.Path.Size] & 0xffff0000) == 0x00ff0000) { pParser->Data.Path.Size--; } - + ResetLocalState(pParser); break; - + case ITEM_FEATURE: case ITEM_INPUT: case ITEM_OUTPUT: @@ -244,96 +234,118 @@ static int HIDParse(HIDParser_t *pParser, HIDData_t *pData) /* It is a continuation of a previous object */ Found = 0; } - + /* Get new pParser->Count from global value */ if(pParser->Count == 0) { pParser->Count = pParser->ReportCount; } - + /* Get UPage/Usage from UsageTab and store them in pParser->Data.Path */ pParser->Data.Path.Node[pParser->Data.Path.Size] = pParser->UsageTab[0]; pParser->Data.Path.Size++; - + /* Unstack UPage/Usage from UsageTab (never remove the last) */ if(pParser->UsageSize > 0) { - int i; - - for (i = 0; i < pParser->UsageSize; i++) { - pParser->UsageTab[i] = pParser->UsageTab[i+1]; + int j; + + for (j = 0; j < pParser->UsageSize; j++) { + pParser->UsageTab[j] = pParser->UsageTab[j+1]; } + /* Remove Usage */ pParser->UsageSize--; } - + /* Copy data type */ pParser->Data.Type = (uint8_t)(pParser->Item & ITEM_MASK); - + /* Copy data attribute */ pParser->Data.Attribute = (uint8_t)pParser->Value; - + /* Store offset */ pParser->Data.Offset = *GetReportOffset(pParser, pParser->Data.ReportID, (uint8_t)(pParser->Item & ITEM_MASK)); - + /* Get Object in pData */ /* -------------------------------------------------------------------------- */ memcpy(pData, &pParser->Data, sizeof(HIDData_t)); /* -------------------------------------------------------------------------- */ - + /* Increment Report Offset */ *GetReportOffset(pParser, pParser->Data.ReportID, (uint8_t)(pParser->Item & ITEM_MASK)) += pParser->Data.Size; - + /* Remove path last node */ pParser->Data.Path.Size--; - + /* Decrement count */ pParser->Count--; - + if (pParser->Count == 0) { ResetLocalState(pParser); } break; - + case ITEM_REP_ID : pParser->Data.ReportID = (uint8_t)pParser->Value; break; - + case ITEM_REP_SIZE : pParser->Data.Size = (uint8_t)pParser->Value; break; - + case ITEM_REP_COUNT : pParser->ReportCount = (uint8_t)pParser->Value; break; - + case ITEM_UNIT_EXP : pParser->Data.UnitExp = (int8_t)pParser->Value; if (pParser->Data.UnitExp > 7) { pParser->Data.UnitExp |= 0xF0; } break; - + case ITEM_UNIT : - pParser->Data.Unit = pParser->Value; + /* TOTHINK: Are there cases where Unit is not-signed, + * but a Value too big becomes signed after casting -- + * and unintentionally so? */ + pParser->Data.Unit = (long)pParser->Value; break; - + case ITEM_LOG_MIN : pParser->Data.LogMin = FormatValue(pParser->Value, ItemSize[pParser->Item & SIZE_MASK]); break; - + case ITEM_LOG_MAX : pParser->Data.LogMax = FormatValue(pParser->Value, ItemSize[pParser->Item & SIZE_MASK]); + /* HACK: If treating the value as signed (FormatValue(...)) results in a LogMax that is + * less than the LogMin value then it is likely that the LogMax value has been + * incorrectly encoded by the UPS firmware (field too short and overflowed into sign + * bit). In that case, reinterpret it as an unsigned number and log the problem. + * This hack is not correct in the sense that it only looks at the LogMin value for + * this item, whereas the HID spec says that Logical values persist in global state. + */ + if (pParser->Data.LogMax < pParser->Data.LogMin) { + upslogx(LOG_WARNING, + "%s: LogMax is less than LogMin. " + "Vendor HID report descriptor may be incorrect; " + "interpreting LogMax %ld as %u in ReportID: 0x%02x", + __func__, + pParser->Data.LogMax, + pParser->Value, + pParser->Data.ReportID); + pParser->Data.LogMax = (long) pParser->Value; + } break; - + case ITEM_PHY_MIN : pParser->Data.PhyMin=FormatValue(pParser->Value, ItemSize[pParser->Item & SIZE_MASK]); pParser->Data.have_PhyMin = 1; break; - + case ITEM_PHY_MAX : pParser->Data.PhyMax=FormatValue(pParser->Value, ItemSize[pParser->Item & SIZE_MASK]); pParser->Data.have_PhyMax = 1; break; - + case ITEM_LONG : /* can't handle long items, but should at least skip them */ pParser->Pos += (uint8_t)(pParser->Value & 0xff); @@ -341,10 +353,19 @@ static int HIDParse(HIDParser_t *pParser, HIDData_t *pData) } } /* while ((Found < 0) && (pParser->Pos < pParser->ReportDescSize)) */ - ERROR(pParser->Data.Path.Size >= PATH_SIZE); - ERROR(pParser->ReportDescSize >= REPORT_DSC_SIZE); - ERROR(pParser->UsageSize >= USAGE_TAB_SIZE); - ERROR(pParser->Data.ReportID >= MAX_REPORT); + if(pParser->Data.Path.Size >= PATH_SIZE) + upslogx(LOG_ERR, "%s: HID path too long", __func__); + if(pParser->ReportDescSize >= REPORT_DSC_SIZE) + upslogx(LOG_ERR, "%s: Report descriptor too big", __func__); + if(pParser->UsageSize >= USAGE_TAB_SIZE) + upslogx(LOG_ERR, "%s: HID Usage too high", __func__); + + /* FIXME: comparison is always false due to limited range of data type [-Werror=type-limits] + * with ReportID beint uint8_t and MAX_REPORT being 500 currently */ + /* + if(pParser->Data.ReportID >= MAX_REPORT) + upslogx(LOG_ERR, "%s: Too many HID reports", __func__); + */ return Found; } @@ -354,14 +375,14 @@ static int HIDParse(HIDParser_t *pParser, HIDData_t *pData) * Get pData characteristics from pData->Path * Return TRUE if object was found * -------------------------------------------------------------------------- */ -int FindObject(HIDDesc_t *pDesc, HIDData_t *pData) +int FindObject(HIDDesc_t *pDesc_arg, HIDData_t *pData) { - HIDData_t *pFoundData = FindObject_with_Path(pDesc, &pData->Path, pData->Type); - + HIDData_t *pFoundData = FindObject_with_Path(pDesc_arg, &pData->Path, pData->Type); + if (!pFoundData) { return 0; } - + memcpy(pData, pFoundData, sizeof(*pData)); return 1; } @@ -370,24 +391,24 @@ int FindObject(HIDDesc_t *pDesc, HIDData_t *pData) * FindObject_with_Path * Get pData item with given Path and Type. Return NULL if not found. * -------------------------------------------------------------------------- */ -HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc, HIDPath_t *Path, uint8_t Type) +HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc_arg, HIDPath_t *Path, uint8_t Type) { - int i; + size_t i; + + for (i = 0; i < pDesc_arg->nitems; i++) { + HIDData_t *pData = &pDesc_arg->item[i]; - for (i = 0; i < pDesc->nitems; i++) { - HIDData_t *pData = &pDesc->item[i]; - if (pData->Type != Type) { continue; } - + if (memcmp(pData->Path.Node, Path->Node, (Path->Size) * sizeof(HIDNode_t))) { continue; } - + return pData; } - + return NULL; } @@ -396,50 +417,85 @@ HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc, HIDPath_t *Path, uint8_t Type) * Get pData item with given ReportID, Offset, and Type. Return NULL * if not found. * -------------------------------------------------------------------------- */ -HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc, uint8_t ReportID, uint8_t Offset, uint8_t Type) +HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc_arg, uint8_t ReportID, uint8_t Offset, uint8_t Type) { - int i; + size_t i; + + for (i = 0; i < pDesc_arg->nitems; i++) { + HIDData_t *pData = &pDesc_arg->item[i]; - for (i = 0; i < pDesc->nitems; i++) { - HIDData_t *pData = &pDesc->item[i]; - if (pData->ReportID != ReportID) { continue; } - + if (pData->Type != Type) { continue; } - + if (pData->Offset != Offset) { continue; } - + return pData; } - + + return NULL; +} + +/* + * FindObject_with_ID_Node + * Get pData item with given ReportID and Node. Return NULL if not found. + * -------------------------------------------------------------------------- */ +HIDData_t *FindObject_with_ID_Node(HIDDesc_t *pDesc_arg, uint8_t ReportID, HIDNode_t Node) +{ + size_t i; + + for (i = 0; i < pDesc_arg->nitems; i++) { + HIDData_t *pData = &pDesc_arg->item[i]; + + if (pData->ReportID != ReportID) { + continue; + } + + HIDPath_t * pPath = &pData->Path; + uint8_t size = pPath->Size; + if (size == 0 || pPath->Node[size-1] != Node) { + continue; + } + + return pData; + } + return NULL; } /* * GetValue * Extract data from a report stored in Buf. - * Use Value, Offset, Size and LogMax of pData. - * Return response in Value. + * Use Offset, Size, LogMin, and LogMax of pData. + * Return response in *pValue. * -------------------------------------------------------------------------- */ void GetValue(const unsigned char *Buf, HIDData_t *pData, long *pValue) { + /* Note: https://github.com/networkupstools/nut/issues/1023 + This conversion code can easily be sensitive to 32- vs 64- bit + compilation environments. Consider the possibility of overflow + in 32-bit representations when computing with extreme values, + for example LogMax-LogMin+1. + Test carefully in both environments if changing any declarations. + */ + int Weight, Bit; - long value = 0, rawvalue; - long range, mask, signbit, b, m; + unsigned long mask, signbit, magMax, magMin; + long value = 0; Bit = pData->Offset + 8; /* First byte of report is report ID */ - + for (Weight = 0; Weight < pData->Size; Weight++, Bit++) { int State = Buf[Bit >> 3] & (1 << (Bit & 7)); - + if(State) { - value += (1 << Weight); + value += (1L << Weight); } } @@ -465,49 +521,25 @@ void GetValue(const unsigned char *Buf, HIDData_t *pData, long *pValue) "throwing away higher-order bits" exacly means, so we try to do something sensible. -PS */ - rawvalue = value; /* remember this for later */ + /* determine representation without sign bit */ + magMax = pData->LogMax >= 0 ? (unsigned long)(pData->LogMax) : (unsigned long)(-(pData->LogMax + 1)); + magMin = pData->LogMin >= 0 ? (unsigned long)(pData->LogMin) : (unsigned long)(-(pData->LogMin + 1)); - /* figure out how many bits are significant */ - range = pData->LogMax - pData->LogMin + 1; - if (range <= 0) { - /* makes no sense, give up */ - *pValue = value; - return; - } - b = hibit(range-1); + /* calculate where the sign bit will be if needed */ + signbit = 1L << hibit(magMax > magMin ? magMax : magMin); - /* throw away insignificant bits; the result is >= 0 */ - mask = (1 << b) - 1; - signbit = 1 << (b - 1); - value = value & mask; + /* but only include sign bit in mask if negative numbers are involved */ + mask = (signbit - 1) | ((pData->LogMin < 0) ? signbit : 0); + + /* throw away excess high order bits (which may contain garbage) */ + value = (long)((unsigned long)(value) & mask); /* sign-extend it, if appropriate */ - if (pData->LogMin < 0 && (value & signbit) != 0) { + if (pData->LogMin < 0 && ((unsigned long)(value) & signbit) != 0) { value |= ~mask; } - /* if the resulting value is in the desired range, stop */ - if (value >= pData->LogMin && value <= pData->LogMax) { - *pValue = value; - return; - } - - /* else, try to reach interval by adjusting high-order bits */ - m = (value - pData->LogMin) & mask; - value = pData->LogMin + m; - if (value <= pData->LogMax) { - *pValue = value; - return; - } - - /* if everything else failed, sign-extend the original raw value, - and simply round it to the closest point in the interval. */ - value = rawvalue; - mask = (1 << pData->Size) - 1; - signbit = 1 << (pData->Size - 1); - if (pData->LogMin < 0 && (value & signbit) != 0) { - value |= ~mask; - } + /* clamp returned value to range [LogMin..LogMax] */ if (value < pData->LogMin) { value = pData->LogMin; } else if (value > pData->LogMax) { @@ -526,11 +558,11 @@ void GetValue(const unsigned char *Buf, HIDData_t *pData, long *pValue) void SetValue(const HIDData_t *pData, unsigned char *Buf, long Value) { int Weight, Bit; - + Bit = pData->Offset + 8; /* First byte of report is report ID */ for (Weight = 0; Weight < pData->Size; Weight++, Bit++) { - int State = Value & (1 << Weight); + long State = Value & (1L << Weight); if (State) { Buf[Bit >> 3] |= (1 << (Bit & 7)); @@ -546,73 +578,109 @@ void SetValue(const HIDData_t *pData, unsigned char *Buf, long Value) Output: parsed data structure. Returns allocated HIDDesc structure on success, NULL on failure with errno set. Note: the value returned by this function must be freed with Free_ReportDesc(). */ -HIDDesc_t *Parse_ReportDesc(const unsigned char *ReportDesc, const int n) +HIDDesc_t *Parse_ReportDesc(const usb_ctrl_charbuf ReportDesc, const usb_ctrl_charbufsize n) { - int ret; - HIDDesc_t *pDesc; + int ret = 0; + HIDDesc_t *pDesc_var; HIDParser_t *parser; - pDesc = calloc(1, sizeof(*pDesc)); - if (!pDesc) { + pDesc_var = calloc(1, sizeof(*pDesc_var)); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if (!pDesc_var + || n < 0 || (uintmax_t)n > SIZE_MAX + ) { return NULL; } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif - pDesc->item = calloc(MAX_REPORT, sizeof(*pDesc->item)); - if (!pDesc->item) { - Free_ReportDesc(pDesc); + pDesc_var->item = calloc(MAX_REPORT, sizeof(*pDesc_var->item)); + if (!pDesc_var->item) { + Free_ReportDesc(pDesc_var); return NULL; } parser = calloc(1, sizeof(*parser)); if (!parser) { - Free_ReportDesc(pDesc); + Free_ReportDesc(pDesc_var); return NULL; } - parser->ReportDesc = ReportDesc; - parser->ReportDescSize = n; + parser->ReportDesc = (const unsigned char *)ReportDesc; + parser->ReportDescSize = (const size_t)n; - for (pDesc->nitems = 0; pDesc->nitems < MAX_REPORT; pDesc->nitems += ret) { - int id, max; + for (pDesc_var->nitems = 0; pDesc_var->nitems < MAX_REPORT; pDesc_var->nitems += (size_t)ret) { + uint8_t id; + size_t max; - ret = HIDParse(parser, &pDesc->item[pDesc->nitems]); + ret = HIDParse(parser, &pDesc_var->item[pDesc_var->nitems]); if (ret < 0) { break; } - id = pDesc->item[pDesc->nitems].ReportID; + id = pDesc_var->item[pDesc_var->nitems].ReportID; /* calculate bit range of this item within report */ - max = pDesc->item[pDesc->nitems].Offset + pDesc->item[pDesc->nitems].Size; + max = pDesc_var->item[pDesc_var->nitems].Offset + pDesc_var->item[pDesc_var->nitems].Size; /* convert to bytes */ max = (max + 7) >> 3; /* update report length */ - if (max > pDesc->replen[id]) { - pDesc->replen[id] = max; + if (max > pDesc_var->replen[id]) { + pDesc_var->replen[id] = max; } } + /* Sanity check: are there remaining HID objects that can't + * be processed? */ + if ((pDesc_var->nitems == MAX_REPORT) && (parser->Pos < parser->ReportDescSize)) + upslogx(LOG_ERR, "ERROR in %s: Too many HID objects", __func__); + free(parser); - if (pDesc->nitems == 0) { - Free_ReportDesc(pDesc); + if (pDesc_var->nitems == 0) { + Free_ReportDesc(pDesc_var); return NULL; } - pDesc->item = realloc(pDesc->item, pDesc->nitems * sizeof(*pDesc->item)); + pDesc_var->item = realloc(pDesc_var->item, pDesc_var->nitems * sizeof(*pDesc_var->item)); - return pDesc; + return pDesc_var; } /* free a parsed report descriptor, as allocated by Parse_ReportDesc() */ -void Free_ReportDesc(HIDDesc_t *pDesc) +void Free_ReportDesc(HIDDesc_t *pDesc_arg) { - if (!pDesc) { + if (!pDesc_arg) { return; } - free(pDesc->item); - free(pDesc); + free(pDesc_arg->item); + free(pDesc_arg); } diff --git a/drivers/hidparser.h b/drivers/hidparser.h index 5c91d6f..1a2a3a7 100644 --- a/drivers/hidparser.h +++ b/drivers/hidparser.h @@ -22,8 +22,8 @@ * * -------------------------------------------------------------------------- */ -#ifndef HIDPARS_H -#define HIDPARS_H +#ifndef NUT_HID_PARSER_H_SEEN +#define NUT_HID_PARSER_H_SEEN #ifdef __cplusplus @@ -32,27 +32,38 @@ extern "C" { /* *INDENT-ON* */ #endif /* __cplusplus */ +#include "config.h" #include "hidtypes.h" +/* Include "usb-common.h" or "libshut.h" as appropriate, to define the + * usb_ctrl_* types used below according to the backend USB API version + */ +#ifdef SHUT_MODE +# include "libshut.h" +#else +# include "usb-common.h" +#endif + /* * Parse_ReportDesc * -------------------------------------------------------------------------- */ -HIDDesc_t *Parse_ReportDesc(const unsigned char *ReportDesc, const int n); +HIDDesc_t *Parse_ReportDesc(const usb_ctrl_charbuf ReportDesc, const usb_ctrl_charbufsize n); /* * Free_ReportDesc * -------------------------------------------------------------------------- */ -void Free_ReportDesc(HIDDesc_t *pDesc); +void Free_ReportDesc(HIDDesc_t *pDesc_arg); /* * FindObject * -------------------------------------------------------------------------- */ -int FindObject(HIDDesc_t *pDesc, HIDData_t *pData); +int FindObject(HIDDesc_t *pDesc_arg, HIDData_t *pData); -HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc, HIDPath_t *Path, uint8_t Type); +HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc_arg, HIDPath_t *Path, uint8_t Type); -HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc, uint8_t ReportID, uint8_t Offset, uint8_t Type); +HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc_arg, uint8_t ReportID, uint8_t Offset, uint8_t Type); +HIDData_t *FindObject_with_ID_Node(HIDDesc_t *pDesc_arg, uint8_t ReportID, HIDNode_t Node); /* * GetValue * -------------------------------------------------------------------------- */ @@ -70,4 +81,4 @@ void SetValue(const HIDData_t *pData, unsigned char *Buf, long Value); /* *INDENT-ON* */ #endif /* __cplusplus */ -#endif +#endif /* NUT_HID_PARSER_H_SEEN */ diff --git a/drivers/hidtypes.h b/drivers/hidtypes.h index f7e2581..87d42df 100644 --- a/drivers/hidtypes.h +++ b/drivers/hidtypes.h @@ -5,6 +5,7 @@ * * Copyright (C) * 1998-2003 MGE UPS SYSTEMS, Luc Descotils + * 2015 Eaton, Arnaud Quette (Update MAX_REPORT) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,11 +39,11 @@ extern "C" { /* * Constants * -------------------------------------------------------------------------- */ -#define PATH_SIZE 10 /* Deep max for Path */ -#define USAGE_TAB_SIZE 50 /* Size of usage stack */ -#define MAX_REPORT 300 /* Including FEATURE, INPUT and OUTPUT */ -#define REPORT_DSC_SIZE 6144 /* Size max of Report Descriptor */ -#define MAX_REPORT_TS 3 /* Max time validity of a report */ +#define PATH_SIZE 10 /* Deep max for Path */ +#define USAGE_TAB_SIZE 50 /* Size of usage stack */ +#define MAX_REPORT 500 /* Including FEATURE, INPUT and OUTPUT */ +#define REPORT_DSC_SIZE 6144 /* Size max of Report Descriptor */ +#define MAX_REPORT_TS 3 /* Max time validity of a report */ /* * Items @@ -90,6 +91,186 @@ extern "C" { #define ATTR_DATA_CST 0x01 #define ATTR_NVOL_VOL 0x80 +/* Usage Pages */ +/* For more details, please see docs/hid-subdrivers.txt */ +#define PAGE_POWER_DEVICE 0x84 +#define PAGE_BATTERY_SYSTEM 0x85 + +/* Usage within Power Device page */ +#define USAGE_POW_UNDEFINED 0x00840000 +#define USAGE_POW_I_NAME 0x00840001 +#define USAGE_POW_PRESENT_STATUS 0x00840002 +#define USAGE_POW_CHANGED_STATUS 0x00840003 +#define USAGE_POW_UPS 0x00840004 +#define USAGE_POW_POWER_SUPPLY 0x00840005 +#define USAGE_POW_PERIPHERAL_DEVICE 0x00840006 +#define USAGE_POW_BATTERY_SYSTEM 0x00840010 +#define USAGE_POW_BATTERY_SYSTEM_ID 0x00840011 +#define USAGE_POW_BATTERY 0x00840012 +#define USAGE_POW_BATTERY_ID 0x00840013 +#define USAGE_POW_CHARGER 0x00840014 +#define USAGE_POW_CHARGER_ID 0x00840015 +#define USAGE_POW_POWER_CONVERTER 0x00840016 +#define USAGE_POW_POWER_CONVERTER_ID 0x00840017 +#define USAGE_POW_OUTLET_SYSTEM 0x00840018 +#define USAGE_POW_OUTLET_SYSTEM_ID 0x00840019 +#define USAGE_POW_INPUT 0x0084001A +#define USAGE_POW_INPUT_ID 0x0084001B +#define USAGE_POW_OUTPUT 0x0084001C +#define USAGE_POW_OUTPUT_ID 0x0084001D +#define USAGE_POW_FLOW 0x0084001E +#define USAGE_POW_FLOW_ID 0x0084001F +#define USAGE_POW_OUTLET 0x00840020 +#define USAGE_POW_OUTLET_ID 0x00840021 +#define USAGE_POW_GANG 0x00840022 +#define USAGE_POW_GANG_ID 0x00840023 +#define USAGE_POW_POWER_SUMMARY 0x00840024 +#define USAGE_POW_POWER_SUMMARY_ID 0x00840025 +#define USAGE_POW_VOLTAGE 0x00840030 +#define USAGE_POW_CURRENT 0x00840031 +#define USAGE_POW_FREQUENCY 0x00840032 +#define USAGE_POW_APPARENT_POWER 0x00840033 +#define USAGE_POW_ACTIVE_POWER 0x00840034 +#define USAGE_POW_PERCENT_LOAD 0x00840035 +#define USAGE_POW_TEMPERATURE 0x00840036 +#define USAGE_POW_HUMIDITY 0x00840037 +#define USAGE_POW_BAD_COUNT 0x00840038 +#define USAGE_POW_CONFIG_VOLTAGE 0x00840040 +#define USAGE_POW_CONFIG_CURRENT 0x00840041 +#define USAGE_POW_CONFIG_FREQUENCY 0x00840042 +#define USAGE_POW_CONFIG_APPARENT_POWER 0x00840043 +#define USAGE_POW_CONFIG_ACTIVE_POWER 0x00840044 +#define USAGE_POW_CONFIG_PERCENT_LOAD 0x00840045 +#define USAGE_POW_CONFIG_TEMPERATURE 0x00840046 +#define USAGE_POW_CONFIG_HUMIDITY 0x00840047 +#define USAGE_POW_SWITCH_ON_CONTROL 0x00840050 +#define USAGE_POW_SWITCH_OFF_CONTROL 0x00840051 +#define USAGE_POW_TOGGLE_CONTROL 0x00840052 +#define USAGE_POW_LOW_VOLTAGE_TRANSFER 0x00840053 +#define USAGE_POW_HIGH_VOLTAGE_TRANSFER 0x00840054 +#define USAGE_POW_DELAY_BEFORE_REBOOT 0x00840055 +#define USAGE_POW_DELAY_BEFORE_STARTUP 0x00840056 +#define USAGE_POW_DELAY_BEFORE_SHUTDOWN 0x00840057 +#define USAGE_POW_TEST 0x00840058 +#define USAGE_POW_MODULE_RESET 0x00840059 +#define USAGE_POW_AUDIBLE_ALARM_CONTROL 0x0084005A +#define USAGE_POW_PRESENT 0x00840060 +#define USAGE_POW_GOOD 0x00840061 +#define USAGE_POW_INTERNAL_FAILURE 0x00840062 +#define USAGE_POW_VOLTAGE_OUT_OF_RANGE 0x00840063 +#define USAGE_POW_FREQUENCY_OUT_OF_RANGE 0x00840064 +#define USAGE_POW_OVERLOAD 0x00840065 +#define USAGE_POW_OVER_CHARGED 0x00840066 +#define USAGE_POW_OVER_TEMPERATURE 0x00840067 +#define USAGE_POW_SHUTDOWN_REQUESTED 0x00840068 +#define USAGE_POW_SHUTDOWN_IMMINENT 0x00840069 +#define USAGE_POW_SWITCH_ON_OFF 0x0084006B +#define USAGE_POW_SWITCHABLE 0x0084006C +#define USAGE_POW_USED 0x0084006D +#define USAGE_POW_BOOST 0x0084006E +#define USAGE_POW_BUCK 0x0084006F +#define USAGE_POW_INITIALIZED 0x00840070 +#define USAGE_POW_TESTED 0x00840071 +#define USAGE_POW_AWAITING_POWER 0x00840072 +#define USAGE_POW_COMMUNICATION_LOST 0x00840073 +#define USAGE_POW_I_MANUFACTURER 0x008400FD +#define USAGE_POW_I_PRODUCT 0x008400FE +#define USAGE_POW_I_SERIAL_NUMBER 0x008400FF + +/* Usage within Battery System page */ +#define USAGE_BAT_UNDEFINED 0x00850000 +#define USAGE_BAT_SMB_BATTERY_MODE 0x00850001 +#define USAGE_BAT_SMB_BATTERY_STATUS 0x00850002 +#define USAGE_BAT_SMB_ALARM_WARNING 0x00850003 +#define USAGE_BAT_SMB_CHARGER_MODE 0x00850004 +#define USAGE_BAT_SMB_CHARGER_STATUS 0x00850005 +#define USAGE_BAT_SMB_CHARGER_SPEC_INFO 0x00850006 +#define USAGE_BAT_SMB_SELECTOR_STATE 0x00850007 +#define USAGE_BAT_SMB_SELECTOR_PRESETS 0x00850008 +#define USAGE_BAT_SMB_SELECTOR_INFO 0x00850009 +#define USAGE_BAT_OPTIONAL_MFG_FUNCTION_1 0x00850010 +#define USAGE_BAT_OPTIONAL_MFG_FUNCTION_2 0x00850011 +#define USAGE_BAT_OPTIONAL_MFG_FUNCTION_3 0x00850012 +#define USAGE_BAT_OPTIONAL_MFG_FUNCTION_4 0x00850013 +#define USAGE_BAT_OPTIONAL_MFG_FUNCTION_5 0x00850014 +#define USAGE_BAT_CONNECTION_TO_SMBUS 0x00850015 +#define USAGE_BAT_OUTPUT_CONNECTION 0x00850016 +#define USAGE_BAT_CHARGER_CONNECTION 0x00850017 +#define USAGE_BAT_BATTERY_INSERTION 0x00850018 +#define USAGE_BAT_USE_NEXT 0x00850019 +#define USAGE_BAT_OK_TO_USE 0x0085001A +#define USAGE_BAT_BATTERY_SUPPORTED 0x0085001B +#define USAGE_BAT_SELECTOR_REVISION 0x0085001C +#define USAGE_BAT_CHARGING_INDICATOR 0x0085001D +#define USAGE_BAT_MANUFACTURER_ACCESS 0x00850028 +#define USAGE_BAT_REMAINING_CAPACITY_LIMIT 0x00850029 +#define USAGE_BAT_REMAINING_TIME_LIMIT 0x0085002A +#define USAGE_BAT_AT_RATE 0x0085002B +#define USAGE_BAT_CAPACITY_MODE 0x0085002C +#define USAGE_BAT_BROADCAST_TO_CHARGER 0x0085002D +#define USAGE_BAT_PRIMARY_BATTERY 0x0085002E +#define USAGE_BAT_CHARGE_CONTROLLER 0x0085002F +#define USAGE_BAT_TERMINATE_CHARGE 0x00850040 +#define USAGE_BAT_TERMINATE_DISCHARGE 0x00850041 +#define USAGE_BAT_BELOW_REMAINING_CAPACITY_LIMIT 0x00850042 +#define USAGE_BAT_REMAINING_TIME_LIMIT_EXPIRED 0x00850043 +#define USAGE_BAT_CHARGING 0x00850044 +#define USAGE_BAT_DISCHARGING 0x00850045 +#define USAGE_BAT_FULLY_CHARGED 0x00850046 +#define USAGE_BAT_FULLY_DISCHARGED 0x00850047 +#define USAGE_BAT_CONDITIONING_FLAG 0x00850048 +#define USAGE_BAT_AT_RATE_OK 0x00850049 +#define USAGE_BAT_SMB_ERROR_CODE 0x0085004A +#define USAGE_BAT_NEED_REPLACEMENT 0x0085004B +#define USAGE_BAT_AT_RATE_TIME_TO_FULL 0x00850060 +#define USAGE_BAT_AT_RATE_TIME_TO_EMPTY 0x00850061 +#define USAGE_BAT_AVERAGE_CURRENT 0x00850062 +#define USAGE_BAT_MAX_ERROR 0x00850063 +#define USAGE_BAT_RELATIVE_STATE_OF_CHARGE 0x00850064 +#define USAGE_BAT_ABSOLUTE_STATE_OF_CHARGE 0x00850065 +#define USAGE_BAT_REMAINING_CAPACITY 0x00850066 +#define USAGE_BAT_FULL_CHARGE_CAPACITY 0x00850067 +#define USAGE_BAT_RUN_TIME_TO_EMPTY 0x00850068 +#define USAGE_BAT_AVERAGE_TIME_TO_EMPTY 0x00850069 +#define USAGE_BAT_AVERAGE_TIME_TO_FULL 0x0085006A +#define USAGE_BAT_CYCLE_COUNT 0x0085006B +#define USAGE_BAT_BATT_PACK_MODEL_LEVEL 0x00850080 +#define USAGE_BAT_INTERNAL_CHARGE_CONTROLLER 0x00850081 +#define USAGE_BAT_PRIMARY_BATTERY_SUPPORT 0x00850082 +#define USAGE_BAT_DESIGN_CAPACITY 0x00850083 +#define USAGE_BAT_SPECIFICATION_INFO 0x00850084 +#define USAGE_BAT_MANUFACTURER_DATE 0x00850085 +#define USAGE_BAT_SERIAL_NUMBER 0x00850086 +#define USAGE_BAT_I_MANUFACTURER_NAME 0x00850087 +#define USAGE_BAT_I_DEVICE_NAME 0x00850088 +#define USAGE_BAT_I_DEVICE_CHEMISTRY 0x00850089 +#define USAGE_BAT_MANUFACTURER_DATA 0x0085008A +#define USAGE_BAT_RECHARGEABLE 0x0085008B +#define USAGE_BAT_WARNING_CAPACITY_LIMIT 0x0085008C +#define USAGE_BAT_CAPACITY_GRANULARITY_1 0x0085008D +#define USAGE_BAT_CAPACITY_GRANULARITY_2 0x0085008E +#define USAGE_BAT_I_OEMINFORMATION 0x0085008F +#define USAGE_BAT_INHIBIT_CHARGE 0x008500C0 +#define USAGE_BAT_ENABLE_POLLING 0x008500C1 +#define USAGE_BAT_RESET_TO_ZERO 0x008500C2 +#define USAGE_BAT_AC_PRESENT 0x008500D0 +#define USAGE_BAT_BATTERY_PRESENT 0x008500D1 +#define USAGE_BAT_POWER_FAIL 0x008500D2 +#define USAGE_BAT_ALARM_INHIBITED 0x008500D3 +#define USAGE_BAT_THERMISTOR_UNDER_RANGE 0x008500D4 +#define USAGE_BAT_THERMISTOR_HOT 0x008500D5 +#define USAGE_BAT_THERMISTOR_COLD 0x008500D6 +#define USAGE_BAT_THERMISTOR_OVER_RANGE 0x008500D7 +#define USAGE_BAT_VOLTAGE_OUT_OF_RANGE 0x008500D8 +#define USAGE_BAT_CURRENT_OUT_OF_RANGE 0x008500D9 +#define USAGE_BAT_CURRENT_NOT_REGULATED 0x008500DA +#define USAGE_BAT_VOLTAGE_NOT_REGULATED 0x008500DB +#define USAGE_BAT_MASTER_MODE 0x008500DC +#define USAGE_BAT_CHARGER_SELECTOR_SUPPORT 0x008500F0 +#define USAGE_BAT_CHARGER_SPEC 0x008500F1 +#define USAGE_BAT_LEVEL_2 0x008500F2 +#define USAGE_BAT_LEVEL_3 0x008500F3 + /* * HIDNode_t struct * @@ -110,7 +291,7 @@ typedef struct { /* * HIDData struct * - * Describe a HID Data with its location in report + * Describe a HID Data with its location in report * -------------------------------------------------------------------------- */ typedef struct { HIDPath_t Path; /* HID Path */ @@ -139,9 +320,9 @@ typedef struct { * Holds a parsed report descriptor * -------------------------------------------------------------------------- */ typedef struct { - int nitems; /* number of items in descriptor */ + size_t nitems; /* number of items in descriptor */ HIDData_t *item; /* list of items */ - int replen[256]; /* list of report lengths, in byte */ + size_t replen[256]; /* list of report lengths, in byte */ } HIDDesc_t; #ifdef __cplusplus diff --git a/drivers/hpe-pdu-mib.c b/drivers/hpe-pdu-mib.c new file mode 100644 index 0000000..bf42987 --- /dev/null +++ b/drivers/hpe-pdu-mib.c @@ -0,0 +1,899 @@ +/* hpe-pdu-mib.c - subdriver to monitor HPE ePDU SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2016 Arnaud Quette + * 2019 Arnaud Quette + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "hpe-pdu-mib.h" +#include "dstate.h" + +#define HPE_EPDU_MIB_VERSION "0.33" +#define HPE_EPDU_MIB_SYSOID ".1.3.6.1.4.1.232.165.7" +#define HPE_EPDU_OID_MODEL_NAME ".1.3.6.1.4.1.232.165.7.1.2.1.3.0" + +static info_lkp_t hpe_pdu_outlet_status_info[] = { + { 1, "off", NULL, NULL }, + { 2, "on", NULL, NULL }, + { 3, "pendingOff", NULL, NULL }, /* transitional status */ + { 4, "pendingOn", NULL, NULL }, /* transitional status */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_outletgroups_status_info[] = { + { 1, "N/A", NULL, NULL }, /* notApplicable, if group.type == outlet-section */ + { 2, "on", NULL, NULL }, /* breakerOn */ + { 3, "off", NULL, NULL }, /* breakerOff */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_outlet_switchability_info[] = { + { 1, "yes", NULL, NULL }, + { 2, "no", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* The physical type of outlet */ +static info_lkp_t hpe_pdu_outlet_type_info[] = { + { 0, "unknown", NULL, NULL }, + { 1, "iecC13", NULL, NULL }, + { 2, "iecC19", NULL, NULL }, + { 10, "uk", NULL, NULL }, + { 11, "french", NULL, NULL }, + { 12, "schuko", NULL, NULL }, + { 20, "nema515", NULL, NULL }, + { 21, "nema51520", NULL, NULL }, + { 22, "nema520", NULL, NULL }, + { 23, "nemaL520", NULL, NULL }, + { 24, "nemaL530", NULL, NULL }, + { 25, "nema615", NULL, NULL }, + { 26, "nema620", NULL, NULL }, + { 27, "nemaL620", NULL, NULL }, + { 28, "nemaL630", NULL, NULL }, + { 29, "nemaL715", NULL, NULL }, + { 30, "rf203p277", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_ambient_presence_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "no", NULL, NULL }, /* disconnected */ + { 2, "yes", NULL, NULL }, /* connected */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_threshold_status_info[] = { + { 1, "good", NULL, NULL }, /* No threshold triggered */ + { 2, "warning-low", NULL, NULL }, /* Warning low threshold triggered */ + { 3, "critical-low", NULL, NULL }, /* Critical low threshold triggered */ + { 4, "warning-high", NULL, NULL }, /* Warning high threshold triggered */ + { 5, "critical-high", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_threshold_frequency_status_info[] = { + { 1, "good", NULL, NULL }, /* No threshold triggered */ + { 2, "out-of-range", NULL, NULL }, /* Frequency out of range triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 0, "unknown", NULL, NULL }, + { 1, "open", NULL, NULL }, + { 2, "closed", NULL, NULL }, + { 3, "bad", NULL, NULL }, /* FIXME: what to do with that? */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_threshold_voltage_alarms_info[] = { + { 1, "", NULL, NULL }, /* No threshold triggered */ + { 2, "low voltage warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 3, "low voltage critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 4, "high voltage warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 5, "high voltage critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_threshold_current_alarms_info[] = { + { 1, "", NULL, NULL }, /* No threshold triggered */ + { 2, "low current warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 3, "low current critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 4, "high current warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 5, "high current critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_threshold_frequency_alarm_info[] = { + { 1, "", NULL, NULL }, /* No threshold triggered */ + { 2, "frequency out of range!", NULL, NULL }, /* Frequency out of range triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_threshold_temperature_alarms_info[] = { + { 1, "", NULL, NULL }, /* No threshold triggered */ + { 2, "low temperature warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 3, "low temperature critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 4, "high temperature warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 5, "high temperature critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_threshold_humidity_alarms_info[] = { + { 1, "", NULL, NULL }, /* No threshold triggered */ + { 2, "low humidity warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 3, "low humidity critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 4, "high humidity warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 5, "high humidity critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_outlet_group_type_info[] = { + { 0, "unknown", NULL, NULL }, + { 1, "unknown", NULL, NULL }, + { 2, "breaker1pole", NULL, NULL }, + { 3, "breaker2pole", NULL, NULL }, + { 4, "breaker3pole", NULL, NULL }, + { 5, "outlet-section", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_input_type_info[] = { + { 1, "1", NULL, NULL }, /* singlePhase */ + { 2, "2", NULL, NULL }, /* splitPhase */ + { 3, "3", NULL, NULL }, /* threePhaseDelta */ + { 4, "3", NULL, NULL }, /* threePhaseWye */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t hpe_pdu_outlet_group_phase_info[] = { + { 1, "L1", NULL, NULL }, /* singlePhase */ + { 2, "L1", NULL, NULL }, /* phase1toN */ + { 3, "L2", NULL, NULL }, /* phase2toN */ + { 4, "L3", NULL, NULL }, /* phase3toN */ + { 5, "L1", NULL, NULL }, /* phase1to2 */ + { 6, "L2", NULL, NULL }, /* phase2to3 */ + { 7, "L3", NULL, NULL }, /* phase3to1 */ + { 0, NULL, NULL, NULL } +}; + +/* Snmp2NUT lookup table for HPE PDU MIB */ +static snmp_info_t hpe_pdu_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* Device collection */ + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "HPE", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* pdu2Model.0 = STRING: "HP 8.6kVA 208V 30A 3Ph NA/JP maPDU" */ + { "device.model", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.1.2.1.3.%i", + "HPE ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* pdu2SerialNumber.0 = STRING: "CN94230105" */ + { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.1.2.1.7.%i", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* pdu2PartNumber.0 = STRING: "H8B52A" */ + { "device.part", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.1.2.1.6.%i", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* For daisychain, there is only 1 physical interface! */ + { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.2.2.1.6.2", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* Daisychained devices support + * Notes: this definition is used to: + * - estimate the number of devices, based on the below OID iteration capabilities + * - determine the base index of the SNMP OID (ie 0 or 1) */ + /* pdu2NumberPDU.0 = INTEGER: 1 */ + { "device.count", 0, 1, + ".1.3.6.1.4.1.232.165.7.1.1.0", + "1", SU_FLAG_STATIC, NULL }, + + /* UPS collection */ + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "HPE", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.1.2.1.3.%i", + "HPE ePDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + + /* FIXME: use unitName.0 (ePDU)? + * { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, AR_OID_DEVICE_NAME, + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, */ + { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.1.2.1.7.%i", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* FIXME: this entry should be SU_FLAG_SEMI_STATIC */ + /* pdu2FirmwareVersion.0 = STRING: "02.00.0043" */ + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.1.2.1.5.%i", + "", SU_FLAG_OK, NULL }, + { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + + /* FIXME: needs a date reformating callback + * 2011-8-29,16:27:25.0,+1:0 + * Hex-STRING: 07 DB 08 1D 10 0C 36 00 2B 01 00 00 + * { "ups.date", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.8.0", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + * { "ups.time", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.1.2.1.8.0", + "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + */ + + /* Input collection */ + /* Note: for daisychain mode, we must handle phase(s) per device, not as a whole */ + /* pdu2InputType.0 = INTEGER: threePhaseWye(4) */ + { "input.phases", 0, 1, ".1.3.6.1.4.1.232.165.7.2.1.1.1.%i", + NULL, SU_FLAG_STATIC, &hpe_pdu_input_type_info[0] }, + + /* Frequency is measured globally */ + /* pdu2InputFrequency.0 = INTEGER: 500 */ + { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.232.165.7.2.1.1.2.%i", + NULL, 0, NULL }, + /* pdu2InputFrequencyStatus.0 = INTEGER: good(1) */ + { "input.frequency.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.1.1.3.%i", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_frequency_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.1.1.3.%i", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_frequency_alarm_info[0] }, + /* inputCurrentPercentLoad (measured globally) + * Current percent load, based on the rated current capacity */ + /* FIXME: input.load is mapped on input.L1.load for both single and 3phase !!! */ + { "input.load", 0, 1.0, ".1.3.6.1.4.1.534.6.6.7.3.3.1.11.%i.1.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2InputPhaseCurrentPercentLoad.0.1 = INTEGER: 0 */ + { "input.L1.load", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.18.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2InputPhaseCurrentPercentLoad.0.2 = INTEGER: 0 */ + { "input.L1.load", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.18.%i.2", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2InputPhaseCurrentPercentLoad.0.3 = INTEGER: 0 */ + { "input.L1.load", 0, 1.0, ".1.3.6.1.4.1.232.165.7.2.2.1.18.%i.3", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + + /* FIXME: + * - Voltage is only measured per phase, as mV! + * so input.voltage == input.L1.voltage for both single and 3phase + * - As per NUT namespace (http://www.networkupstools.org/docs/developer-guide.chunked/apas01.html#_valid_contexts) + * Voltage has to be expressed either phase-phase or phase-neutral + * This is depending on OID inputVoltageMeasType + * INTEGER {singlePhase (1),phase1toN (2),phase2toN (3),phase3toN (4),phase1to2 (5),phase2to3 (6),phase3to1 (7) + * => RFC input.Lx.voltage.context */ + /* pdu2InputPhaseVoltage.0.1 = INTEGER: 216790 */ + { "input.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.3.%i.1", + NULL, 0, NULL }, + /* pdu2InputPhaseVoltageThStatus.0.1 = INTEGER: good(1) */ + { "input.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_voltage_alarms_info[0] }, + /* pdu2InputPhaseVoltageThLowerWarning.0.1 = INTEGER: 190000 */ + { "input.voltage.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.5.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltageThLowerCritical.0.1 = INTEGER: 180000 */ + { "input.voltage.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.6.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltageThUpperWarning.0.1 = INTEGER: 255000 */ + { "input.voltage.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.7.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltageThUpperCritical.0.1 = INTEGER: 265000 */ + { "input.voltage.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.8.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltage.0.1 = INTEGER: 216790 */ + { "input.L1.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.3.%i.1", + NULL, 0, NULL }, + /* pdu2InputPhaseVoltageThStatus.0.1 = INTEGER: good(1) */ + { "input.L1.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, + { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_voltage_alarms_info[0] }, + /* pdu2InputPhaseVoltageThLowerWarning.0.1 = INTEGER: 190000 */ + { "input.L1.voltage.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.5.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltageThLowerCritical.0.1 = INTEGER: 180000 */ + { "input.L1.voltage.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.6.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltageThUpperWarning.0.1 = INTEGER: 255000 */ + { "input.L1.voltage.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.7.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltageThUpperCritical.0.1 = INTEGER: 265000 */ + { "input.L1.voltage.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.8.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltage.0.2 = INTEGER: 216790 */ + { "input.L2.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.3.%i.2", + NULL, 0, NULL }, + /* pdu2InputPhaseVoltageThStatus.0.2 = INTEGER: good(1) */ + { "input.L2.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.2", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, + { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.2", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_voltage_alarms_info[0] }, + /* pdu2InputPhaseVoltageThLowerWarning.0.2 = INTEGER: 190000 */ + { "input.L2.voltage.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.5.%i.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltageThLowerCritical.0.2 = INTEGER: 180000 */ + { "input.L2.voltage.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.6.%i.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltageThUpperWarning.0.2 = INTEGER: 255000 */ + { "input.L2.voltage.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.7.%i.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltageThUpperCritical.0.2 = INTEGER: 265000 */ + { "input.L2.voltage.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.8.%i.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltage.0.3 = INTEGER: 216790 */ + { "input.L3.voltage", 0, 0.001, ".1.3.6.1.4.1.232.165.7.2.2.1.3.%i.3", + NULL, 0, NULL }, + /* pdu2InputPhaseVoltageThStatus.0.3 = INTEGER: good(1) */ + { "input.L3.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.3", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, + { "L3.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.4.%i.3", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_voltage_alarms_info[0] }, + /* pdu2InputPhaseVoltageThLowerWarning.0.3 = INTEGER: 190000 */ + { "input.L3.voltage.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.5.%i.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltageThLowerCritical.0.3 = INTEGER: 180000 */ + { "input.L3.voltage.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.6.%i.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltageThUpperWarning.0.3 = INTEGER: 255000 */ + { "input.L3.voltage.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.7.%i.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseVoltageThUpperCritical.0.3 = INTEGER: 265000 */ + { "input.L3.voltage.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.8.%i.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* FIXME: + * - input.current is mapped on input.L1.current for both single and 3phase !!! */ + /* pdu2InputPhaseCurrent.0.1 = INTEGER: 185 */ + { "input.current", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.11.%i.1", + NULL, 0, NULL }, + /* pdu2InputPhaseCurrentRating.0.1 = INTEGER: 24000 */ + { "input.current.nominal", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.10.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThStatus.0.1 = INTEGER: good(1) */ + { "input.current.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_current_alarms_info[0] }, + /* pdu2InputPhaseCurrentThLowerWarning.0.1 = INTEGER: 0 */ + { "input.current.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.13.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThLowerCritical.0.1 = INTEGER: -1 */ + { "input.current.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.14.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThUpperWarning.0.1 = INTEGER: 19200 */ + { "input.current.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.15.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThUpperCritical.0.1 = INTEGER: 24000 */ + { "input.current.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.16.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrent.0.1 = INTEGER: 185 */ + { "input.L1.current", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.11.%i.1", + NULL, 0, NULL }, + /* pdu2InputPhaseCurrentRating.0.1 = INTEGER: 24000 */ + { "input.L1.current.nominal", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.10.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThStatus.0.1 = INTEGER: good(1) */ + { "input.L1.current.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, + { "L1.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_current_alarms_info[0] }, + /* pdu2InputPhaseCurrentThLowerWarning.0.1 = INTEGER: 0 */ + { "input.L1.current.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.13.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThLowerCritical.0.1 = INTEGER: -1 */ + { "input.L1.current.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.14.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThUpperWarning.0.1 = INTEGER: 19200 */ + { "input.L1.current.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.15.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThUpperCritical.0.1 = INTEGER: 24000 */ + { "input.L1.current.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.16.%i.1", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrent.0.2 = INTEGER: 185 */ + { "input.L2.current", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.11.%i.2", + NULL, 0, NULL }, + /* pdu2InputPhaseCurrentRating.0.2 = INTEGER: 24000 */ + { "input.L2.current.nominal", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.10.%i.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThStatus.0.2 = INTEGER: good(1) */ + { "input.L2.current.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.2", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, + { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.2", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_current_alarms_info[0] }, + /* pdu2InputPhaseCurrentThLowerWarning.0.2 = INTEGER: 0 */ + { "input.L2.current.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.13.%i.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThLowerCritical.0.2 = INTEGER: -1 */ + { "input.L2.current.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.14.%i.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThUpperWarning.0.2 = INTEGER: 19200 */ + { "input.L2.current.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.15.%i.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThUpperCritical.0.2 = INTEGER: 24000 */ + { "input.L2.current.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.16.%i.2", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrent.0.3 = INTEGER: 185 */ + { "input.L3.current", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.11.%i.3", + NULL, 0, NULL }, + /* pdu2InputPhaseCurrentRating.0.3 = INTEGER: 24000 */ + { "input.L3.current.nominal", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.10.%i.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThStatus.0.3 = INTEGER: good(1) */ + { "input.L3.current.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.3", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, + { "L2.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.2.2.1.12.%i.2", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_current_alarms_info[0] }, + /* pdu2InputPhaseCurrentThLowerWarning.0.3 = INTEGER: 0 */ + { "input.L3.current.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.13.%i.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThLowerCritical.0.3 = INTEGER: -1 */ + { "input.L3.current.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.14.%i.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThUpperWarning.0.3 = INTEGER: 19200 */ + { "input.L3.current.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.15.%i.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPhaseCurrentThUpperCritical.0.3 = INTEGER: 24000 */ + { "input.L3.current.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.2.2.1.16.%i.3", + NULL, SU_FLAG_NEGINVALID, NULL }, + /* pdu2InputPowerWatts.0 = INTEGER: 19 */ + { "input.realpower", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.2.1.1.5.%i", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, + /* pdu2InputPhasePowerWatts.0.1 = INTEGER: 19 */ + { "input.L1.realpower", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.2.2.1.21.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2InputPhasePowerWatts.0.2 = INTEGER: 0 */ + { "input.L2.realpower", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.2.2.1.21.%i.2", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2InputPhasePowerWatts.0.3 = INTEGER: 0 */ + { "input.L3.realpower", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.2.2.1.21.%i.3", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* Sum of all phases apparent power, valid for Shark 1ph/3ph only */ + /* pdu2InputPowerVA.0 = INTEGER: 39 */ + { "input.power", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.2.1.1.4.%i", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_UNIQUE | SU_FLAG_OK, NULL }, + /* pdu2InputPhasePowerVA.0.1 = INTEGER: 40 */ + { "input.L1.power", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.2.2.1.20.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2InputPhasePowerVA.0.2 = INTEGER: 0 */ + { "input.L2.power", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.2.2.1.20.%i.2", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2InputPhasePowerVA.0.3 = INTEGER: 0 */ + { "input.L3.power", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.2.2.1.20.%i.3", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + + /* TODO: handle statistics */ + /* pdu2InputPowerWattHour.0 = INTEGER: 91819 + { "unmapped.pdu2InputPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.7.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, */ + /* pdu2InputPowerWattHourTimer.0 = STRING: "16/10/2017,17:58:53" + { "unmapped.pdu2InputPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.2.1.1.7.0", NULL, SU_FLAG_OK, NULL }, */ + /* pdu2InputPowerFactor.0 = INTEGER: 483 */ + { "input.powerfactor", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.2.1.1.8.%i", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + + /* Ambient collection */ + /* pdu2TemperatureProbeStatus.0.1 = INTEGER: disconnected(1) */ + { "ambient.present", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.4.2.1.3.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_ambient_presence_info[0] }, + /* pdu2TemperatureThStatus.0.1 = INTEGER: good(1) */ + { "ambient.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.4.2.1.5.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.4.2.1.5.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_temperature_alarms_info[0] }, + /* pdu2TemperatureValue.0.1 = INTEGER: 0 */ + { "ambient.temperature", 0, 0.1, + ".1.3.6.1.4.1.232.165.7.4.2.1.4.%i.1", + NULL, SU_FLAG_OK, NULL }, + /* Low and high threshold use the respective critical levels */ + /* pdu2TemperatureThLowerCritical.0.1 = INTEGER: 50 */ + { "ambient.temperature.low", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.232.165.7.4.2.1.7.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.temperature.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.232.165.7.4.2.1.7.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2TemperatureThLowerWarning.0.1 = INTEGER: 100 */ + { "ambient.temperature.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.232.165.7.4.2.1.6.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2TemperatureThUpperCritical.0.1 = INTEGER: 650 */ + { "ambient.temperature.high", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.232.165.7.4.2.1.9.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.temperature.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.232.165.7.4.2.1.9.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2TemperatureThUpperWarning.0.1 = INTEGER: 200 */ + { "ambient.temperature.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.232.165.7.4.2.1.8.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2HumidityThStatus.0.1 = INTEGER: good(1) */ + { "ambient.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.4.3.1.5.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.4.3.1.5.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_threshold_humidity_alarms_info[0] }, + /* pdu2HumidityValue.0.1 = INTEGER: 0 */ + { "ambient.humidity", 0, 0.1, + ".1.3.6.1.4.1.232.165.7.4.3.1.4.%i.1", + NULL, SU_FLAG_OK, NULL }, + /* Low and high threshold use the respective critical levels */ + /* pdu2HumidityThLowerCritical.0.1 = INTEGER: 100 */ + { "ambient.humidity.low", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.232.165.7.4.3.1.7.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.humidity.low.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.232.165.7.4.3.1.7.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2HumidityThLowerWarning.0.1 = INTEGER: 200 */ + { "ambient.humidity.low.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.232.165.7.4.3.1.6.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2HumidityThUpperWarning.0.1 = INTEGER: 250 */ + { "ambient.humidity.high.warning", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.232.165.7.4.3.1.8.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* pdu2HumidityThUpperCritical.0.1 = INTEGER: 900 */ + { "ambient.humidity.high", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.232.165.7.4.3.1.9.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + { "ambient.humidity.high.critical", ST_FLAG_RW, 0.1, + ".1.3.6.1.4.1.232.165.7.4.3.1.9.%i.1", + NULL, SU_FLAG_NEGINVALID | SU_FLAG_OK, NULL }, + /* Dry contacts on TH module */ + /* pdu2ContactState.0.1 = INTEGER: contactBad(3) */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.4.4.1.4.%i.1", + NULL, SU_FLAG_OK, &hpe_pdu_ambient_drycontacts_info[0] }, + /* pdu2ContactState.0.2 = INTEGER: contactBad(3) */ + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.4.4.1.4.%i.2", + NULL, SU_FLAG_OK, &hpe_pdu_ambient_drycontacts_info[0] }, + + /* Outlet collection */ + { "outlet.id", 0, 1, NULL, + "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* pdu2OutletCount.0 = INTEGER: 24 */ + { "outlet.count", 0, 1, + ".1.3.6.1.4.1.232.165.7.1.2.1.12.%i", + "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* outlet template definition + * Indexes start from 1, ie outlet.1 => .1 */ + /* Note: the first definition is used to determine the base index (ie 0 or 1) */ + /* pdu2OutletName.0.%i = STRING: "Outlet L1-%i" */ + { "outlet.%i.desc", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.5.1.1.2.%i.%i", + NULL, SU_FLAG_STATIC | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pdu2OutletControlStatus.0.%i = INTEGER: on(2) */ + { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.5.2.1.1.%i.%i", + NULL, SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, &hpe_pdu_outlet_status_info[0] }, + /* Numeric identifier of the outlet, tied to the whole unit */ + { "outlet.%i.id", 0, 1, NULL, "%i", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + + +#if 0 + /* FIXME: the last part of the OID gives the group number (i.e. %i.1 means "group 1") + * Need to address that, without multiple declaration (%i.%i, SU_OUTLET | SU_OUTLET_GROUP)? */ + { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.1", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.2", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.3", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.4", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.5", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + { "outlet.%i.groupid", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.6.7.6.2.1.3.%i.%i.6", + NULL, SU_FLAG_STATIC | SU_FLAG_UNIQUE | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, +#endif + + /* pdu2OutletCurrent.0.%i = INTEGER: 0 */ + { "outlet.%i.current", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.5.1.1.5.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pdu2OutletCurrentThStatus.0.%i = INTEGER: good(1) */ + { "outlet.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.5.1.1.6.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, &hpe_pdu_threshold_status_info[0] }, + { "outlet.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.5.1.1.6.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, &hpe_pdu_threshold_current_alarms_info[0] }, + /* pdu2OutletCurrentThLowerWarning.0.%i = INTEGER: 0 */ + { "outlet.%i.current.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.5.1.1.7.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pdu2OutletCurrentThLowerCritical.0.%i = INTEGER: -1 */ + { "outlet.%i.current.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.5.1.1.8.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pdu2OutletCurrentThUpperWarning.0.1 = INTEGER: 8000 */ + { "outlet.%i.current.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.5.1.1.9.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pdu2OutletCurrentThUpperCritical.0.1 = INTEGER: 10000 */ + { "outlet.%i.current.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.5.1.1.10.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pdu2OutletWatts.0.1 = INTEGER: 0 */ + { "outlet.%i.realpower", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.5.1.1.14.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pdu2OutletVA.0.%i = INTEGER: 0 */ + { "outlet.%i.power", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.5.1.1.13.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pdu2OutletControlSwitchable.0.%i = INTEGER: switchable(1) */ + { "outlet.%i.switchable", ST_FLAG_RW, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.5.2.1.8.%i.%i", + "no", SU_FLAG_STATIC | SU_OUTLET | SU_FLAG_OK | SU_TYPE_DAISY_1, + &hpe_pdu_outlet_switchability_info[0] }, + /* pdu2OutletType.0.%i = INTEGER: iecC13(1) */ + { "outlet.%i.type", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.5.1.1.3.%i.%i", + "unknown", SU_FLAG_STATIC | SU_OUTLET | SU_TYPE_DAISY_1, + &hpe_pdu_outlet_type_info[0] }, + /* pdu2OutletPowerFactor.0.%i = INTEGER: 1000 */ + { "outlet.%i.powerfactor", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.5.1.1.17.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* TODO: handle statistics */ + /* pdu2OutletWh.0.1 = INTEGER: 1167 + * Note: setting this to zero resets the counter and timestamp => instcmd ???counter???.reset + { "unmapped.pdu2OutletWh", 0, 1, ".1.3.6.1.4.1.232.165.7.5.1.1.15.%i.%i", NULL, SU_FLAG_OK, NULL }, */ + /* pdu2OutletWhTimer.0.1 = STRING: "25/03/2016,09:03:26" + { "unmapped.pdu2OutletWhTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.5.1.1.16.%i.%i", NULL, SU_FLAG_OK, NULL }, */ + + /* Outlet groups collection */ + /* pdu2GroupCount.0 = INTEGER: 3 */ + { "outlet.group.count", 0, 1, + ".1.3.6.1.4.1.232.165.7.1.2.1.11.%i", + "0", SU_FLAG_STATIC | SU_TYPE_DAISY_1, NULL }, + /* outlet groups template definition + * Indexes start from 1, ie outlet.group.1 => .1 */ + /* Note: the first definition is used to determine the base index (ie 0 or 1) */ + /* pdu2GroupIndex.0.%i = INTEGER: %i */ + { "outlet.group.%i.id", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.3.1.1.1.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupName.0.%i = STRING: "Section L1" */ + { "outlet.group.%i.name", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.3.1.1.2.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupType.0.%i = INTEGER: breaker2pole(3) */ + { "outlet.group.%i.type", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.3.1.1.3.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, &hpe_pdu_outlet_group_type_info[0] }, + /* pdu2GroupVoltageMeasType.0.1 = INTEGER: phase1to2(5) */ + { "outlet.group.%i.phase", 0, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.3.1.1.4.%i.%i", + NULL, SU_FLAG_STATIC | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &hpe_pdu_outlet_group_phase_info[0] }, + /* pdu2groupBreakerStatus.0.%i = INTEGER: breakerOn(2) */ + { "outlet.group.%i.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.3.1.1.27.%i.%i", + NULL, SU_FLAG_OK | SU_FLAG_NAINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &hpe_pdu_outletgroups_status_info[0] }, + /* pdu2GroupOutletCount.0.%i = INTEGER: 8 */ + { "outlet.group.%i.count", 0, 1, + ".1.3.6.1.4.1.232.165.7.3.1.1.26.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupVoltage.0.%i = INTEGER: 216760 */ + { "outlet.group.%i.voltage", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.3.1.1.5.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupVoltageThStatus.0.%i = INTEGER: good(1) */ + { "outlet.group.%i.voltage.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.3.1.1.6.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &hpe_pdu_threshold_status_info[0] }, + { "outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.3.1.1.6.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &hpe_pdu_threshold_voltage_alarms_info[0] }, + /* pdu2GroupVoltageThLowerWarning.0.%i = INTEGER: 190000 */ + { "outlet.group.%i.voltage.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.3.1.1.7.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupVoltageThLowerCritical.0.%i = INTEGER: 180000 */ + { "outlet.group.%i.voltage.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.3.1.1.8.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupVoltageThUpperWarning.0.%i = INTEGER: 255000 */ + { "outlet.group.%i.voltage.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.3.1.1.9.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupVoltageThUpperCritical.0.%i = INTEGER: 265000 */ + { "outlet.group.%i.voltage.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.3.1.1.10.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupCurrent.0.%i = INTEGER: 0 */ + { "outlet.group.%i.current", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.3.1.1.12.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2groupCurrentRating.0.%i = INTEGER: 16000 */ + { "outlet.group.%i.current.nominal", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.3.1.1.11.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupCurrentThStatus.0.%i = INTEGER: good(1) */ + { "outlet.group.%i.current.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.3.1.1.13.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &hpe_pdu_threshold_status_info[0] }, + { "outlet.group.%i.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.232.165.7.3.1.1.13.%i.%i", + NULL, SU_OUTLET_GROUP | SU_TYPE_DAISY_1, + &hpe_pdu_threshold_current_alarms_info[0] }, + /* pdu2GroupCurrentThLowerWarning.0.%i = INTEGER: 0 */ + { "outlet.group.%i.current.low.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.3.1.1.14.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupCurrentThLowerCritical.0.%i = INTEGER: -1 */ + { "outlet.group.%i.current.low.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.3.1.1.15.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupCurrentThUpperWarning.0.%i = INTEGER: 12800 */ + { "outlet.group.%i.current.high.warning", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.3.1.1.16.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupCurrentThUpperCritical.0.%i = INTEGER: 16000 */ + { "outlet.group.%i.current.high.critical", ST_FLAG_RW, 0.001, + ".1.3.6.1.4.1.232.165.7.3.1.1.17.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupCurrentPercentLoad.0.%i = INTEGER: 0 */ + { "outlet.group.%i.load", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.3.1.1.19.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupPowerWatts.0.%i = INTEGER: 0 */ + { "outlet.group.%i.realpower", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.3.1.1.21.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupPowerVA.0.%i = INTEGER: 0 */ + { "outlet.group.%i.power", 0, 1.0, + ".1.3.6.1.4.1.232.165.7.3.1.1.20.%i.%i", + NULL, SU_FLAG_NEGINVALID | SU_OUTLET_GROUP | SU_TYPE_DAISY_1, NULL }, + /* pdu2GroupPowerFactor.0.%i = INTEGER: 1000 */ + { "outlet.group.%i.powerfactor", 0, 0.001, + ".1.3.6.1.4.1.232.165.7.3.1.1.24.%i.%i", + NULL, SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + + /* TODO: handle statistics */ + /* pdu2GroupPowerWattHour.0.%i = INTEGER: 1373 + * Note: setting this to zero resets the counter and timestamp => instcmd .reset + { "unmapped.pdu2GroupPowerWattHour", 0, 1, ".1.3.6.1.4.1.232.165.7.3.1.1.22.%i.%i", NULL, SU_FLAG_OK, NULL }, */ + /* pdu2GroupPowerWattHourTimer.0.%i = STRING: "25/03/2016,09:01:16" + { "unmapped.pdu2GroupPowerWattHourTimer", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.232.165.7.3.1.1.23.%i.%i", NULL, SU_FLAG_OK, NULL }, */ + + /* instant commands. */ + /* TODO: handle delays (outlet.%i.{on,off}.delay) */ + /* pdu2OutletControlOffCmd.0.%i = INTEGER: -1 */ + { "outlet.%i.load.off", 0, 1, + ".1.3.6.1.4.1.232.165.7.5.2.1.2.%i.%i", + "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pdu2OutletControlOnCmd.0.%i = INTEGER: -1 */ + { "outlet.%i.load.on", 0, 1, + ".1.3.6.1.4.1.232.165.7.5.2.1.3.%i.%i", + "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pdu2OutletControlRebootCmd.0.%i = INTEGER: -1 */ + { "outlet.%i.load.cycle", 0, 1, + ".1.3.6.1.4.1.232.165.7.5.2.1.4.%i.%i", + "0", SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + /* pdu2OutletControlOffCmd.0.%i = INTEGER: -1 */ + { "outlet.%i.load.off.delay", 0, 1, + ".1.3.6.1.4.1.232.165.7.5.2.1.2.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pdu2OutletControlOnCmd.0.%i = INTEGER: -1 */ + { "outlet.%i.load.on.delay", 0, 1, + ".1.3.6.1.4.1.232.165.7.5.2.1.3.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + /* pdu2OutletControlRebootCmd.0.%i = INTEGER: -1 */ + { "outlet.%i.load.cycle.delay", 0, 1, + ".1.3.6.1.4.1.232.165.7.5.2.1.4.%i.%i", + NULL, SU_TYPE_CMD | SU_OUTLET | SU_TYPE_DAISY_1, NULL }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + + +mib2nut_info_t hpe_pdu = { "hpe_epdu", HPE_EPDU_MIB_VERSION, NULL, HPE_EPDU_OID_MODEL_NAME, hpe_pdu_mib, HPE_EPDU_MIB_SYSOID, NULL }; diff --git a/drivers/hpe-pdu-mib.h b/drivers/hpe-pdu-mib.h new file mode 100644 index 0000000..0ccfcdf --- /dev/null +++ b/drivers/hpe-pdu-mib.h @@ -0,0 +1,30 @@ +/* hpe-pdu-mib.h - subdriver to monitor HPE ePDU SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2016 Arnaud Quette + * 2019 Arnaud Quette + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef HPE_EPDU_MIB_H +#define HPE_EPDU_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t hpe_pdu; + +#endif /* HPE_EPDU_MIB_H */ diff --git a/drivers/huawei-mib.c b/drivers/huawei-mib.c new file mode 100644 index 0000000..9ff193b --- /dev/null +++ b/drivers/huawei-mib.c @@ -0,0 +1,243 @@ +/* huawei-mib.c - subdriver to monitor Huawei SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2012 Arnaud Quette + * 2015 Stuart Henderson + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "huawei-mib.h" + +#define HUAWEI_MIB_VERSION "0.4" + +#define HUAWEI_SYSOID ".1.3.6.1.4.1.8072.3.2.10" +#define HUAWEI_UPSMIB ".1.3.6.1.4.1.2011" +#define HUAWEI_OID_MODEL_NAME ".1.3.6.1.4.1.2011.6.174.1.2.100.1.2.1" + +/* To create a value lookup structure (as needed on the 2nd line of the example + * below), use the following kind of declaration, outside of the present snmp_info_t[]: + * static info_lkp_t huawei_onbatt_info[] = { + * { 1, "OB", NULL, NULL }, + * { 2, "OL", NULL, NULL }, + * { 0, NULL, NULL, NULL } + * }; + */ + +static info_lkp_t huawei_supplymethod_info[] = { + { 1, "", NULL, NULL }, /* no supply */ + { 2, "OL BYPASS", NULL, NULL }, + { 3, "OL", NULL, NULL }, + { 4, "OB", NULL, NULL }, + { 5, "", NULL, NULL }, /* combined */ + { 6, "OL ECO", NULL, NULL }, + { 7, "OB ECO", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t huawei_battstate_info[] = { + { 1, "", NULL, NULL }, /* not connected */ + { 2, "", NULL, NULL }, /* not charging or discharging */ + { 3, "", NULL, NULL }, /* hibernation */ + { 4, "", NULL, NULL }, /* float */ + { 5, "CHRG", NULL, NULL }, /* equalized charging */ + { 6, "DISCHRG", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t huawei_phase_info[] = { + { 1, "1", NULL, NULL }, + { 2, "3", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t huawei_voltrating_info[] = { + { 1, "200", NULL, NULL }, + { 2, "208", NULL, NULL }, + { 3, "220", NULL, NULL }, + { 4, "380", NULL, NULL }, + { 5, "400", NULL, NULL }, + { 6, "415", NULL, NULL }, + { 7, "480", NULL, NULL }, + { 8, "600", NULL, NULL }, + { 9, "690", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t huawei_freqrating_info[] = { + { 1, "50", NULL, NULL }, + { 2, "60", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t huawei_pwrrating_info[] = { + { 1, "80000", NULL, NULL }, + { 2, "100000", NULL, NULL }, + { 3, "120000", NULL, NULL }, + { 4, "160000", NULL, NULL }, + { 5, "200000", NULL, NULL }, + { 6, "30000", NULL, NULL }, + { 7, "40000", NULL, NULL }, + { 8, "60000", NULL, NULL }, + { 9, "2400000", NULL, NULL }, + { 10, "2500000", NULL, NULL }, + { 11, "2800000", NULL, NULL }, + { 12, "3000000", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* Note: This is currently identical to ietf_test_result_info from IETF MIB + * We rename it here to a) allow evolution that may become incompatible; + * b) avoid namespace conflicts, especially with DMF loader of named objects + */ +static info_lkp_t huawei_test_result_info[] = { + { 1, "done and passed", NULL, NULL }, + { 2, "done and warning", NULL, NULL }, + { 3, "done and error", NULL, NULL }, + { 4, "aborted", NULL, NULL }, + { 5, "in progress", NULL, NULL }, + { 6, "no test initiated", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + + +/* HUAWEI Snmp2NUT lookup table */ +static snmp_info_t huawei_mib[] = { + + /* Data format: + * { info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar }, + * + * info_type: NUT INFO_ or CMD_ element name + * info_flags: flags to set in addinfo + * info_len: length of strings if STR + * cmd value if CMD, multiplier otherwise + * OID: SNMP OID or NULL + * dfl: default value + * flags: snmp-ups internal flags (FIXME: ...) + * oid2info: lookup table between OID and NUT values + * + * Example: + * { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, + * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, huawei_onbatt_info }, + * + * To create a value lookup structure (as needed on the 2nd line), use the + * following kind of declaration, outside of the present snmp_info_t[]: + * static info_lkp_t huawei_onbatt_info[] = { + * { 1, "OB" }, + * { 2, "OL" }, + * { 0, NULL } + * }; + */ + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* UPS page */ + + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Huawei", SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.2.1", "Generic SNMP UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.1.1.2.0", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + + { "ups.time", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.11.1.0", NULL, SU_FLAG_OK, NULL }, /* seconds since epoch */ + + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.3.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.5.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + + { "ups.status", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.2.101.1.1.1", NULL, SU_FLAG_OK, huawei_supplymethod_info }, + { "ups.status", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.2.101.1.3.1", NULL, SU_STATUS_BATT | SU_FLAG_OK, huawei_battstate_info }, + + { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.33.1.7.3.0", "", 0, huawei_test_result_info }, + + + /* Input page */ + + /* hwUpsCtrlInputStandard listed in MIB but not present on tested UPS5000-E */ + { "input.phases", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.102.100.1.8", "3", SU_FLAG_ABSENT | SU_FLAG_OK, huawei_phase_info }, + + { "input.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.1.1", NULL, SU_FLAG_OK, NULL }, + { "input.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.2.1", NULL, SU_FLAG_OK, NULL }, + { "input.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.3.1", NULL, SU_FLAG_OK, NULL }, + + { "input.frequency", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.4.1", NULL, SU_FLAG_OK, NULL }, + + { "input.L1.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.5.1", NULL, SU_FLAG_OK, NULL }, + { "input.L2.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.6.1", NULL, SU_FLAG_OK, NULL }, + { "input.L3.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.7.1", NULL, SU_FLAG_OK, NULL }, + + { "input.L1.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.8.1", NULL, SU_FLAG_OK, NULL }, + { "input.L2.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.9.1", NULL, SU_FLAG_OK, NULL }, + { "input.L3.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.3.100.1.10.1", NULL, SU_FLAG_OK, NULL }, + + { "input.bypass.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.1.1", NULL, SU_FLAG_OK, NULL }, + { "input.bypass.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.2.1", NULL, SU_FLAG_OK, NULL }, + { "input.bypass.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.3.1", NULL, SU_FLAG_OK, NULL }, + + { "input.bypass.frequency", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.5.100.1.4.1", NULL, SU_FLAG_OK, NULL }, + + + /* Output page */ + + /* hwUpsCtrlOutputStandard listed in MIB but not present on tested UPS5000-E */ + { "output.phases", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.102.100.1.9", "3", SU_FLAG_ABSENT | SU_FLAG_OK, huawei_phase_info }, + + { "output.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.1.1", NULL, SU_FLAG_OK, NULL }, + { "output.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.2.1", NULL, SU_FLAG_OK, NULL }, + { "output.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.3.1", NULL, SU_FLAG_OK, NULL }, + + { "output.L1.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.4.1", NULL, SU_FLAG_OK, NULL }, + { "output.L2.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.5.1", NULL, SU_FLAG_OK, NULL }, + { "output.L3.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.6.1", NULL, SU_FLAG_OK, NULL }, + + { "output.frequency", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.7.1", NULL, SU_FLAG_OK, NULL }, + + { "output.L1.realpower", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.8.1", NULL, SU_FLAG_OK, NULL }, + { "output.L1.realpower", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.9.1", NULL, SU_FLAG_OK, NULL }, + { "output.L1.realpower", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.10.1", NULL, SU_FLAG_OK, NULL }, + + { "output.L1.power", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.11.1", NULL, SU_FLAG_OK, NULL }, + { "output.L2.power", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.12.1", NULL, SU_FLAG_OK, NULL }, + { "output.L3.power", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.13.1", NULL, SU_FLAG_OK, NULL }, + + { "output.L1.power.percent", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.14.1", NULL, SU_FLAG_OK, NULL }, + { "output.L2.power.percent", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.15.1", NULL, SU_FLAG_OK, NULL }, + { "output.L3.power.percent", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.16.1", NULL, SU_FLAG_OK, NULL }, + + { "output.voltage.nominal", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.17.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, huawei_voltrating_info }, + { "output.frequency.nominal", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.18.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, huawei_freqrating_info }, + { "output.power.nominal", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.2011.6.174.1.2.100.1.6.1", NULL, SU_FLAG_STATIC | SU_FLAG_OK, huawei_pwrrating_info }, + + { "output.L1.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.19.1", NULL, SU_FLAG_OK, NULL }, + { "output.L2.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.20.1", NULL, SU_FLAG_OK, NULL }, + { "output.L2.powerfactor", 0, 0.01, ".1.3.6.1.4.1.2011.6.174.1.4.100.1.21.1", NULL, SU_FLAG_OK, NULL }, + + + /* Battery page */ + + { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.1.1", NULL, SU_FLAG_OK, NULL }, + { "battery.current", 0, 0.1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.2.1", NULL, SU_FLAG_OK, NULL }, + { "battery.charge", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.3.1", NULL, SU_FLAG_OK, NULL }, + { "battery.runtime", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.6.100.1.4.1", NULL, SU_FLAG_OK, NULL }, + + + /* { "unmapped.hwUpsBattTest", 0, 1, ".1.3.6.1.4.1.2011.6.174.1.103.101.1.6.1", NULL, SU_FLAG_OK, NULL }, */ + + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + +mib2nut_info_t huawei = { "huawei", HUAWEI_MIB_VERSION, NULL, HUAWEI_OID_MODEL_NAME, huawei_mib, HUAWEI_SYSOID, NULL }; diff --git a/drivers/huawei-mib.h b/drivers/huawei-mib.h new file mode 100644 index 0000000..326a71e --- /dev/null +++ b/drivers/huawei-mib.h @@ -0,0 +1,9 @@ +#ifndef HUAWEI_MIB_H +#define HUAWEI_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t huawei; + +#endif /* HUAWEI_MIB_H */ diff --git a/drivers/huawei-ups2000.c b/drivers/huawei-ups2000.c new file mode 100644 index 0000000..c144fcc --- /dev/null +++ b/drivers/huawei-ups2000.c @@ -0,0 +1,2122 @@ +/* + * huawei-ups2000.c - Driver for Huawei UPS2000 (1kVA-3kVA) + * + * Note: Huawei UPS2000 (1kVA-3kVA) can be accessed via RS-232, + * USB, or an optional RMS-MODBUS01B (RS-485) adapter. Only + * RS-232 and USB are supported, RS-485 is not. + * + * The USB port on the UPS is implemented via a MaxLinear RX21V1410 + * USB-to-serial converter, and can be recongized as a standard + * USB-CDC serial device. Unfortunately, the generic USB-CDC driver + * is incompatible with the specific chip configuration and cannot + * be used. A device-specific driver, "xr_serial", must be used. + * + * The driver has only been merged to Linux 5.12 or later, via the + * "xr_serial" kernel module. When the UPS2000 is connected via USB + * to a supported Linux system, you should see the following logs in + * "dmesg". + * + * xr_serial 1-1.2:1.1: xr_serial converter detected + * usb 1-1.2: xr_serial converter now attached to ttyUSB0 + * + * The driver must be "xr_serial". If your system doesn't have the + * necessary device driver, you will get this message instead: + * + * cdc_acm 1-1.2:1.0: ttyACM0: USB ACM device + * + * On other operating systems, USB cannot be used due to the absence + * of the driver. You must use connect UPS2000 to your computer via + * RS-232, either directly or using an USB-to-RS-232 converter supported + * by your Linux or BSD kernel. + * + * A document describing the protocol implemented by this driver can + * be found online at: + * + * https://support.huawei.com/enterprise/en/doc/EDOC1000110696 + * + * Huawei UPS2000 driver implemented by + * Copyright (C) 2020, 2021 Yifeng Li + * The author is not affiliated to Huawei or other manufacturers. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" /* must be the first header */ + +#include +#include +#include "main.h" +#include "serial.h" + +#define DRIVER_NAME "NUT Huawei UPS2000 (1kVA-3kVA) RS-232 Modbus driver" +#define DRIVER_VERSION "0.02" + +#define CHECK_BIT(var,pos) ((var) & (1<<(pos))) +#define MODBUS_SLAVE_ID 1 + +/* + * Known UPS models. We only attempt to load the driver if + * the initial communication indicates the UPS is a known + * model of the UPS2000 series. + */ +static const char *supported_model[] = { + "UPS2000", "UPS2000A", + NULL +}; + +/* + * UPS2000 device identification. The information is obtained during + * initial communication using Modbus command 0x2B (read device identi- + * fication) to read the object 0x87 (device list). The object contains + * a list of fields, each with a type, length, and value. The object is + * parsed by ups2000_device_identification() and filled into the array + * of struct ups2000_ident. + * + * Fields of interest are: + * + * 0x87, int32 (Device Count): Only one UPS unit is supported, + * the driver aborts if more than one device is detected. + * + * 0x88, string (Device Description of the 1st unit): This is a + * ASCII string that contains information about the 1st UPS unit. + * This string, again, contains a list of fields. They are parsed + * further into the array ups2000_desc. + * + */ +#define UPS2000_IDENT_MAX_FIELDS 9 +#define UPS2000_IDENT_MAX_LEN 128 +#define UPS2000_IDENT_OFFSET +static struct { + uint8_t type; + uint8_t len; + uint8_t val[UPS2000_IDENT_MAX_LEN]; +} ups2000_ident[UPS2000_IDENT_MAX_FIELDS]; + +/* + * UPS2000 device description. The information is initially obtained + * as field 0x88 in the UPS2000 device identification. This field is + * a semicolon seperated ASCII string that contains multiple fields. + * It is parsed again by ups2000_device_identification() and filled + * into the ups2000_desc[] 2D array. The first dimension is used as + * a key to select the wanted field (defined in the following enmu, + * the second dimension is a NULL-terminated ASCII string. + * + * Note that ups2000_desc[0] is deliberately unused, the array begins + * at one, allowing mapping from UPS2000_DESC_* to ups2000_desc[] + * directly without using offsets. + */ +#define UPS2000_DESC_MAX_FIELDS 9 +#define UPS2000_DESC_MAX_LEN 128 +enum { + UPS2000_DESC_MODEL = 1, + UPS2000_DESC_FIRMWARE_REV, + UPS2000_DESC_PROTOCOL_REV, + UPS2000_DESC_ESN, + UPS2000_DESC_DEVICE_ID, /* currently unused */ + UPS2000_DESC_PARALLEL_ID /* currently unused */ +}; +static char ups2000_desc[UPS2000_DESC_MAX_FIELDS][UPS2000_DESC_MAX_LEN] = { { 0 } }; + +/* global variable for modbus communication */ +static modbus_t *modbus_ctx = NULL; + +/* + * How many seconds to wait before switching off/on/reboot the UPS? + * + * This can be set at startup time via a command-line argument, + * or at runtime by writing to RW variables "ups.delay.shutdown" + * and "ups.delay.start". See ups2000_delay_get/set. + */ +#define UPS2000_DELAY_INVALID 0xFFFF +static uint16_t ups2000_offdelay = UPS2000_DELAY_INVALID; +static uint16_t ups2000_ondelay = UPS2000_DELAY_INVALID; +static uint16_t ups2000_rebootdelay = UPS2000_DELAY_INVALID; + +/* + * Time when the current shutdown/reboot request is expected + * to complete. This is used to calculate the ETA, See + * ups2000_update_timers(). + */ +static time_t shutdown_at = 0; +static time_t reboot_at = 0; +static time_t start_at = 0; + +/* + * Is it safe to enter bypass mode? It's checked by ups2000_update_alarm() + * and used by ups2000_instcmd_bypass_start(). + */ +static bool bypass_available = 0; + +/* function prototypes */ +static int ups2000_update_info(void); +static int ups2000_update_status(void); +static int ups2000_update_alarm(void); +static int ups2000_update_timers(void); +static void ups2000_device_identification(void); +static size_t ups2000_read_serial(uint8_t *buf, size_t buf_len); +static int ups2000_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest); +static int ups2000_write_register(modbus_t *ctx, int addr, uint16_t val); +static int ups2000_write_registers(modbus_t *ctx, int addr, int nb, uint16_t *src); +static uint16_t crc16(uint8_t *buffer, uint16_t buffer_length); +static time_t time_seek(time_t t, int seconds); + +/* rw variables function prototypes */ +static int ups2000_update_rw_var(void); +static int setvar(const char *name, const char *val); +static int ups2000_autostart_set(const uint16_t reg, const char *string); +static int ups2000_autostart_get(const uint16_t reg); +static int ups2000_beeper_set(const uint16_t reg, const char *string); +static int ups2000_beeper_get(const uint16_t reg); +static void ups2000_delay_get(void); +static int ups2000_delay_set(const char *var, const char *string); + +/* instant command function prototypes */ +static void ups2000_init_instcmd(void); +static int instcmd(const char *cmd, const char *extra); +static int ups2000_instcmd_load_on(const uint16_t reg); +static int ups2000_instcmd_bypass_start(const uint16_t reg); +static int ups2000_instcmd_beeper_toggle(const uint16_t reg); +static int ups2000_instcmd_shutdown_stayoff(const uint16_t reg); +static int ups2000_instcmd_shutdown_return(const uint16_t reg); +static int ups2000_instcmd_shutdown_reboot(const uint16_t reg); +static int ups2000_instcmd_shutdown_reboot_graceful(const uint16_t reg); + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Yifeng Li \n", + DRV_EXPERIMENTAL, + { NULL } +}; + + +void upsdrv_initups(void) +{ + int r; + + upsdebugx(2, "upsdrv_initups"); + + /* + * This is an ugly workaround to a serious problem: libmodbus doesn't + * support device identification. Although there's a function called + * modbus_send_raw_request() for custom commands, but modbus_receive_ + * confirmation() assumes a message length in the header, which is + * incompatible with device identification - It simply stops reading + * in the middle of the message and cannot receive our message. Worse, + * there's no public API to receive a raw response. + * + * See: https://github.com/stephane/libmodbus/issues/231 + * + * Thus, the only thing we could do is opening it as a serial device + * for device identification, and reopen it via libmodbus for other + * commands as usual. We also have to copy the CRC-16 function from + * the libmodbus source code since there's no public API to use that... + */ + upsfd = ser_open(device_path); + ser_set_speed(upsfd, device_path, B9600); + ser_set_rts(upsfd, 0); + ser_set_dtr(upsfd, 0); + + modbus_ctx = modbus_new_rtu(device_path, 9600, 'N', 8, 1); + if (modbus_ctx == NULL) + fatalx(EXIT_FAILURE, "Unable to create the libmodbus context"); + +#if LIBMODBUS_VERSION_CHECK(3, 1, 2) + /* It can take as slow as 1 sec. for the UPS to respond. */ + modbus_set_response_timeout(modbus_ctx, 1, 0); +#else + { + struct timeval timeout; + timeout.tv_sec = 1; + timeout.tv_usec = 0; + modbus_set_response_timeout(modbus_ctx, &timeout); + } +#endif + + r = modbus_set_slave(modbus_ctx, MODBUS_SLAVE_ID); + if (r < 0) { + modbus_free(modbus_ctx); + fatalx(EXIT_FAILURE, "Invalid slave ID %d", MODBUS_SLAVE_ID); + } + + if (modbus_connect(modbus_ctx) == -1) { + modbus_free(modbus_ctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } +} + + +#define IDENT_REQUEST_LEN 7 +#define IDENT_RESPONSE_MAX_LEN 128 +#define IDENT_RESPONSE_HEADER_LEN 8 +#define IDENT_RESPONSE_CRC_LEN 2 +#define IDENT_FIELD_HEADER_LEN 2 +static void ups2000_device_identification(void) +{ + static const uint8_t ident_req[IDENT_REQUEST_LEN] = { + MODBUS_SLAVE_ID, /* addr */ + 0x2B, /* command: device identification */ + 0x0E, /* MEI type */ + 0x03, /* ReadDevID: extended identification */ + 0x87, /* Object ID: device list */ + 0x31, 0x75 /* CRC-16 */ + }; + + /* + * Response header: + * 0x01, 0x2B, 0x0E, 0x03, 0x03, 0x00, 0x00, 0x02 + * + * Response fields: + * header: 0x87, 0x04 // type (device counts), length + * data: uint32_t + * (e.g. 0x00, 0x00, 0x00, 0x01) + * + * header: 0x88, 0x?? // type (1st dev desc), length + * data: ASCII string + * (e.g. 1=UPS2000;2=V100R001C01SPC120;3=...) + * + * header: 0x89, 0x?? // type (2nd dev desc), length + * data: ASCII string + * + * ... + * header: 0xFF, 0x?? // type (120th dev desc), length + * data: ASCII string + * + * CRC-16: + * 0x??, 0x?? + */ + static const uint8_t expected_header[IDENT_RESPONSE_HEADER_LEN] = { + MODBUS_SLAVE_ID, + 0x2B, 0x0E, 0x03, 0x03, 0x00, 0x00, 0x02, + }; + + bool serial_fail = 0; /* unable to read from serial */ + uint16_t crc16_recv, crc16_calc; /* resp CRC */ + bool crc16_fail = 0; /* resp CRC failure */ + uint32_t ups_count = 0; /* number of UPS in the resp list */ + uint8_t ident_response[IDENT_RESPONSE_MAX_LEN]; /* resp buf */ + size_t ident_response_len; /* buf len */ + uint8_t *ident_response_end = NULL; /* buf end marker (excluding CRC) */ + uint8_t *ptr = NULL; /* buf iteratior */ + + /* a desc string copied from ups2000_ident[] */ + char *ups2000_ident_desc = NULL; + int i; + ssize_t r; + + /* attempt to obtain a response header with valid CRC. */ + for (i = 0; i < 3; i++) { + /* step 1: record response length and initialize ptr */ + upsdebugx(2, "ser_send_buf"); + + ser_flush_in(upsfd, "", nut_debug_level); + r = ser_send_buf(upsfd, ident_req, IDENT_REQUEST_LEN); + if (r != IDENT_REQUEST_LEN) { + fatalx(EXIT_FAILURE, "unable to send request!\n"); + } + + ident_response_len = ups2000_read_serial(ident_response, IDENT_RESPONSE_MAX_LEN); + ptr = ident_response; + ident_response_end = ptr + ident_response_len - IDENT_RESPONSE_CRC_LEN; + + /* step 2: check response length */ + if (ident_response_len == 0) { + upslogx(LOG_ERR, "unable to read from serial port %s, retry...", device_path); + serial_fail = 1; + continue; + } + else + serial_fail = 0; + + upsdebug_hex(2, "ups2000_read_serial() received", ptr, ident_response_len); + + if (ptr + IDENT_RESPONSE_HEADER_LEN > ident_response_end) { + fatalx(EXIT_FAILURE, "response header too short! " + "expected %d, received %zu.", + IDENT_RESPONSE_HEADER_LEN, ident_response_len); + } + + /* step 3: check response CRC-16 */ + crc16_recv = (uint16_t)((uint16_t)(ident_response_end[0]) << 8) | (uint16_t)(ident_response_end[1]); + if (ident_response_len < IDENT_RESPONSE_CRC_LEN + || (((uintmax_t)(ident_response_len) - IDENT_RESPONSE_CRC_LEN) > UINT16_MAX) + ) { + fatalx(EXIT_FAILURE, "response header shorter than CRC " + "or longer than UINT16_MAX!"); + } + + crc16_calc = crc16(ident_response, (uint16_t)(ident_response_len - IDENT_RESPONSE_CRC_LEN)); + if (crc16_recv == crc16_calc) { + crc16_fail = 0; + break; + } + crc16_fail = 1; + } + + /* step 4: check serial & CRC-16 verification status */ + if (serial_fail) + fatalx(EXIT_FAILURE, "unable to read from serial port %s!", device_path); + + if (crc16_fail) + fatalx(EXIT_FAILURE, "response CRC verification failed!"); + + /* step 5: check response header */ + if (memcmp(expected_header, ident_response, IDENT_RESPONSE_HEADER_LEN)) + fatalx(EXIT_FAILURE, "unexpected response header!"); + + ptr += IDENT_RESPONSE_HEADER_LEN; + + /* step 6: extract ident fields */ + memset(ups2000_ident, 0x00, sizeof(ups2000_ident)); + for (i = 0; i < UPS2000_IDENT_MAX_FIELDS; i++) { + uint8_t type, len; + + if (ptr + 2 > ident_response_end) + break; + + type = *ptr++; + len = *ptr++; + + if (len + 1 > UPS2000_IDENT_MAX_LEN) + fatalx(EXIT_FAILURE, "response field too long!"); + + ups2000_ident[i].type = type; + ups2000_ident[i].len = len; + /* + * Always zero-terminate the bytes, in case the data + * is an ASCII string (i.e. device desc string), libc + * string functions can be used. + */ + ups2000_ident[i].val[len] = '\0'; + + if (ptr + len > ident_response_end) + fatalx(EXIT_FAILURE, "response field too short!"); + + memcpy(ups2000_ident[i].val, ptr, len); + ptr += len; + } + + /* step 7: validate device identification field 0x87 and 0x88 */ + for (i = 0; i < UPS2000_IDENT_MAX_FIELDS; i++) { + /* only one device is supported */ + if (ups2000_ident[i].type == 0x87) { + /* so we assume 0x87 must be 1 */ + ups_count = + (uint32_t)(ups2000_ident[i].val[0]) << 24 | + (uint32_t)(ups2000_ident[i].val[1]) << 16 | + (uint32_t)(ups2000_ident[i].val[2]) << 8 | + (uint32_t)(ups2000_ident[i].val[3]); + } + if (ups2000_ident[i].type == 0x88) { + /* + * And only check 0x88, not 0x89, etc. Also copy the + * string for later parsing via strtok(). + */ + ups2000_ident_desc = strdup((char *) ups2000_ident[i].val); + break; + } + } + if (ups_count != 1) + fatalx(EXIT_FAILURE, "only 1 UPS is supported, %u found", ups_count); + + if (!ups2000_ident_desc) + fatalx(EXIT_FAILURE, "device desc string not found"); + + /* + * step 8: extract fields from the desc string. + * (1=UPS2000;2=V100R001C01SPC120;3=...) + */ + for (i = 0; i < UPS2000_DESC_MAX_FIELDS; i++) { + char *key; /* "1", "2", "3", ... */ + char *val; /* "UPS2000", "V100R001C01SPC120", ... */ + unsigned int idx = 0; + + if (i == 0) + key = strtok(ups2000_ident_desc, "="); + else + key = strtok(NULL, "="); + if (!key) + break; + + val = strtok(NULL, ";"); + if (!val) + break; + + r = str_to_uint_strict(key, &idx, 10); + if (!r || idx + 1 > UPS2000_DESC_MAX_FIELDS || idx < 1) + fatalx(EXIT_FAILURE, "desc index %d is invalid!", idx); + + if (strlen(val) + 1 > UPS2000_DESC_MAX_LEN) + fatalx(EXIT_FAILURE, "desc field %d too long!", idx); + + memcpy(ups2000_desc[idx], val, strlen(val) + 1); + } + free(ups2000_ident_desc); + + /* + * step 9: Validate desc fields that we are going to use are valid. + * + * Note: UPS2000_DESC_DEVICE_ID and UPS2000_DESC_PARALLEL_ID are + * currently unused and unchecked. + */ + for (i = UPS2000_DESC_MODEL; i <= UPS2000_DESC_ESN; i++) { + if (strlen(ups2000_desc[i]) == 0) + fatalx(EXIT_FAILURE, "desc field %d is missing!", i); + } +} + + +void upsdrv_initinfo(void) +{ + bool in_list = 0; + int i = 0; + + upsdebugx(2, "upsdrv_initinfo"); + + ups2000_device_identification(); + + /* check whether the UPS is a known model */ + for (i = 0; supported_model[i] != NULL; i++) { + if (!strcmp(supported_model[i], + ups2000_desc[UPS2000_DESC_MODEL])) { + in_list = 1; + } + } + if (!in_list) { + fatalx(EXIT_FAILURE, "Unknown UPS model %s", + ups2000_desc[UPS2000_DESC_MODEL]); + } + + dstate_setinfo("device.mfr", "Huawei"); + dstate_setinfo("device.type", "ups"); + dstate_setinfo("device.model", "%s", + ups2000_desc[UPS2000_DESC_MODEL]); + dstate_setinfo("device.serial", "%s", + ups2000_desc[UPS2000_DESC_ESN]); + + dstate_setinfo("ups.mfr", "Huawei"); + dstate_setinfo("ups.model", "%s", + ups2000_desc[UPS2000_DESC_MODEL]); + dstate_setinfo("ups.firmware", "%s", + ups2000_desc[UPS2000_DESC_FIRMWARE_REV]); + dstate_setinfo("ups.firmware.aux", "%s", + ups2000_desc[UPS2000_DESC_PROTOCOL_REV]); + dstate_setinfo("ups.serial", "%s", + ups2000_desc[UPS2000_DESC_ESN]); + dstate_setinfo("ups.type", "online"); + + /* RW variables */ + upsh.setvar = setvar; + + /* instant commands */ + ups2000_init_instcmd(); + upsh.instcmd = instcmd; +} + + +/* + * All registers are uint16_t. But the data they represent can + * be either an integer or a float. This information is used for + * error checking (int and float have different invalid values). + */ +enum { + REG_UINT16, + REG_UINT32, /* occupies two registers */ + REG_FLOAT, /* actually a misnomer, it should really be called + fixed-point number, but we follow the datasheet */ +}; +#define REG_UINT16_INVALID 0xFFFFU +#define REG_UINT32_INVALID 0xFFFFFFFFU +#define REG_FLOAT_INVALID 0x7FFFU + + +/* + * Declare UPS attribute variables, format strings, registers, + * and their scaling factors in a lookup table to avoid spaghetti + * code. + */ +static struct { + const char *name; + const char *fmt; + const uint16_t reg; + const int datatype; /* only UINT32 occupies 2 regs */ + const float scaling; /* scale it down to get the original */ +} ups2000_var[] = +{ + { "input.voltage", "%03.1f", 1000, REG_FLOAT, 10.0 }, + { "input.frequency", "%02.1f", 1003, REG_FLOAT, 10.0 }, + { "input.bypass.voltage", "%03.1f", 1004, REG_FLOAT, 10.0 }, + { "input.bypass.frequency", "%03.1f", 1007, REG_FLOAT, 10.0 }, + { "output.voltage", "%03.1f", 1008, REG_FLOAT, 10.0 }, + { "output.current", "%03.1f", 1011, REG_FLOAT, 10.0 }, + { "output.frequency", "%03.1f", 1014, REG_FLOAT, 10.0 }, + { "output.realpower", "%02.1f", 1015, REG_FLOAT, 0.01 }, /* 10 / 1 kW */ + { "output.power", "%03.1f", 1018, REG_FLOAT, 0.01 }, /* 10 / 1 kVA */ + { "ups.load", "%02.1f", 1021, REG_FLOAT, 10.0 }, + { "ups.temperature", "%02.1f", 1027, REG_FLOAT, 10.0 }, + { "battery.voltage", "%02.1f", 2000, REG_FLOAT, 10.0 }, + { "battery.charge", "%02.1f", 2003, REG_UINT16, 1.0 }, + { "battery.runtime", "%.0f", 2004, REG_UINT32, 1.0 }, + { "battery.packs", "%.0f", 2007, REG_UINT16, 1.0 }, + { "battery.capacity", "%.0f", 2033, REG_UINT16, 1.0 }, + { "ups.power.nominal", "%.0f", 9009, REG_FLOAT, 0.01 }, /* 10 / 1 kVA */ + { NULL, NULL, 0, 0, 0 }, +}; + + +static int ups2000_update_info(void) +{ + uint16_t reg[3][34]; + int i; + int r; + + upsdebugx(2, "ups2000_update_info"); + + /* + * All status registers have an offset of 10000 * ups_number. + * We only support 1 UPS, thus it's always 10000. Register + * 1000 becomes 11000. + */ + r = ups2000_read_registers(modbus_ctx, 11000, 28, reg[0]); + if (r != 28) + return 1; + + r = ups2000_read_registers(modbus_ctx, 12000, 34, reg[1]); + if (r != 34) + return 1; + + r = ups2000_read_registers(modbus_ctx, 19009, 1, ®[2][9]); + if (r != 1) + return 1; + + for (i = 0; ups2000_var[i].name != NULL; i++) { + uint16_t reg_id = ups2000_var[i].reg; + uint8_t page = (uint8_t)(reg_id / 1000 - 1); + uint8_t idx = (uint8_t)(reg_id % 1000); + uint32_t val; + bool invalid = 0; + + if (page == 8) /* hack for the lonely register 9009 */ + page = 2; + + if (page > 2 || idx > 33) /* also suppress compiler warn */ + fatalx(EXIT_FAILURE, "register calculation overflow!\n"); + + switch (ups2000_var[i].datatype) { + case REG_FLOAT: + val = reg[page][idx]; + if (val == REG_FLOAT_INVALID) + invalid = 1; + break; + case REG_UINT16: + val = reg[page][idx]; + if (val == REG_UINT16_INVALID) + invalid = 1; + break; + case REG_UINT32: + val = (uint32_t)(reg[page][idx]) << 16; + val |= (uint32_t)(reg[page][idx + 1]); + if (val == REG_UINT32_INVALID) + invalid = 1; + break; + default: + fatalx(EXIT_FAILURE, "invalid data type in register table!\n"); + } + + if (invalid) { + upslogx(LOG_ERR, "register %04d has invalid value %04x,", reg_id, val); + return 1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + dstate_setinfo(ups2000_var[i].name, ups2000_var[i].fmt, + (float) val / ups2000_var[i].scaling); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + } + return 0; +} + + +/* + * A lookup table of all the status registers and the list of + * corresponding flags they represent. A register may set multiple + * status flags, represented by an array of flags_t. + * + * There are two types of flags. If the flag is a "status flag" + * for status_set(), for example, "OL" or "OB", the field + * "status_name" is used. If the flag is a "data variable" for + * dstate_setinfo(), the variable name and value is written in + * "var_name" and "var_val" fields. + * + * For each flag, if it's indicated by a specific value in a + * register, the "val" field is used. If a flag is indicated by + * a bit, the "bit" field should be used. Fields "val" and "bit" + * cannot be used at the same time, at least one must be "-1". + * + * Also, some important registers indicate basic system status + * (e.g. whether the UPS is on line power or battery), this info + * must always be available, and they are always expected to set + * at least one flag. If the important register does not set any + * flag, it means we've received an invalid or unknown value, + * and we must report an error. The "must_set_flag" field is used + * for this purpose. + */ +static struct { + const uint16_t reg; + bool must_set_flag; + struct flags_t { + const char *status_name; + const int16_t val; + const int bit; + const char *var_name, *var_val; + } flags[10]; +} ups2000_status_reg[] = +{ + { 1024, 1, { + { "OFF", 0, -1, NULL, NULL }, + { "BYPASS", 1, -1, NULL, NULL }, + { "OL", 2, -1, NULL, NULL }, + { "OB", 3, -1, NULL, NULL }, + { "OL ECO", 5, -1, NULL, NULL }, + { NULL, -1, -1, NULL, NULL }, + }}, + { 1043, 0, { + { "CAL", -1, 2, NULL, NULL }, /* battery self-test */ + { "LB", -1, 6, NULL, NULL }, + { NULL, -1, -1, NULL, NULL }, + }}, + /* + * Note: 3 = float charging, 4 = equalization charging, but + * both of them are reported as "charging", not "floating". + * The definition of "floating" in NUT is: "battery has + * completed its charge cycle, and waiting to go to resting + * mode", which is not true for UPS2000. + */ + { 2002, 1, { + { "", 2, -1, "battery.charger.status", "resting" }, + { "CHRG", 3, -1, "battery.charger.status", "charging" }, + { "CHRG", 4, -1, "battery.charger.status", "charging" }, + { "DISCHRG", 5, -1, "battery.charger.status", "discharging" }, + { NULL, -1, -1, NULL, NULL }, + }}, + { 0, 0, { { NULL, -1, -1, NULL, NULL } } } +}; + + +static int ups2000_update_status(void) +{ + int i, j; + int r; + + upsdebugx(2, "ups2000_update_status"); + + for (i = 0; ups2000_status_reg[i].reg != 0; i++) { + uint16_t reg, val; + struct flags_t *flag; + int flag_count = 0; + + reg = ups2000_status_reg[i].reg; + r = ups2000_read_registers(modbus_ctx, reg + 10000, 1, &val); + if (r != 1) + return 1; + + if (val == REG_UINT16_INVALID) { + upslogx(LOG_ERR, "register %04d has invalid value %04x,", reg, val); + return 1; + } + + flag = ups2000_status_reg[i].flags; + for (j = 0; flag[j].status_name != NULL; j++) { + /* + * if the register is equal to the "val" we are looking + * for, or if register has its n-th "bit" set... + */ + if ((flag[j].val != -1 && flag[j].val == val) || + (flag[j].bit != -1 && CHECK_BIT(val, flag[j].bit))) { + /* if it has a corresponding status flag */ + if (strlen(flag[j].status_name) != 0) + status_set(flag[j].status_name); + /* or if it has a corresponding dstate variable (or both) */ + if (flag[j].var_name && flag[j].var_val) + dstate_setinfo(flag[j].var_name, "%s", flag[j].var_val); + flag_count++; + } + } + if (ups2000_status_reg[i].must_set_flag && flag_count == 0) { + upslogx(LOG_ERR, "register %04d has invalid value %04x,", reg, val); + return 1; + } + } + + return 0; +} + + +/* + * A lookup table of all the alarm registers and the list of + * corresponding alarms they represent. Each alarm condition is + * listed by its register base address "reg" and its "bit" + * position. + * + * Each alarm condition has an "alarm_id", "alarm_cause_id", + * and "alarm_name". In addition, a few alarms conditions also + * indicates conditions related to batteries that is needed to + * be set via status_set(), those are listed in "status_name". + * Unused "status_name" is set to NULL. + * + * After an alarm is reported/cleared by the UPS, the "active" + * flag is changed to reflect its status. The error logging + * code uses this variable to issue warnings only when needed + * (i.e. only after a change, avoid issuing the same warning + * repeatedly). + */ +#define ALARM_CLEAR_AUTO 1 +#define ALARM_CLEAR_MANUAL 2 +#define ALARM_CLEAR_DEPENDING 3 + +static struct { + bool active; /* runtime: is this alarm currently active? */ + const uint16_t reg; /* alarm register to check */ + const int bit; /* alarm bit to check */ + const int alarm_clear; /* auto or manual clear */ + const int loglevel; /* warning or error */ + const int alarm_id, alarm_cause_id; + const char *status_name; /* corresponding NUT status word */ + const char *alarm_name; /* alarm string */ + const char *alarm_desc; /* brief explanation */ +} ups2000_alarm[] = +{ + { + false, 40156, 3, ALARM_CLEAR_AUTO, LOG_ALERT, + 30, 1, NULL, "UPS internal overtemperature", + "The ambient temperature is over 50-degree C. " + "Startup from standby mode is prohibited.", + }, + { + false, 40161, 1, ALARM_CLEAR_AUTO, LOG_WARNING, + 10, 1, NULL, "Abnormal bypass voltage", + "Bypass input is unavailable or out-of-range. Wait for " + "bypass input to recover, or change acceptable bypass " + "range via front panel.", + }, + { + false, 40161, 2, ALARM_CLEAR_AUTO, LOG_WARNING, + 10, 2, NULL, "Abnormal bypass frequency", + "Bypass input is unavailable or out-of-range. Wait for " + "bypass input to recover, or change acceptable bypass " + "range via front panel.", + }, + { + false, 40163, 3, ALARM_CLEAR_DEPENDING, LOG_WARNING, + 25, 1, NULL, "Battery overvoltage", + "When the UPS is started, voltage of each battery exceeds 15 V. " + "Or: current battery voltage exceeds 14.7 V.", + }, + { + false, 40164, 1, ALARM_CLEAR_AUTO, LOG_WARNING, + 29, 1, "RB", "Battery needs maintenance", + "During the last battery self-check, the battery voltage " + "was lower than the replacement threshold (11 V).", + }, + { + false, 40164, 3, ALARM_CLEAR_AUTO, LOG_WARNING, + 26, 1, NULL, "Battery undervoltage", + NULL, + }, + { + false, 40170, 4, ALARM_CLEAR_AUTO, LOG_ALERT, + 22, 1, NULL, "Battery disconnected", + "Battery is not connected, has loose connection, or faulty.", + }, + { + false, 40173, 5, ALARM_CLEAR_AUTO, LOG_ALERT, + 66, 1, "OVER", "Output overload (105%-110%)", + "UPS will shut down or transfer to bypass mode in 5-10 minutes.", + }, + { + false, 40173, 3, ALARM_CLEAR_AUTO, LOG_ALERT, + 66, 2, "OVER", "Output overload (110%-130%)", + "UPS will shut down or transfer to bypass mode in 30-60 seconds.", + }, + { + false, 40174, 0, ALARM_CLEAR_DEPENDING, LOG_ALERT, + 14, 1, NULL, "UPS startup timeout", + "The inverter output voltage is not within +/- 2 V of the " + "rated output. Or: battery is overdischarged.", + }, + { + false, 40179, 14, ALARM_CLEAR_MANUAL, LOG_ALERT, + 42, 15, NULL, "Rectifier fault (internal fault)", + "Bus voltage is lower than 320 V.", + }, + { + false, 40179, 15, ALARM_CLEAR_MANUAL, LOG_ALERT, + 42, 17, NULL, "Rectifier fault (internal fault)", + "Bus voltage is higher than 450 V.", + }, + { + false, 40180, 1, ALARM_CLEAR_MANUAL, LOG_ALERT, + 42, 18, NULL, "Rectifier fault (internal fault)", + "Bus voltage is lower than 260 V.", + }, + { + false, 40180, 5, ALARM_CLEAR_AUTO, LOG_ALERT, + 42, 24, NULL, "EEPROM fault (internal fault)", + "Faulty EEPROM. All settings are restored to " + "factory default and cannot be saved.", + }, + { + false, 40180, 6, ALARM_CLEAR_MANUAL, LOG_ALERT, + 42, 27, NULL, "Inverter fault (internal fault)", + "Inverter output overvoltage, undervoltage or " + "undercurrent.", + }, + { + false, 40180, 7, ALARM_CLEAR_DEPENDING, LOG_ALERT, + 42, 28, NULL, "Inverter fault (internal fault)", + "The inverter output voltage is lower than 100 V.", + }, + { + false, 40180, 10, ALARM_CLEAR_MANUAL, LOG_ALERT, + 42, 31, NULL, "Inverter fault (internal fault)", + "The difference between the absolute value of the positive bus " + "voltage and that of the negative bus voltage is 100 V.", + }, + { + false, 40180, 11, ALARM_CLEAR_DEPENDING, LOG_ALERT, + 42, 32, NULL, "UPS internal overtemperature", + "The ambient temperature is over 50 degree C, " + "switching to bypass mode.", + + }, + { + false, 40180, 13, ALARM_CLEAR_MANUAL, LOG_ALERT, + 42, 36, NULL, "Charger fault (internal fault)", + "The charger has no output. Faulty internal connections.", + + }, + { + false, 40182, 4, ALARM_CLEAR_MANUAL, LOG_ALERT, + 42, 42, NULL, "Charger fault (internal fault)", + "The charger has no output while the inverter is on, " + "battery undervoltage. Faulty switching transistor.", + }, + { + false, 40182, 13, ALARM_CLEAR_MANUAL, LOG_ALERT, + 66, 3, "OVER", "Output overload shutdown", + "UPS has shutdown or transferred to bypass mode.", + }, + { + false, 40182, 14, ALARM_CLEAR_MANUAL, LOG_ALERT, + 66, 4, "OVER", "Bypass output overload shutdown", + "UPS has shutdown, bypass output was overload and exceeded " + "time limit.", + }, + { false, 0, -1, -1, -1, -1, -1, NULL, NULL, NULL } +}; + + +/* don't spam the syslog */ +static time_t alarm_logged_since = 0; +#define UPS2000_LOG_INTERVAL 600 /* 10 minutes */ + + +static int ups2000_update_alarm(void) +{ + uint16_t val[27]; + int i; + int r; + + char alarm_buf[128]; + size_t all_alarms_len = 0; + + int alarm_count = 0; + bool alarm_logged = 0; + bool alarm_rtfm = 0; + time_t now = time(NULL); + + upsdebugx(2, "ups2000_update_alarm"); + + /* + * All alarm registers have an offset of 1024 * ups_number. + * We only support 1 UPS, it's always 1024. + */ + r = ups2000_read_registers(modbus_ctx, ups2000_alarm[0].reg + 1024, 27, val); + if (r != 27) + return 1; + + bypass_available = 1; /* register 40161 hack, see comments below */ + + for (i = 0; ups2000_alarm[i].alarm_id != -1; i++) { + int idx = ups2000_alarm[i].reg - ups2000_alarm[0].reg; + if (idx > 26 || idx < 0) + fatalx(EXIT_FAILURE, "register calculation overflow!\n"); + + if (CHECK_BIT(val[idx], ups2000_alarm[i].bit)) { + int gotlen; + if (ups2000_alarm[i].reg == 40161) + /* + * HACK: special treatment for register 40161. If this + * register indicates an alarm, we need to lock the + * "bypass.on" command as a software foolproof mechanism. + * It's written to the global "bypass_available" flag. + */ + bypass_available = 0; + + alarm_count++; + + gotlen = snprintf(alarm_buf, 128, "(ID %02d/%02d): %s!", + ups2000_alarm[i].alarm_id, + ups2000_alarm[i].alarm_cause_id, + ups2000_alarm[i].alarm_name); + + if (gotlen < 0 || (uintmax_t)gotlen > SIZE_MAX) { + fatalx(EXIT_FAILURE, "alarm_buf preparation over/under-flow!\n"); + } + + all_alarms_len += (size_t)gotlen; + alarm_set(alarm_buf); + + if (ups2000_alarm[i].status_name) + status_set(ups2000_alarm[i].status_name); + + /* + * Log the warning only if it's a new alarm, or if a long time + * has paseed since we first warned it. + */ + if (!ups2000_alarm[i].active || + difftime(now, alarm_logged_since) >= UPS2000_LOG_INTERVAL) { + int loglevel; + const char *alarm_word; + + /* + * Most text editors have syntax highlighting, adding an + * alarm word makes the log more readable + */ + loglevel = ups2000_alarm[i].loglevel; + if (loglevel <= LOG_ERR) { + alarm_word = "ERROR"; + /* + * If at least one error is serious, suggest reading + * manual. + */ + alarm_rtfm = 1; + } + else { + alarm_word = "WARNING"; + } + + upslogx(loglevel, "%s: alarm %02d, Cause %02d: %s!", + alarm_word, + ups2000_alarm[i].alarm_id, + ups2000_alarm[i].alarm_cause_id, + ups2000_alarm[i].alarm_name); + + if (ups2000_alarm[i].alarm_desc) + upslogx(loglevel, "%s", ups2000_alarm[i].alarm_desc); + + switch (ups2000_alarm[i].alarm_clear) { + case ALARM_CLEAR_AUTO: + upslogx(loglevel, "This alarm can be auto cleared."); + break; + case ALARM_CLEAR_MANUAL: + upslogx(loglevel, "This alarm can only be manual cleared " + "via front panel."); + break; + case ALARM_CLEAR_DEPENDING: + upslogx(loglevel, "This alarm is auto or manual cleared " + "depending on the specific problem."); + } + + ups2000_alarm[i].active = 1; + alarm_logged = 1; + } + + } + else { + if (ups2000_alarm[i].active) { + upslogx(LOG_WARNING, "Cleared alarm %02d, Cause %02d: %s", + ups2000_alarm[i].alarm_id, + ups2000_alarm[i].alarm_cause_id, + ups2000_alarm[i].alarm_name); + ups2000_alarm[i].active = 0; + alarm_logged = 1; + } + } + + } + + if (alarm_count > 0) { + /* append this to the alarm string as a friendly reminder */ + int gotlen = snprintf(alarm_buf, 128, "Check log for details!"); + + if (gotlen < 0 || (uintmax_t)gotlen > SIZE_MAX) { + fatalx(EXIT_FAILURE, "alarm_buf preparation over/under-flow!\n"); + } + + all_alarms_len += (size_t)gotlen; + alarm_set(alarm_buf); + + /* if the alarm string is too long, replace it with this */ + if (all_alarms_len + 1 > ST_MAX_VALUE_LEN) { + alarm_init(); /* discard all original alarms */ + snprintf(alarm_buf, 128, "UPS has %d alarms in effect, " + "check log for details!", alarm_count); + alarm_set(alarm_buf); + } + + /* + * If we are doing a syslog, write the final message and refresh the + * do-not-spam-the-log timer "alarm_logged_since". + */ + if (alarm_logged) { + upslogx(LOG_WARNING, "UPS has %d alarms in effect.", alarm_count); + if (alarm_rtfm) + upslogx(LOG_WARNING, "Read Huawei User Manual for " + "troubleshooting information."); + alarm_logged_since = time(NULL); + } + } + else { + upsdebugx(2, "UPS has 0 alarms in effect."); + + if (alarm_logged) { + upslogx(LOG_WARNING, "UPS has cleared all alarms."); + alarm_logged_since = time(NULL); + } + } + return 0; +} + + +void upsdrv_updateinfo(void) +{ + int err = 0; + + upsdebugx(2, "upsdrv_updateinfo"); + status_init(); + alarm_init(); + + err += ups2000_update_timers(); + err += ups2000_update_alarm(); + err += ups2000_update_info(); + err += ups2000_update_status(); + err += ups2000_update_rw_var(); + + if (err > 0) { + upsdebugx(2, "upsdrv_updateinfo failed, data stale."); + dstate_datastale(); + return; + } + + alarm_commit(); + status_commit(); + dstate_dataok(); + upsdebugx(2, "upsdrv_updateinfo done"); +} + + +/* + * A lookup table of simple RW (configurable) variable "name", and their + * "getter" and "setter". A "getter" function reads the variable from + * the UPS, and a "setter" overwrites it. + * + * This struct only handles simple variables, delays are handled in another + * table. + */ +static struct { + const char *name; + const uint16_t reg; + int (*const getter)(const uint16_t); + int (*const setter)(const uint16_t, const char *); +} ups2000_rw_var[] = +{ + { "ups.start.auto", 1044, ups2000_autostart_get, ups2000_autostart_set }, + { "ups.beeper.status", 1046, ups2000_beeper_get, ups2000_beeper_set }, + { NULL, 0, NULL, NULL }, +}; + + +/* + * A specialized lookup table of startup, reboot and shutdown delays, + * represented by RW variables. + */ +static struct ups2000_delay_t { + const char *name; /* RW variable name */ + uint16_t *const global_var; /* its corresponding global variable */ + const char *varname_cmdline; /* cmdline argument passed to us */ + const uint16_t min; /* minimum value allowed (seconds) */ + const uint16_t max; /* maximum value allowed (seconds) */ + const uint8_t step; /* can only be set in discrete steps */ + const uint16_t dfault; /* default value */ +} ups2000_rw_delay[] = +{ + /* 5940 = 99 min. */ + { "ups.delay.shutdown", &ups2000_offdelay, "offdelay", 6, 5940, 6, 60 }, + { "ups.delay.reboot", &ups2000_rebootdelay, "rebootdelay", 6, 5940, 6, 60 }, + { "ups.delay.start", &ups2000_ondelay, "ondelay", 60, 5940, 60, 60 }, + { NULL, NULL, NULL, 0, 0, 0, 0 }, +}; +enum { + SHUTDOWN, + REBOOT, + START +}; + + +static int ups2000_update_rw_var(void) +{ + int i; + int r; + + upsdebugx(2, "ups2000_update_rw_var"); + + for (i = 0; ups2000_rw_var[i].name != NULL; i++) { + r = ups2000_rw_var[i].getter(ups2000_rw_var[i].reg); + if (r != 0) + return 1; + } + + ups2000_delay_get(); + + return 0; +} + + +static int setvar(const char *name, const char *val) +{ + int i; + int r; + + for (i = 0; ups2000_rw_var[i].name != NULL; i++) { + if (!strcasecmp(ups2000_rw_var[i].name, name)) { + r = ups2000_rw_var[i].setter(ups2000_rw_var[i].reg, val); + goto found; + } + } + + for (i = 0; ups2000_rw_delay[i].name != NULL; i++) { + if (!strcasecmp(ups2000_rw_delay[i].name, name)) { + r = ups2000_rw_var[i].setter(ups2000_rw_var[i].reg, val); + goto found; + } + } + + return STAT_SET_UNKNOWN; + +found: + if (r == STAT_SET_FAILED) + upslogx(LOG_ERR, "setvar: setting variable [%s] to [%s] failed", name, val); + else if (r == STAT_SET_INVALID) + upslogx(LOG_WARNING, "setvar: [%s] is not valid for variable [%s]", val, name); + return r; +} + + +static int ups2000_autostart_get(const uint16_t reg) +{ + /* + * "ups.start.auto" is not supported because it overcomplicates + * the logic. The driver changes "ups.start.auto" internally to + * allow shutdown and reboot commands to do their jobs. If we make + * "ups.start.auto" an user configuration, it means we must (1) + * watch for UPS front panel updates and apply the user setting to + * the driver, and (2) save the restart setting temporally before + * restarting, track the UPS restart process, and program the value + * back later. + * + * Not supporting it greatly simplifies the logic - upsdrv_shutdown + * always put the UPS in a restartable mode, following the standard + * NUT behavior. Worse is better. (To prevent user confusion, we + * don't even report this variable, otherwise the user may attempt + * to change it using the front panel. + */ + NUT_UNUSED_VARIABLE(reg); + return 0; +} + + +/* + * Currently for internal use only, see comments above. + */ +static int ups2000_autostart_set(const uint16_t reg, const char *string) +{ + uint16_t val; + int r; + + if (!strcasecmp(string, "yes")) + val = 1; + else if (!strcasecmp(string, "no")) + val = 0; + else + return STAT_SET_INVALID; + + r = ups2000_write_register(modbus_ctx, reg + 10000, val); + if (r != 1) + return STAT_SET_FAILED; + + return STAT_SET_HANDLED; +} + + +static int ups2000_beeper_get(const uint16_t reg) +{ + uint16_t val; + int r; + + r = ups2000_read_registers(modbus_ctx, reg + 10000, 1, &val); + if (r != 1) + return -1; + + if (val != 0 && val != 1) + return -1; + + /* + * The register is "beeper disable", but we need to report whether it's + * enabled, thus we invert the boolean. + */ + if (val == 0) + dstate_setinfo("ups.beeper.status", "enabled"); + else + dstate_setinfo("ups.beeper.status", "disabled"); + + dstate_setflags("ups.beeper.status", ST_FLAG_RW); + dstate_addenum("ups.beeper.status", "enabled"); + dstate_addenum("ups.beeper.status", "disabled"); + + return 0; +} + + +static int ups2000_beeper_set(const uint16_t reg, const char *string) +{ + uint16_t val; + int r; + + if (!strcasecmp(string, "disabled") || !strcasecmp(string, "muted")) { + /* + * Temporary "muted" is not supported. Only permanent "disabled" + * is. This is why we only support "beeper.disable" as an instant + * command, not "beeper.muted". But when setting it as a variable, + * we try to be robust here and treat both as synonyms. + */ + val = 1; + } + else if (!strcasecmp(string, "enabled")) + val = 0; + else + return STAT_SET_INVALID; + + r = ups2000_write_register(modbus_ctx, reg + 10000, val); + if (r != 1) + return STAT_SET_FAILED; + + return STAT_SET_HANDLED; +} + + +/* + * Note: variables "ups.delay.{shutdown,start,reboot}" are software- + * only variables. We only get the user settings, validate its value + * and store them as global variables. The actual hardware register + * are only programmed when a shutdown/reboot is issued. + */ +static void ups2000_delay_get(void) +{ + char *cmdline; + int i; + int r; + + for (i = 0; ups2000_rw_delay[i].name != NULL; i++) { + struct ups2000_delay_t *delay; + + delay = &ups2000_rw_delay[i]; + if (*delay->global_var == UPS2000_DELAY_INVALID) { + cmdline = getval(delay->varname_cmdline); + if (cmdline) { + r = ups2000_delay_set(delay->name, cmdline); + if (r != STAT_SET_HANDLED) { + upslogx(LOG_ERR, "servar: %s is invalid. " + "Reverting to default %s %d seconds", + delay->varname_cmdline, + delay->varname_cmdline, + delay->dfault); + *delay->global_var = delay->dfault; + } + } + else { + *delay->global_var = delay->dfault; + upslogx(LOG_INFO, "setvar: use default %s %d seconds", + delay->varname_cmdline, delay->dfault); + } + } + + dstate_setinfo(delay->name, "%d", *delay->global_var); + dstate_setflags(delay->name, ST_FLAG_RW); + dstate_addrange(delay->name, delay->min, delay->max); + } +} + + +static int ups2000_delay_set(const char *var, const char *string) +{ + struct ups2000_delay_t *delay_schema = NULL; + uint16_t delay, delay_rounded; + int i; + int r; + + r = str_to_ushort_strict(string, &delay, 10); + if (!r) + return STAT_SET_INVALID; + + for (i = 0; ups2000_rw_delay[i].name != NULL; i++) { + if (!strcmp(ups2000_rw_delay[i].name, var)) { + delay_schema = &ups2000_rw_delay[i]; + break; + } + } + + if (!delay_schema) + return STAT_SET_UNKNOWN; + + if (delay > delay_schema->max) + return STAT_SET_INVALID; + if (delay < delay_schema->min) { + upslogx(LOG_NOTICE, "setvar: %s [%u] is too low, " + "it has been set to %u seconds\n", + delay_schema->varname_cmdline, delay, + delay_schema->min); + delay = delay_schema->min; + } + + if (delay % delay_schema->step != 0) { + delay_rounded = delay + delay_schema->step - delay % delay_schema->step; + upslogx(LOG_NOTICE, "setvar: %s [%u] is not a multiple of %d, " + "it has been rounded up to %u seconds\n", + delay_schema->varname_cmdline, delay, + delay_schema->step, delay_rounded); + delay = delay_rounded; + } + + *delay_schema->global_var = delay; + return STAT_SET_HANDLED; +} + + +/* + * A lookup table of all instant commands "cmd" and their + * corresponding registers "reg". For each instant command, + * it's handled by... + * + * 1. One register write, by writing "val1" to "reg1", the + * simplest case. + * + * 2. Two register writes, by writing "val1" to "reg1", and + * writing "val2" to "reg2". One after another. + * + * 3. Calling "*handler_func" and passing "reg1". This is + * used to handle commands that needs additional processing. + * If "reg1" is not necessary or unsuitable, "-1" is used. + */ +#define REG_NONE -1, -1 + +static struct ups2000_cmd_t { + const char *cmd; + const int16_t reg1, val1, reg2, val2; + int (*const handler_func)(const uint16_t); +} ups2000_cmd[] = +{ + { "test.battery.start.quick", 2028, 1, REG_NONE, NULL }, + { "test.battery.start.deep", 2021, 1, REG_NONE, NULL }, + { "test.battery.stop", 2023, 1, REG_NONE, NULL }, + { "beeper.enable", 1046, 0, REG_NONE, NULL }, + { "beeper.disable", 1046, 1, REG_NONE, NULL }, + { "load.off", 1045, 0, 1030, 1, NULL }, + { "bypass.stop", 1029, 1, 1045, 0, NULL }, + { "load.on", 1029, -1, REG_NONE, ups2000_instcmd_load_on }, + { "bypass.start", REG_NONE, REG_NONE, ups2000_instcmd_bypass_start }, + { "beeper.toggle", 1046, -1, REG_NONE, ups2000_instcmd_beeper_toggle }, + { "shutdown.stayoff", 1049, -1, REG_NONE, ups2000_instcmd_shutdown_stayoff }, + { "shutdown.return", REG_NONE, REG_NONE, ups2000_instcmd_shutdown_return }, + { "shutdown.reboot", REG_NONE, REG_NONE, ups2000_instcmd_shutdown_reboot }, + { "shutdown.reboot.graceful", REG_NONE, REG_NONE, ups2000_instcmd_shutdown_reboot_graceful }, + { NULL, -1, -1, -1, -1, NULL }, +}; + + +static void ups2000_init_instcmd(void) +{ + int i; + + for (i = 0; ups2000_cmd[i].cmd != NULL; i++) { + dstate_addcmd(ups2000_cmd[i].cmd); + } +} + + +static int instcmd(const char *cmd, const char *extra) +{ + int i; + int status; + struct ups2000_cmd_t *cmd_action = NULL; + NUT_UNUSED_VARIABLE(extra); + + for (i = 0; ups2000_cmd[i].cmd != NULL; i++) { + if (!strcasecmp(cmd, ups2000_cmd[i].cmd)) { + cmd_action = &ups2000_cmd[i]; + } + } + + if (!cmd_action) { + upslogx(LOG_WARNING, "instcmd: command [%s] unknown", cmd); + return STAT_INSTCMD_UNKNOWN; + } + + if (cmd_action->handler_func) { + /* handled by a function */ + if (cmd_action->reg1 < 0) { + upslogx(LOG_WARNING, "instcmd: command [%s] reg1 is negative", cmd); + return STAT_INSTCMD_UNKNOWN; + } else { + status = cmd_action->handler_func((uint16_t)cmd_action->reg1); + } + } + else if (cmd_action->reg1 >= 0 && cmd_action->val1 >= 0) { + /* handled by a register write */ + int r = ups2000_write_register(modbus_ctx, + 10000 + cmd_action->reg1, + (uint16_t)cmd_action->val1); + if (r == 1) + status = STAT_INSTCMD_HANDLED; + else + status = STAT_INSTCMD_FAILED; + + /* + * if the previous write succeeds and there is an additional + * register to write. + */ + if (r == 1 && cmd_action->reg2 >= 0 && cmd_action->val2 >= 0) { + r = ups2000_write_register(modbus_ctx, + 10000 + cmd_action->reg2, + (uint16_t)cmd_action->val2); + if (r == 1) + status = STAT_INSTCMD_HANDLED; + else + status = STAT_INSTCMD_FAILED; + } + } + else { + fatalx(EXIT_FAILURE, "invalid ups2000_cmd table!"); + } + + if (status == STAT_INSTCMD_FAILED) + upslogx(LOG_ERR, "instcmd: command [%s] failed", cmd); + else if (status == STAT_INSTCMD_HANDLED) + upslogx(LOG_INFO, "instcmd: command [%s] handled", cmd); + return status; +} + + +static int ups2000_instcmd_load_on(const uint16_t reg) +{ + int r; + const char *status; + + /* force refresh UPS status */ + status_init(); + r = ups2000_update_status(); + if (r != 0) { + /* + * When the UPS status is updated, the code must set either OL, OB, OL ECO, + * BYPASS, or OFF. These five options are mutually exclusive. If the register + * value is invalid and set none of these flags, failure code 1 is returned. + */ + dstate_datastale(); + return STAT_INSTCMD_FAILED; + } + status_commit(); + + status = dstate_getinfo("ups.status"); + if (strstr(status, "OFF")) { + /* no warning needed, continue at ups2000_write_register() below */ + } + else if (strstr(status, "OL") || strstr(status, "OB")) { + /* + * "Turning it on" has no effect if it's already on. Log a warning + * while still accepting and executing the command. + */ + upslogx(LOG_WARNING, "load.on: UPS is already on."); + upslogx(LOG_WARNING, "load.on: still executing command anyway."); + } + else if (strstr(status, "BYPASS")) { + /* + * If it's in bypass mode, reject this command. The UPS would otherwise + * enter normal mode, but "load.on" is not supposed to affect the + * normal/bypass status. Also log an error and suggest "bypass.stop". + */ + upslogx(LOG_ERR, "load.on error: UPS is already on, and is in bypass mode. " + "To enter normal mode, use bypass.stop"); + return STAT_INSTCMD_FAILED; + } + else { + /* unreachable, see comments for r != 0 at the beginning */ + upslogx(LOG_ERR, "load.on error: invalid ups.status (%s) detected. " + "Please file a bug report!", status); + return STAT_INSTCMD_FAILED; + } + + r = ups2000_write_register(modbus_ctx, 10000 + reg, 1); + if (r != 1) + return STAT_INSTCMD_FAILED; + return STAT_INSTCMD_HANDLED; +} + + +static int ups2000_instcmd_bypass_start(const uint16_t reg) +{ + int r; + NUT_UNUSED_VARIABLE(reg); + + /* force update alarms */ + alarm_init(); + r = ups2000_update_alarm(); + if (r != 0) + return STAT_INSTCMD_FAILED; + alarm_commit(); + + /* bypass input has a power failure, refuse to bypass */ + if (!bypass_available) { + upslogx(LOG_ERR, "bypass input is abnormal, refuse to enter bypass mode."); + return STAT_INSTCMD_FAILED; + } + + /* enable "bypass on shutdown" */ + r = ups2000_write_register(modbus_ctx, 10000 + 1045, 1); + if (r != 1) + return STAT_INSTCMD_FAILED; + + /* shutdown */ + r = ups2000_write_register(modbus_ctx, 10000 + 1030, 1); + if (r != 1) + return STAT_INSTCMD_FAILED; + + return STAT_INSTCMD_HANDLED; +} + + +static int ups2000_instcmd_beeper_toggle(const uint16_t reg) +{ + int r; + const char *string; + + r = ups2000_beeper_get(reg); + if (r != 0) + return STAT_INSTCMD_FAILED; + + string = dstate_getinfo("ups.beeper.status"); + if (!strcasecmp(string, "enabled")) + r = ups2000_beeper_set(reg, "disabled"); + else if (!strcasecmp(string, "disabled")) + r = ups2000_beeper_set(reg, "enabled"); + else + return STAT_INSTCMD_FAILED; + + if (r != STAT_SET_HANDLED) + return STAT_INSTCMD_FAILED; + + return STAT_INSTCMD_HANDLED; +} + + +/* + * "ups.shutdown.stayoff": wait an optional offdelay and shutdown. + * When the grid power returns, stay off. + */ +static int ups2000_instcmd_shutdown_stayoff(const uint16_t reg) +{ + uint16_t val; + int r; + + r = setvar("ups.start.auto", "no"); + if (r != STAT_SET_HANDLED) + return STAT_INSTCMD_FAILED; + + val = ups2000_offdelay * 10; /* scaling factor */ + val /= 60; /* convert to minutes */ + + r = ups2000_write_register(modbus_ctx, 10000 + reg, val); + if (r != 1) + return STAT_INSTCMD_FAILED; + + return STAT_INSTCMD_HANDLED; +} + + +/* + * Wait for "offdelay" second, turn off the load. Then, wait + * for "ondelay" seconds. If the grid power still exists or + * has returned after the timer, turn on the load. Otherwise, + * shutdown the UPS. When combined with "ups.start.auto", it + * guarantees the server can always be restarted even if there + * is a power race. + * + * "shutdown.return", "shutdown.reboot" and "shutdown.reboot. + * graceful" all rely on this function. + */ +static int ups2000_shutdown_guaranteed_return(uint16_t offdelay, uint16_t ondelay) +{ + int r; + uint16_t val[2]; + + r = setvar("ups.start.auto", "yes"); + if (r != STAT_SET_HANDLED) + return STAT_INSTCMD_FAILED; + + val[0] = (offdelay * 10) / 60; + val[1] = ondelay / 60; + + r = ups2000_write_registers(modbus_ctx, 1047 + 10000, 2, val); + if (r != 2) + return STAT_INSTCMD_FAILED; + + return STAT_INSTCMD_HANDLED; +} + + +/* + * "ups.shutdown.return": wait an optional "offdelay" and shutdown. + * When the grid power returns, power on the load. + */ +static int ups2000_instcmd_shutdown_return(const uint16_t reg) +{ + int r; + NUT_UNUSED_VARIABLE(reg); + + r = ups2000_shutdown_guaranteed_return(ups2000_offdelay, + ups2000_ondelay); + if (r == STAT_INSTCMD_HANDLED) { + shutdown_at = time_seek(time(NULL), ups2000_offdelay); + } + return r; +} + + +/* + * "ups.shutdown.reboot": shutdown as soon as possible using the + * smallest "rebootdelay" (inside the UPS, it's the same "ondelay" + * timer), restart after an "ondelay". + * + * In our implementation, it's like "ups.shutdown.return", just + * with a minimal "ondelay". + */ +static int ups2000_instcmd_shutdown_reboot(const uint16_t reg) +{ + int r; + NUT_UNUSED_VARIABLE(reg); + + r = ups2000_shutdown_guaranteed_return(ups2000_rw_delay[REBOOT].min, + ups2000_ondelay); + if (r == STAT_INSTCMD_HANDLED) { + reboot_at = time_seek(time(NULL), ups2000_rw_delay[REBOOT].min); + start_at = time_seek(reboot_at, ups2000_ondelay); + } + return r; +} + + +/* + * "ups.shutdown.reboot.graceful": shutdown after a "rebootdelay" + * (inside the UPS, it's the same "ondelay" timer), restart after + * an "ondelay". + * + * In our implementation, it's like "ups.shutdown.return", just + * with a "rebootdelay" instead of an "ondelay". + */ +static int ups2000_instcmd_shutdown_reboot_graceful(const uint16_t reg) +{ + int r; + NUT_UNUSED_VARIABLE(reg); + + r = ups2000_shutdown_guaranteed_return(ups2000_rebootdelay, + ups2000_ondelay); + if (r == STAT_INSTCMD_HANDLED) { + reboot_at = time_seek(time(NULL), ups2000_rebootdelay); + start_at = time_seek(reboot_at, ups2000_ondelay); + } + return r; +} + + +/* + * List of countdown timers and pointers to their corresponding + * global variables "at_time". They record estimated timestamps + * when the actions are supposed to be performed. + */ +static struct { + const char *name; + time_t *const at_time; +} ups2000_timers[] = { + { "ups.timer.reboot", &reboot_at }, + { "ups.timer.shutdown", &shutdown_at }, + { "ups.timer.start", &start_at }, + { NULL, NULL }, +}; + + +static int ups2000_update_timers(void) +{ + time_t now; + int eta; + int i; + + now = time(NULL); + + for (i = 0; ups2000_timers[i].name != NULL; i++) { + if (*ups2000_timers[i].at_time) { + eta = difftime(*ups2000_timers[i].at_time, now); + if (eta < 0) + eta = 0; + dstate_setinfo(ups2000_timers[i].name, "%d", eta); + } + else { + dstate_setinfo(ups2000_timers[i].name, "%d", -1); + } + } + return 0; +} + + +void upsdrv_shutdown(void) +{ + int r; + + r = instcmd("shutdown.reboot", ""); + if (r != STAT_INSTCMD_HANDLED) + fatalx(EXIT_FAILURE, "upsdrv_shutdown failed!"); +} + + +void upsdrv_help(void) +{ +} + + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + char msg[64]; + + snprintf(msg, 64, "Set shutdown delay, in seconds, 6-second step" + " (default=%d)", ups2000_rw_delay[SHUTDOWN].dfault); + addvar(VAR_VALUE, "offdelay", msg); + + snprintf(msg, 64, "Set reboot delay, in seconds, 6-second step" + " (default=%d).", ups2000_rw_delay[REBOOT].dfault); + addvar(VAR_VALUE, "rebootdelay", msg); + + snprintf(msg, 64, "Set start delay, in seconds, 60-second step" + " (default=%d).", ups2000_rw_delay[START].dfault); + addvar(VAR_VALUE, "ondelay", msg); +} + + +void upsdrv_cleanup(void) +{ + if (modbus_ctx != NULL) { + modbus_close(modbus_ctx); + modbus_free(modbus_ctx); + } + ser_close(upsfd, device_path); +} + + +/* + * Seek time "t" forward or backward by n "seconds" without assuming + * the underlying type and format of "time_t". This ensures maximum + * portability. Although on POSIX and many other systems, "time_t" + * is guaranteed to be in seconds. + * + * On error, abort the program. + */ +static time_t time_seek(time_t t, int seconds) +{ + struct tm time_tm; + time_t time_output; + + if (!t) + fatalx(EXIT_FAILURE, "time_seek() failed!"); + + if (!gmtime_r(&t, &time_tm)) + fatalx(EXIT_FAILURE, "time_seek() failed!"); + + time_tm.tm_sec += seconds; + time_output = mktime(&time_tm); + + if (time_output == (time_t) -1) + fatalx(EXIT_FAILURE, "time_seek() failed!"); + + return time_output; +} + + +/* + * Read bytes from the UPS2000 serial port, until the buffer has + * nothing left to read (after ser_get_buf() times out). The buffer + * size is limited to buf_len (inclusive). + * + * On error, return 0. + * + * In the serial library, ser_get_buf() can be a short read, and + * ser_get_buf_let() requires a precalculated length, necessiates + * our own read function. + */ +static size_t ups2000_read_serial(uint8_t *buf, size_t buf_len) +{ + ssize_t bytes = 0; + size_t total = 0; + + /* wait 400 ms for the device to process our command */ + usleep(400 * 1000); + + while (buf_len > 0) { + bytes = ser_get_buf(upsfd, buf, buf_len, 1, 0); + if (bytes < 0) + return 0; /* read failure */ + else if (bytes == 0) + return total; /* nothing to read */ + + total += (size_t)bytes; /* increment byte counter */ + buf += bytes; /* advance buffer position */ + if ((size_t)bytes > buf_len) { + fatalx(EXIT_FAILURE, "ups2000_read_serial() read too much!"); + } + buf_len -= (size_t)bytes; /* decrement limiter */ + } + return 0; /* buffer exhaustion */ +} + + +/* + * Retry control. By default, we are in RETRY_ENABLE mode. For each + * register read, we retry three times (1 sec. between each attempt), + * before raising a fatal error and giving up. So far so good, but, + * if the link went down, all operation would fail and the program + * would become unresponsive due to excessive retrys. + * + * To prevent this problem, after the first fatal error, we stop all + * retry attempts by entering RETRY_DISABLE_TEMPORARY mode, allowing + * subsequent operation to fail without retry. Later, after the first + * success is encountered, we move back to RETRY_ENABLE mode. + */ +enum { + RETRY_ENABLE, + RETRY_DISABLE_TEMPORARY +}; +static int retry_status = RETRY_ENABLE; + + +/* + * Read one or more registers using libmodbus. + * + * This is simply a wrapper for libmodbus's modbus_read_registers() with + * retry and workaround logic. When an error has occured, we retry 3 times + * before giving up, allowing us to recover from non-fatal failures without + * triggering a data stale. + */ +static int ups2000_read_registers(modbus_t *ctx, int addr, int nb, uint16_t *dest) +{ + int i; + int r = -1; + + if (addr < 10000) + upslogx(LOG_ERR, "Invalid register read from %04d detected. " + "Please file a bug report!", addr); + + for (i = 0; i < 3; i++) { + r = modbus_read_registers(ctx, addr, nb, dest); + + /* generic retry for modbus read failures. */ + if (retry_status == RETRY_ENABLE && r != nb) { + upslogx(LOG_WARNING, "Register %04d has a read failure. Retrying...", addr); + sleep(1); + continue; + } + else if (r == nb) + retry_status = RETRY_ENABLE; + + /* + * Workaround for buggy register 2002 (battery status). Sometimes + * this register returns invalid values. This is a known problem + * and it's not fatal, so we use LOG_INFO. + */ + if (retry_status == RETRY_ENABLE && + addr == 12002 && (dest[0] < 2 || dest[0] > 5)) { + upslogx(LOG_INFO, "Battery status has a non-fatal read failure, it's usually harmless. Retrying... "); + sleep(1); + continue; + } + else if (addr == 12002 && dest[0] >= 2 && dest[0] <= 5) + retry_status = RETRY_ENABLE; + + return r; + } + + /* Give up */ + upslogx(LOG_WARNING, "Register %04d has a fatal read failure.", addr); + retry_status = RETRY_DISABLE_TEMPORARY; + return r; +} + + +static int ups2000_write_registers(modbus_t *ctx, int addr, int nb, uint16_t *src) +{ + int i; + int r = -1; + + if (addr < 10000) + upslogx(LOG_ERR, "Invalid register write to %04d detected. " + "Please file a bug report!", addr); + + for (i = 0; i < 3; i++) { + r = modbus_write_registers(ctx, addr, nb, src); + + /* generic retry for modbus write failures. */ + if (retry_status == RETRY_ENABLE && r != nb) { + upslogx(LOG_WARNING, "Register %04d has a write failure. Retrying...", addr); + sleep(1); + continue; + } + else if (r == nb) + retry_status = RETRY_ENABLE; + + return r; + } + + /* Give up */ + upslogx(LOG_WARNING, "Register %04d has a fatal write failure.", addr); + retry_status = RETRY_DISABLE_TEMPORARY; + return r; +} + + +static int ups2000_write_register(modbus_t *ctx, int addr, uint16_t val) +{ + return ups2000_write_registers(ctx, addr, 1, &val); +} + + +/* + * The following CRC-16 code was copied from libmodbus. + * + * Copyright (C) 2001-2011 Stéphane Raimbault + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +/* Table of CRC values for high-order byte */ +static const uint8_t table_crc_hi[] = { + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, + 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, + 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 +}; + +/* Table of CRC values for low-order byte */ +static const uint8_t table_crc_lo[] = { + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, + 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, + 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, + 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, + 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, + 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, + 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, + 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, + 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, + 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, + 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, + 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, + 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, + 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, + 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, + 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, + 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, + 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, + 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, + 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, + 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, + 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, + 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, + 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, + 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, + 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 +}; + + +static uint16_t crc16(uint8_t * buffer, uint16_t buffer_length) +{ + uint8_t crc_hi = 0xFF; /* high CRC byte initialized */ + uint8_t crc_lo = 0xFF; /* low CRC byte initialized */ + unsigned int i; /* will index into CRC lookup */ + + /* pass through message buffer */ + while (buffer_length--) { + i = crc_hi ^ *buffer++; /* calculate the CRC */ + crc_hi = crc_lo ^ table_crc_hi[i]; + crc_lo = table_crc_lo[i]; + } + + return ((uint16_t)((uint16_t)(crc_hi) << 8) | (uint16_t)crc_lo); +} diff --git a/drivers/idowell-hid.c b/drivers/idowell-hid.c index 0d77fc5..f96958f 100644 --- a/drivers/idowell-hid.c +++ b/drivers/idowell-hid.c @@ -6,7 +6,7 @@ * 2008 - 2009 Arjen de Korte * * Note: this subdriver was initially generated as a "stub" by the - * path-to-subdriver script. It must be customized. + * gen-usbhid-subdriver script. It must be customized. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,12 +23,14 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" /* must be first */ + #include "usbhid-ups.h" #include "idowell-hid.h" #include "main.h" /* for getval() */ #include "usb-common.h" -#define IDOWELL_HID_VERSION "iDowell HID 0.1" +#define IDOWELL_HID_VERSION "iDowell HID 0.2" /* FIXME: experimental flag to be put in upsdrv_info */ /* iDowell */ @@ -40,7 +42,7 @@ static usb_device_id_t idowell_usb_device_table[] = { { USB_DEVICE(IDOWELL_VENDORID, 0x0300), NULL }, /* Terminating entry */ - { -1, -1, NULL } + { 0, 0, NULL } }; /* --------------------------------------------------------------- */ @@ -102,7 +104,7 @@ static hid_info_t idowell_hid2nut[] = { { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", NULL, "%.0f", 0, NULL }, { "ups.power.nominal", 0, 0, "UPS.Flow.[4].ConfigApparentPower", NULL, "%.0f", HU_FLAG_STATIC, NULL }, - + /* input page */ { "input.transfer.high", 0, 0, "UPS.PowerConverter.Output.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "input.transfer.low", 0, 0, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, NULL }, @@ -137,7 +139,7 @@ static const char *idowell_format_serial(HIDDevice_t *hd) { * the device is supported by this subdriver, else 0. */ static int idowell_claim(HIDDevice_t *hd) { - int status = is_usb_device_supported(idowell_usb_device_table, hd->VendorID, hd->ProductID); + int status = is_usb_device_supported(idowell_usb_device_table, hd); switch (status) { @@ -166,4 +168,5 @@ subdriver_t idowell_subdriver = { idowell_format_model, idowell_format_mfr, idowell_format_serial, + fix_report_desc, }; diff --git a/drivers/ietf-mib.c b/drivers/ietf-mib.c index 0cc3192..dd69a1e 100644 --- a/drivers/ietf-mib.c +++ b/drivers/ietf-mib.c @@ -1,9 +1,9 @@ /* ietf-mib.c - data to monitor SNMP UPS (RFC 1628 compliant) with NUT * * Copyright (C) 2002-2006 - * Arnaud Quette - * Niels Baggesen - * Arjen de Korte + * 2002-2012 Arnaud Quette + * 2002-2006 Niels Baggesen + * 2002-2006 Arjen de Korte * * Sponsored by MGE UPS SYSTEMS * @@ -26,80 +26,97 @@ #include "ietf-mib.h" -#define IETF_MIB_VERSION "1.3" +#define IETF_MIB_VERSION "1.54" /* SNMP OIDs set */ #define IETF_OID_UPS_MIB "1.3.6.1.2.1.33.1." #define IETF_SYSOID ".1.3.6.1.2.1.33" +/* NOTE: Currently the Tripplite UPSes await user-validation of their + * real SNMP OID tree, so temporarily the IETF tree is used as "tripplite" + * for the mapping purposes; the devices ave their entry point OID though. + * For more details see: + * https://github.com/networkupstools/nut/issues/309 + * https://github.com/networkupstools/nut/issues/171 + * Also related to: + * https://github.com/networkupstools/nut/issues/270 + */ +#define TRIPPLITE_SYSOID ".1.3.6.1.4.1.850.1" + /* #define DEBUG */ static info_lkp_t ietf_battery_info[] = { - { 1, "" /* unknown */ }, - { 2, "" /* batteryNormal */}, - { 3, "LB" /* batteryLow */ }, - { 4, "LB" /* batteryDepleted */ }, - { 0, "NULL" } + { 1, "" /* unknown */, NULL, NULL }, + { 2, "" /* batteryNormal */, NULL, NULL }, + { 3, "LB" /* batteryLow */, NULL, NULL }, + { 4, "LB" /* batteryDepleted */, NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t ietf_power_source_info[] = { - { 1, "" /* other */ }, - { 2, "OFF" /* none */ }, - { 3, "OL" /* normal */ }, - { 4, "OL BYPASS" /* bypass */ }, - { 5, "OB" /* battery */ }, - { 6, "OL BOOST" /* booster */ }, - { 7, "OL TRIM" /* reducer */ }, - { 0, "NULL" } + { 1, "" /* other */, NULL, NULL }, + { 2, "OFF" /* none */, NULL, NULL }, + { 3, "OL" /* normal */, NULL, NULL }, + { 4, "OL BYPASS" /* bypass */, NULL, NULL }, + { 5, "OB" /* battery */, NULL, NULL }, + { 6, "OL BOOST" /* booster */, NULL, NULL }, + { 7, "OL TRIM" /* reducer */, NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t ietf_overload_info[] = { - { 1, "OVER" }, /* output overload */ - { 0, "NULL" } + { 1, "OVER", NULL, NULL }, /* output overload */ + { 0, NULL, NULL, NULL } }; static info_lkp_t ietf_test_active_info[] = { - { 1, "" }, /* upsTestNoTestsInitiated */ - { 2, "" }, /* upsTestAbortTestInProgress */ - { 3, "TEST" }, /* upsTestGeneralSystemsTest */ - { 4, "TEST" }, /* upsTestQuickBatteryTest */ - { 5, "CAL" }, /* upsTestDeepBatteryCalibration */ - { 0, "NULL" } + { 1, "", NULL, NULL }, /* upsTestNoTestsInitiated */ + { 2, "", NULL, NULL }, /* upsTestAbortTestInProgress */ + { 3, "TEST", NULL, NULL }, /* upsTestGeneralSystemsTest */ + { 4, "TEST", NULL, NULL }, /* upsTestQuickBatteryTest */ + { 5, "CAL", NULL, NULL }, /* upsTestDeepBatteryCalibration */ + { 0, NULL, NULL, NULL } }; static info_lkp_t ietf_test_result_info[] = { - { 1, "done and passed" }, - { 2, "done and warning" }, - { 3, "done and error" }, - { 4, "aborted" }, - { 5, "in progress" }, - { 6, "no test initiated" }, - { 0, "NULL" } + { 1, "done and passed", NULL, NULL }, + { 2, "done and warning", NULL, NULL }, + { 3, "done and error", NULL, NULL }, + { 4, "aborted", NULL, NULL }, + { 5, "in progress", NULL, NULL }, + { 6, "no test initiated", NULL, NULL }, + { 0, NULL, NULL, NULL } }; #ifdef DEBUG static info_lkp_t ietf_shutdown_type_info[] = { - { 1, "output" }, - { 2, "system" }, - { 0, "NULL" } + { 1, "output", NULL, NULL }, + { 2, "system", NULL, NULL }, + { 0, NULL, NULL, NULL } }; #endif static info_lkp_t ietf_yes_no_info[] = { - { 1, "yes" }, - { 2, "no" }, - { 0, "NULL" } + { 1, "yes", NULL, NULL }, + { 2, "no", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t ietf_beeper_status_info[] = { - { 1, "disabled" }, - { 2, "enabled" }, - { 3, "muted" }, - { 0, "NULL" } + { 1, "disabled", NULL, NULL }, + { 2, "enabled", NULL, NULL }, + { 3, "muted", NULL, NULL }, + { 0, NULL, NULL, NULL } }; /* Snmp2NUT lookup table info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar */ static snmp_info_t ietf_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* The Device Identification group */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.1.0", "Generic", SU_FLAG_STATIC, NULL }, /* upsIdentManufacturer */ { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "1.2.0", "Generic SNMP UPS", SU_FLAG_STATIC, NULL }, /* upsIdentModel */ @@ -124,7 +141,7 @@ static snmp_info_t ietf_mib[] = { #ifdef DEBUG { "debug.upsInputLineBads", 0, 1.0, IETF_OID_UPS_MIB "3.1.0", "", 0, NULL }, /* upsInputLineBads */ #endif - { "input.phases", 0, 1.0, IETF_OID_UPS_MIB "3.2.0", "", SU_FLAG_SETINT, NULL, &input_phases }, /* upsInputNumLines */ + { "input.phases", 0, 1.0, IETF_OID_UPS_MIB "3.2.0", "", 0, NULL }, /* upsInputNumLines */ #ifdef DEBUG { "debug.upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.1", "", SU_INPUT_1, NULL }, /* upsInputLineIndex */ { "debug.[1].upsInputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "3.3.1.1.1", "", SU_INPUT_3, NULL }, @@ -151,7 +168,7 @@ static snmp_info_t ietf_mib[] = { /* Output Group */ { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "4.1.0", "", SU_STATUS_PWR, ietf_power_source_info }, /* upsOutputSource */ { "output.frequency", 0, 0.1, IETF_OID_UPS_MIB "4.2.0", "", 0, NULL }, /* upsOutputFrequency */ - { "output.phases", 0, 1.0, IETF_OID_UPS_MIB "4.3.0", "", SU_FLAG_SETINT, NULL, &output_phases }, /* upsOutputNumLines */ + { "output.phases", 0, 1.0, IETF_OID_UPS_MIB "4.3.0", "", 0, NULL }, /* upsOutputNumLines */ #ifdef DEBUG { "debug.upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.1", "", SU_OUTPUT_1, NULL }, /* upsOutputLineIndex */ { "debug.[1].upsOutputLineIndex", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.1.1", "", SU_OUTPUT_3, NULL }, @@ -176,7 +193,7 @@ static snmp_info_t ietf_mib[] = { { "output.L3.power.percent", 0, 1.0, IETF_OID_UPS_MIB "4.4.1.5.3", "", SU_OUTPUT_3, NULL }, /* Bypass Group */ - { "input.bypass.phases", 0, 1.0, IETF_OID_UPS_MIB "5.2.0", "", SU_FLAG_SETINT, NULL, &bypass_phases }, /* upsBypassNumLines */ + { "input.bypass.phases", 0, 1.0, IETF_OID_UPS_MIB "5.2.0", "", 0, NULL }, /* upsBypassNumLines */ { "input.bypass.frequency", 0, 0.1, IETF_OID_UPS_MIB "5.1.0", "", SU_BYPASS_1 | SU_BYPASS_3, NULL }, /* upsBypassFrequency */ #ifdef DEBUG { "debug.upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.1", "", SU_BYPASS_1, NULL }, /* upsBypassLineIndex */ @@ -184,18 +201,18 @@ static snmp_info_t ietf_mib[] = { { "debug.[2].upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.2", "", SU_BYPASS_3, NULL }, { "debug.[3].upsBypassLineIndex", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.1.3", "", SU_BYPASS_3, NULL }, #endif - { "input.bypass.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.3.1", "", SU_BYPASS_1, NULL }, /* upsBypassVoltage */ - { "input.bypass.L1-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.3.1", "", SU_BYPASS_3, NULL }, - { "input.bypass.L2-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.3.2", "", SU_BYPASS_3, NULL }, - { "input.bypass.L3-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.3.3", "", SU_BYPASS_3, NULL }, - { "input.bypass.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.4.1", "", SU_BYPASS_1, NULL }, /* upsBypassCurrent */ - { "input.bypass.L1.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.4.1", "", SU_BYPASS_3, NULL }, - { "input.bypass.L2.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.4.2", "", SU_BYPASS_3, NULL }, - { "input.bypass.L3.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.4.3", "", SU_BYPASS_3, NULL }, - { "input.bypass.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.5.1", "", SU_BYPASS_1, NULL }, /* upsBypassPower */ - { "input.bypass.L1.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.5.1", "", SU_BYPASS_3, NULL }, - { "input.bypass.L2.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.5.2", "", SU_BYPASS_3, NULL }, - { "input.bypass.L3.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.5.3", "", SU_BYPASS_3, NULL }, + { "input.bypass.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.1", "", SU_BYPASS_1, NULL }, /* upsBypassVoltage */ + { "input.bypass.L1-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.1", "", SU_BYPASS_3, NULL }, + { "input.bypass.L2-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.2", "", SU_BYPASS_3, NULL }, + { "input.bypass.L3-N.voltage", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.2.3", "", SU_BYPASS_3, NULL }, + { "input.bypass.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.1", "", SU_BYPASS_1, NULL }, /* upsBypassCurrent */ + { "input.bypass.L1.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.1", "", SU_BYPASS_3, NULL }, + { "input.bypass.L2.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.2", "", SU_BYPASS_3, NULL }, + { "input.bypass.L3.current", 0, 0.1, IETF_OID_UPS_MIB "5.3.1.3.3", "", SU_BYPASS_3, NULL }, + { "input.bypass.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.1", "", SU_BYPASS_1, NULL }, /* upsBypassPower */ + { "input.bypass.L1.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.1", "", SU_BYPASS_3, NULL }, + { "input.bypass.L2.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.2", "", SU_BYPASS_3, NULL }, + { "input.bypass.L3.realpower", 0, 1.0, IETF_OID_UPS_MIB "5.3.1.4.3", "", SU_BYPASS_3, NULL }, /* Alarm Group */ #ifdef DEBUG @@ -230,10 +247,10 @@ static snmp_info_t ietf_mib[] = { /* Test Group */ { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "7.1.0", "", 0, ietf_test_active_info }, /* upsTestId */ - { "test.battery.stop", 0, 0, IETF_OID_UPS_MIB "7.1.0", IETF_OID_UPS_MIB "7.7.2", SU_TYPE_CMD, NULL }, /* upsTestAbortTestInProgress */ - { "test.battery.start", 0, 0, IETF_OID_UPS_MIB "7.1.0", IETF_OID_UPS_MIB "7.7.3", SU_TYPE_CMD, NULL }, /* upsTestGeneralSystemsTest */ - { "test.battery.start.quick", 0, 0, IETF_OID_UPS_MIB "7.1.0", IETF_OID_UPS_MIB "7.7.4", SU_TYPE_CMD, NULL }, /* upsTestQuickBatteryTest */ - { "test.battery.start.deep", 0, 0, IETF_OID_UPS_MIB "7.1.0", IETF_OID_UPS_MIB "7.7.5", SU_TYPE_CMD, NULL }, /* upsTestDeepBatteryCalibration */ + { "test.battery.stop", 0, 1, IETF_OID_UPS_MIB "7.1.0", "0", SU_TYPE_CMD, NULL }, /* upsTestAbortTestInProgress */ + { "test.battery.start", 0, 1, IETF_OID_UPS_MIB "7.1.0", "0", SU_TYPE_CMD, NULL }, /* upsTestGeneralSystemsTest */ + { "test.battery.start.quick", 0, 1, IETF_OID_UPS_MIB "7.1.0", "0", SU_TYPE_CMD, NULL }, /* upsTestQuickBatteryTest */ + { "test.battery.start.deep", 0, 1, IETF_OID_UPS_MIB "7.1.0", "0", SU_TYPE_CMD, NULL }, /* upsTestDeepBatteryCalibration */ #ifdef DEBUG { "debug.upsTestSpinLock", 0, 1.0, IETF_OID_UPS_MIB "7.2.0", "", 0, NULL }, /* upsTestSpinLock */ #endif @@ -249,9 +266,9 @@ static snmp_info_t ietf_mib[] = { { "debug.upsShutdownType", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "8.1.0", "", 0, ietf_shutdown_type_info }, /* upsShutdownType */ #endif { "ups.timer.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 8, IETF_OID_UPS_MIB "8.2.0", "", 0, NULL }, /* upsShutdownAfterDelay*/ - { "load.off", 0, 0, IETF_OID_UPS_MIB "8.2.0", "", SU_TYPE_CMD, NULL }, + { "load.off", 0, 1, IETF_OID_UPS_MIB "8.2.0", "0", SU_TYPE_CMD, NULL }, { "ups.timer.start", ST_FLAG_STRING | ST_FLAG_RW, 8, IETF_OID_UPS_MIB "8.3.0", "", 0, NULL }, /* upsStartupAfterDelay */ - { "load.on", 0, 0, IETF_OID_UPS_MIB "8.3.0", "", SU_TYPE_CMD, NULL }, + { "load.on", 0, 1, IETF_OID_UPS_MIB "8.3.0", "0", SU_TYPE_CMD, NULL }, { "ups.timer.reboot", ST_FLAG_STRING | ST_FLAG_RW, 8, IETF_OID_UPS_MIB "8.4.0", "", 0, NULL }, /* upsRebootWithDuration */ { "ups.start.auto", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "8.5.0", "", 0, ietf_yes_no_info }, /* upsAutoRestart */ @@ -263,10 +280,10 @@ static snmp_info_t ietf_mib[] = { { "output.power.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.5.0", "", 0, NULL }, /* upsConfigOutputVA */ { "output.realpower.nominal", 0, 1.0, IETF_OID_UPS_MIB "9.6.0", "", 0, NULL }, /* upsConfigOutputPower */ { "battery.runtime.low", 0, 60.0, IETF_OID_UPS_MIB "9.7.0", "", 0, NULL }, /* upsConfigLowBattTime */ - { "beeper.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "9.8.0", "", 0, ietf_beeper_status_info }, /* upsConfigAudibleStatus */ - { "beeper.disable", 0, 1, IETF_OID_UPS_MIB "9.8.0", "", SU_TYPE_CMD, NULL }, - { "beeper.enable", 0, 2, IETF_OID_UPS_MIB "9.8.0", "", SU_TYPE_CMD, NULL }, - { "beeper.mute", 0, 3, IETF_OID_UPS_MIB "9.8.0", "", SU_TYPE_CMD, NULL }, + { "ups.beeper.status", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_UPS_MIB "9.8.0", "", 0, ietf_beeper_status_info }, /* upsConfigAudibleStatus */ + { "beeper.disable", 0, 1, IETF_OID_UPS_MIB "9.8.0", "1", SU_TYPE_CMD, NULL }, + { "beeper.enable", 0, 1, IETF_OID_UPS_MIB "9.8.0", "2", SU_TYPE_CMD, NULL }, + { "beeper.mute", 0, 1, IETF_OID_UPS_MIB "9.8.0", "3", SU_TYPE_CMD, NULL }, { "input.transfer.low", 0, 1.0, IETF_OID_UPS_MIB "9.9.0", "", 0, NULL }, /* upsConfigLowVoltageTransferPoint */ { "input.transfer.high", 0, 1.0, IETF_OID_UPS_MIB "9.10.0", "", 0, NULL }, /* upsConfigHighVoltageTransferPoint */ @@ -274,4 +291,12 @@ static snmp_info_t ietf_mib[] = { { NULL, 0, 0, NULL, NULL, 0, NULL } }; -mib2nut_info_t ietf = { "ietf", IETF_MIB_VERSION, IETF_OID_UPS_MIB "4.1.0", IETF_OID_UPS_MIB "1.1.0", ietf_mib, IETF_SYSOID }; +/* FIXME: Rename the structure here (or even relocate to new file) + * and in snmp-ups.c when the real TrippLite mappings get defined. */ +/* FIXME: Duplicate the line below to fix an issue with the code generator (nut-snmpinfo.py -> line is discarding) */ +/*mib2nut_info_t tripplite_ietf = { "tripplite", IETF_MIB_VERSION, NULL, NULL, ietf_mib, TRIPPLITE_SYSOID, NULL };*/ +mib2nut_info_t tripplite_ietf = { "tripplite", IETF_MIB_VERSION, NULL, NULL, ietf_mib, TRIPPLITE_SYSOID, NULL }; + +/* FIXME: Duplicate the line below to fix an issue with the code generator (nut-snmpinfo.py -> line is discarding) */ +/*mib2nut_info_t ietf = { "ietf", IETF_MIB_VERSION, IETF_OID_UPS_MIB "4.1.0", IETF_OID_UPS_MIB "1.1.0", ietf_mib, IETF_SYSOID, NULL };*/ +mib2nut_info_t ietf = { "ietf", IETF_MIB_VERSION, IETF_OID_UPS_MIB "4.1.0", IETF_OID_UPS_MIB "1.1.0", ietf_mib, IETF_SYSOID, NULL }; diff --git a/drivers/ietf-mib.h b/drivers/ietf-mib.h index d1c32ac..b9ddedc 100644 --- a/drivers/ietf-mib.h +++ b/drivers/ietf-mib.h @@ -5,5 +5,6 @@ #include "snmp-ups.h" extern mib2nut_info_t ietf; +extern mib2nut_info_t tripplite_ietf; #endif /* IETF_MIB_H */ diff --git a/drivers/isbmex.c b/drivers/isbmex.c index 17328b2..8762562 100644 --- a/drivers/isbmex.c +++ b/drivers/isbmex.c @@ -27,7 +27,7 @@ #include #define DRIVER_NAME "ISBMEX UPS driver" -#define DRIVER_VERSION "0.06" +#define DRIVER_VERSION "0.07" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -40,6 +40,7 @@ upsdrv_info_t upsdrv_info = { { NULL } }; +/* FIXME: Replace with proper upsdebugx() et al */ #define xDEBUG #ifdef DEBUG @@ -52,7 +53,7 @@ upsdrv_info_t upsdrv_info = { #define MAXTRIES 15 /* #define IGNCHARS "" */ -float lagrange(unsigned int vbyte) +static float lagrange(unsigned int vbyte) { float f0, f1, f2, f3, f4, f5, f6; float a, b, c, d, e, g, h; @@ -100,7 +101,7 @@ float lagrange(unsigned int vbyte) return a + b + c + d + e + g + h; } -float interpol(float vbytes) +static float interpol(float vbytes) { const int x[7]={75,83,87,98,103,118,145}; const float f[7]={96.0,102.0,105.0,113.0,116.0,124.0,140.0}; @@ -141,173 +142,187 @@ void upsdrv_initinfo(void) dstate_setinfo("input.transfer.high", "140.0"); /* defined */ dstate_setinfo("output.voltage", "120.0"); /* defined */ - - /* addinfo(INFO_, "", 0, 0); */ - /*printf("Using %s %s on %s\n", getdata(INFO_MFR), getdata(INFO_MODEL), device_path);*/ + + /* addinfo(INFO_, "", 0, 0); */ + /*printf("Using %s %s on %s\n", getdata(INFO_MFR), getdata(INFO_MODEL), device_path);*/ } static const char *getpacket(int *we_know){ - fd_set readfds; - struct timeval tv; - int bytes_per_packet=0; - int ret; - static const char *packet_id=NULL; - static char buf[256]; - const char *s; - ssize_t r; + fd_set readfds; + struct timeval tv; + int bytes_per_packet=0; + int ret; + static const char *packet_id=NULL; + static char buf[256]; + const char *s; + ssize_t r; - - bytes_per_packet=*we_know; - D(printf("getpacket with %d\n",bytes_per_packet);) - - FD_ZERO(&readfds); - FD_SET(upsfd,&readfds); + bytes_per_packet=*we_know; + D(printf("getpacket with %d\n",bytes_per_packet);) + + FD_ZERO(&readfds); + FD_SET(upsfd,&readfds); /* Wait up to 2 seconds. */ - tv.tv_sec = 5; - tv.tv_usec = 0; - - ret=select(upsfd+1, &readfds, NULL, NULL, &tv); - if (!ret) { - s="Nothing received from UPS. Check cable conexion"; - upslogx(LOG_ERR, "%s", s); - D(printf("%s\n",s);) - return NULL; - } + tv.tv_sec = 5; + tv.tv_usec = 0; - r=read(upsfd,buf,255); - D(printf("%d bytes read: ",r);) - buf[r]=0; - if (bytes_per_packet && r < bytes_per_packet){ - ssize_t rr; - D(printf("short read...\n");) - usleep(500000); - tv.tv_sec = 2; - tv.tv_usec = 0; - ret=select(upsfd+1, &readfds, NULL, NULL, &tv); - if (!ret) return NULL; - rr=read(upsfd,buf+r,255-r); - r += rr; - if (r < bytes_per_packet) return NULL; - } - - if (!bytes_per_packet){ /* packet size determination */ - /* if (r%10 && r%9) { - printf("disregarding incomplete packet\n"); - return NULL; - }*/ - if (r%10==0) *we_know=10; - else if (r%9==0) *we_know=9; - return NULL; - } - - /* by here we have bytes_per_packet and a complete packet */ - /* lets check if within the complete packet we have a valid packet */ - if (bytes_per_packet == 10) packet_id="&&&"; else packet_id="***"; - s=strstr(buf,packet_id); - /* check validity of packet */ - if (!s) { - s="isbmex: no valid packet signature!"; - upslogx(LOG_ERR, "%s", s); - D(printf("%s\n",s);) - *we_know=0; - return NULL; - } - D(if (s != buf) printf("overlapping packet received\n");) - if ((int) strlen(s) < bytes_per_packet) { - D(printf("incomplete packet information\n");) - return NULL; - } + ret=select(upsfd+1, &readfds, NULL, NULL, &tv); + if (!ret) { + s="Nothing received from UPS. Check cable conexion"; + upslogx(LOG_ERR, "%s", s); + D(printf("%s\n",s);) + return NULL; + } + + r=read(upsfd,buf,255); + D(printf("%zd bytes read: ",r);) + buf[r]=0; + if (bytes_per_packet && r < bytes_per_packet){ + ssize_t rr; + D(printf("short read...\n");) + usleep(500000); + tv.tv_sec = 2; + tv.tv_usec = 0; + ret=select(upsfd+1, &readfds, NULL, NULL, &tv); + if (!ret) return NULL; + /* Casting is okay since bytes_per_packet is small + * and r is smaller, so 255-r is positive */ + assert (r <= 255); + rr = read(upsfd, buf+r, (size_t)(255-r)); + r += rr; + if (r < bytes_per_packet) return NULL; + } + + if (!bytes_per_packet){ /* packet size determination */ + /* + if (r%10 && r%9) { + printf("disregarding incomplete packet\n"); + return NULL; + } + */ + if (r%10==0) + *we_know=10; + else if (r%9==0) + *we_know=9; + return NULL; + } + + /* by here we have bytes_per_packet and a complete packet */ + /* lets check if within the complete packet we have a valid packet */ + if (bytes_per_packet == 10) + packet_id="&&&"; + else + packet_id="***"; + s=strstr(buf,packet_id); + /* check validity of packet */ + if (!s) { + s="isbmex: no valid packet signature!"; + upslogx(LOG_ERR, "%s", s); + D(printf("%s\n",s);) + *we_know=0; + return NULL; + } + D(if (s != buf) printf("overlapping packet received\n");) + if ((int) strlen(s) < bytes_per_packet) { + D(printf("incomplete packet information\n");) + return NULL; + } #ifdef DEBUG - printf("Got signal:"); - {int i;for (i=0;i",(unsigned char)s[i]);} - printf("\n"); + printf("Got signal:"); + {int i;for (i=0;i",(unsigned char)s[i]);} + printf("\n"); #endif - - return s; + + return s; } void upsdrv_updateinfo(void) { - static float high_volt=-1, low_volt=999; - const char *buf=NULL; - char buf2[17]; - int i; - static int bytes_per_packet=0; + static float high_volt=-1, low_volt=999; + const char *buf=NULL; + char buf2[17]; + int i; + static int bytes_per_packet=0; - for (i=0;i<5;i++) { - if ((buf=getpacket(&bytes_per_packet)) != NULL) break; - } - if (!bytes_per_packet || !buf) { - dstate_datastale(); + for (i=0;i<5;i++) { + if ((buf=getpacket(&bytes_per_packet)) != NULL) break; + } + if (!bytes_per_packet || !buf) { + dstate_datastale(); + return; + } + + /* do the parsing */ + { + float in_volt,battpct,acfreq; + double d; + D(printf("parsing (%d bytes per packet)\n",bytes_per_packet);) + /* input voltage :*/ + if (bytes_per_packet==9) { + in_volt = lagrange((unsigned char)buf[3]); + } else { + in_volt = interpol(sqrt((float)((unsigned char)buf[3]*256+(unsigned char)buf[4]))); + } + snprintf(buf2,16,"%5.1f",in_volt); + D(printf("utility=%s\n",buf2);) + dstate_setinfo("input.voltage", "%s", buf2); + + if (in_volt >= high_volt) high_volt=in_volt; + snprintf(buf2,16,"%5.1f",high_volt); + D(printf("highvolt=%s\n",buf2);) + dstate_setinfo("input.voltage.maximum", "%s", buf2); + + if (in_volt <= low_volt) low_volt=in_volt; + snprintf(buf2,16,"%5.1f",low_volt); + D(printf("lowvolt=%s\n",buf2);) + dstate_setinfo("input.voltage.minimum", "%s", buf2); + + battpct = ((double)((unsigned char)buf[(bytes_per_packet==10)?5:4])-168.0)*(100.0/(215.0-168.0)); + snprintf(buf2,16,"%5.1f",battpct); + D(printf("battpct=%s\n",buf2);) + dstate_setinfo("battery.charge", "%s", buf2); + + d=(unsigned char)buf[(bytes_per_packet==10)?6:5]*256 + + (unsigned char)buf[(bytes_per_packet==10)?7:6]; + acfreq = 1000000/d; + snprintf(buf2,16,"%5.2f",acfreq); + D(printf("acfreq=%s\n",buf2);) + dstate_setinfo("input.frequency", "%s", buf2); + + D(printf("status: ");) + status_init(); + switch (buf[(bytes_per_packet==10)?8:7]){ + case 48: break; /* normal operation */ + case 49: D(printf("BOOST ");) + status_set("BOOST"); + break; + case 50: D(printf("TRIM ");) + status_set("TRIM"); + break; + default: break; + } + switch (buf[(bytes_per_packet==10)?9:8]){ + case 48: D(printf("OL ");) + status_set("OL"); + break; + case 50: D(printf("LB ");) + status_set("LB"); + /* break; */ + /* FIXME? Can this device set independently LB and OB? */ + goto fallthrough_LB_means_OB; + /* FALLTHRU */ + case 49: + fallthrough_LB_means_OB: + D(printf("OB ");) + status_set("OB"); + break; + default: break; + } + D(printf("\n");) + status_commit(); + } + dstate_dataok(); return; - } - - /* do the parsing */ - { - float in_volt,battpct,acfreq; - double d; - D(printf("parsing (%d bytes per packet)\n",bytes_per_packet);) - /* input voltage :*/ - if (bytes_per_packet==9) { - in_volt = lagrange((unsigned char)buf[3]); - } else { - in_volt = interpol(sqrt((float)((unsigned char)buf[3]*256+(unsigned char)buf[4]))); - } - snprintf(buf2,16,"%5.1f",in_volt); - D(printf("utility=%s\n",buf2);) - dstate_setinfo("input.voltage", "%s", buf2); - - if (in_volt >= high_volt) high_volt=in_volt; - snprintf(buf2,16,"%5.1f",high_volt); - D(printf("highvolt=%s\n",buf2);) - dstate_setinfo("input.voltage.maximum", "%s", buf2); - - if (in_volt <= low_volt) low_volt=in_volt; - snprintf(buf2,16,"%5.1f",low_volt); - D(printf("lowvolt=%s\n",buf2);) - dstate_setinfo("input.voltage.minimum", "%s", buf2); - - battpct = ((double)((unsigned char)buf[(bytes_per_packet==10)?5:4])-168.0)*(100.0/(215.0-168.0)); - snprintf(buf2,16,"%5.1f",battpct); - D(printf("battpct=%s\n",buf2);) - dstate_setinfo("battery.charge", "%s", buf2); - - d=(unsigned char)buf[(bytes_per_packet==10)?6:5]*256 - + (unsigned char)buf[(bytes_per_packet==10)?7:6]; - acfreq = 1000000/d; - snprintf(buf2,16,"%5.2f",acfreq); - D(printf("acfreq=%s\n",buf2);) - dstate_setinfo("input.frequency", "%s", buf2); - - D(printf("status: ");) - status_init(); - switch (buf[(bytes_per_packet==10)?8:7]){ - case 48: break; /* normal operation */ - case 49: D(printf("BOOST ");) - status_set("BOOST"); - break; - case 50: D(printf("TRIM ");) - status_set("TRIM"); - default: break; - } - switch (buf[(bytes_per_packet==10)?9:8]){ - case 48: D(printf("OL ");) - status_set("OL"); - break; - case 50: D(printf("LB ");) - status_set("LB"); - case 49: D(printf("OB ");) - status_set("OB"); - break; - default: break; - } - D(printf("\n");) - status_commit(); - - - } - dstate_dataok(); - return; } void upsdrv_shutdown(void) @@ -316,9 +331,9 @@ void upsdrv_shutdown(void) * contact closure. Some ISB models with serial * support support contact closure, some don't. * If yours does support it, then a 12V signal - * on pin 9 does the trick (only when ups is + * on pin 9 does the trick (only when ups is * on OB condition) */ - /* + /* * here try to do the pin 9 trick, if it does not * work, else:*/ /* fatalx(EXIT_FAILURE, "Shutdown only supported with the Generic Driver, type 6 and special cable"); */ diff --git a/drivers/ivtscd.c b/drivers/ivtscd.c index d8ae507..1c3cee7 100644 --- a/drivers/ivtscd.c +++ b/drivers/ivtscd.c @@ -18,11 +18,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" #include "main.h" #include "serial.h" +#include "attribute.h" #define DRIVER_NAME "IVT Solar Controller driver" -#define DRIVER_VERSION "0.02" +#define DRIVER_VERSION "0.03" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -49,10 +51,11 @@ static struct { float temperature; } battery; -static int ivt_status(void) +static ssize_t ivt_status(void) { char reply[SMALLBUF]; - int ret, i, j = 0; + int i, j = 0; + ssize_t ret; ser_flush_io(upsfd); @@ -73,7 +76,7 @@ static int ivt_status(void) upsdebugx(3, "send: F"); sleep(1); /* allow controller some time to digest this */ - + /* * read: R:12,57;- 1,1;20;12,57;13,18;- 2,1; 1,5;\n */ @@ -90,7 +93,7 @@ static int ivt_status(void) } upsdebugx(3, "read: %.*s", (int)strcspn(reply, "\r\n"), reply); - upsdebug_hex(4, " \\_", reply, ret); + upsdebug_hex(4, " \\_", reply, (size_t)ret); for (i = 0; i < ret; i++) { switch(reply[i]) @@ -112,7 +115,7 @@ static int ivt_status(void) ret = sscanf(reply, "R:%f;%f;%f;%f;%f;%f;%f;", &battery.voltage.act, &battery.current.act, &battery.temperature, &battery.voltage.min, &battery.voltage.max, &battery.current.min, &battery.current.max); - upsdebugx(3, "Parsed %d parameters from reply", ret); + upsdebugx(3, "Parsed %zd parameters from reply", ret); return ret; } @@ -123,10 +126,10 @@ static int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } - + void upsdrv_initinfo(void) { if (ivt_status() < 7) { @@ -177,6 +180,9 @@ void upsdrv_updateinfo(void) dstate_dataok(); } +void upsdrv_shutdown(void) + __attribute__((noreturn)); + void upsdrv_shutdown(void) { while (1) { diff --git a/drivers/legrand-hid.c b/drivers/legrand-hid.c new file mode 100644 index 0000000..3715553 --- /dev/null +++ b/drivers/legrand-hid.c @@ -0,0 +1,236 @@ +/* legrand-hid.c - subdriver to monitor Legrand USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2012 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2013 Charles Lepple + * 2018 Gabriele Taormina , for Legrand + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "common.h" +#include "usbhid-ups.h" +#include "legrand-hid.h" +#include "main.h" +#include "usb-common.h" + +#define LEGRAND_HID_VERSION "Legrand HID 0.2" + +/* Legrand VendorID and ProductID */ +#define LEGRAND_VID 0x1cb0 /* Legrand */ +#define LEGRAND_PID_PDU 0x0038 /* Keor PDU model (800VA) */ +#define LEGRAND_PID_SP 0x0032 /* Keor SP model (600, 800, 1000, 1500, 2000VA version) */ + +static void *disable_interrupt_pipe(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + if (!use_interrupt_pipe) + return NULL; + use_interrupt_pipe = FALSE; + upslogx(LOG_INFO, "interrupt pipe disabled (add 'pollonly' flag to 'ups.conf' to get rid of this message)"); + return NULL; +} + +/* USB IDs device table */ +static usb_device_id_t legrand_usb_device_table[] = { + { USB_DEVICE(LEGRAND_VID, LEGRAND_PID_PDU), disable_interrupt_pipe }, /* Legrand Keor PDU */ + { USB_DEVICE(LEGRAND_VID, LEGRAND_PID_SP), disable_interrupt_pipe }, /* Legrand Keor SP */ + + /* Terminating entry */ + { 0, 0, NULL } +}; + +/* --------------------------------------------------------------- */ +/* Vendor-specific usage table */ +/* --------------------------------------------------------------- */ + +/* LEGRAND usage table */ +static usage_lkp_t legrand_usage_lkp[] = { + { NULL, 0 } +}; + +static usage_tables_t legrand_utab[] = { + legrand_usage_lkp, + hid_usage_lkp, + NULL, +}; + +static const char *legrand_times10(double value) +{ + static char buf[20]; + snprintf(buf, sizeof(buf), "%0.1f", value * 10); + return buf; +} + +static info_lkp_t legrand_times10_info[] = { + { 0, NULL, legrand_times10, NULL }, + { 0, NULL, NULL, NULL } +}; + +static const char *legrand_times100k(double value) +{ + static char buf[20]; + snprintf(buf, sizeof(buf), "%0.1f", value * 100000); + return buf; +} + +static info_lkp_t legrand_times100k_info[] = { + { 0, NULL, legrand_times100k, NULL }, + { 0, NULL, NULL, NULL } +}; + +static const char *legrand_times1M(double value) +{ + static char buf[20]; + snprintf(buf, sizeof(buf), "%0.1f", value * 1000000); + return buf; +} + +static info_lkp_t legrand_times1M_info[] = { + { 0, NULL, legrand_times1M, NULL }, + { 0, NULL, NULL, NULL } +}; + +static const char *legrand_times10M(double value) +{ + static char buf[20]; + snprintf(buf, sizeof(buf), "%0.1f", value * 10000000); + return buf; +} + +static info_lkp_t legrand_times10M_info[] = { + { 0, NULL, legrand_times10M, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* --------------------------------------------------------------- */ +/* HID2NUT lookup table */ +/* --------------------------------------------------------------- */ + +static hid_info_t legrand_hid2nut[] = { + /* Input Data */ + { "input.voltage", 0, 0, "UPS.Input.Voltage", NULL, "%.0f", 0, NULL }, + { "input.voltage", 0, 0, "UPS.PowerConverter.Input.Voltage", NULL, "%.0f", 0, legrand_times1M_info }, + { "input.transfer.high", 0, 0, "UPS.Input.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "input.transfer.high", 0, 0, "UPS.PowerConverter.Output.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, legrand_times10_info }, + { "input.transfer.low", 0, 0, "UPS.Input.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "input.transfer.low", 0, 0, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "input.voltage.nominal", 0, 0, "UPS.Input.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "input.voltage.nominal", 0, 0, "UPS.Flow.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + + /* Battery Data */ + { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, divide_by_10_conversion }, + { "battery.voltage.nominal", 0, 0, "UPS.BatterySystem.Battery.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.0f", 0, divide_by_10_conversion }, + { "battery.voltage", 0, 0, "UPS.BatterySystem.Battery.Voltage", NULL, "%.0f", 0, legrand_times100k_info }, + { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, + { "battery.runtime", 0, 0, "UPS.PowerSummary.RuntimeToEmpty", NULL, "%.0f", 0, NULL }, + { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + + /* Output Data */ + { "output.voltage", 0, 0, "UPS.Output.Voltage", NULL, "%.0f", 0, NULL }, + { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.0f", 0, legrand_times10M_info }, + { "output.frequency", 0, 0, "UPS.Output.Frequency", NULL, "%.0f", 0, NULL }, + { "ups.load", 0, 0, "UPS.Output.PercentLoad", NULL, "%.0f", 0, NULL }, + { "ups.load", 0, 0, "UPS.OutletSystem.Outlet.PercentLoad", NULL, "%.0f", 0, NULL }, + { "ups.realpower.nominal", 0, 0, "UPS.Output.ConfigActivePower", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "ups.realpower.nominal", 0, 0, "UPS.Flow.ConfigApparentPower", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + + /* UPS Status */ + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, + { "BOOL", 0, 0, "UPS.Output.Overload", NULL, NULL, HU_FLAG_QUICK_POLL, overload_info }, + + /* Delays */ + { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL }, + { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.OutletSystem.Outlet.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL }, + { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Output.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL }, + { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Output.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL }, + { "load.off.delay", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, + { "load.on.delay", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, + { "load.off.delay", 0, 0, "UPS.Output.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, + { "load.on.delay", 0, 0, "UPS.Output.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, + + /* Battery Testing */ + { "test.battery.start.quick", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "1", HU_TYPE_CMD, NULL }, + { "test.battery.start.deep", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "2", HU_TYPE_CMD, NULL }, + { "test.battery.stop", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "3", HU_TYPE_CMD, NULL }, + { "test.battery.start.quick", 0, 0, "UPS.Output.Test", NULL, "1", HU_TYPE_CMD, NULL }, + { "test.battery.start.deep", 0, 0, "UPS.Output.Test", NULL, "2", HU_TYPE_CMD, NULL }, + { "test.battery.stop", 0, 0, "UPS.Output.Test", NULL, "3", HU_TYPE_CMD, NULL }, + + /* Buzzer */ + { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, + { "beeper.disable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } +}; + +static const char *legrand_format_model(HIDDevice_t *hd) +{ + return hd->Product; +} + +static const char *legrand_format_mfr(HIDDevice_t *hd) +{ + return hd->Vendor ? hd->Vendor : "Legrand"; +} + +static const char *legrand_format_serial(HIDDevice_t *hd) +{ + return hd->Serial; +} + +/* this function allows the subdriver to "claim" a device: return 1 if + * the device is supported by this subdriver, else 0. */ +static int legrand_claim(HIDDevice_t *hd) +{ + int status = is_usb_device_supported(legrand_usb_device_table, hd); + + switch (status) + { + case POSSIBLY_SUPPORTED: + /* by default, reject, unless the productid option is given */ + if (getval("productid")) + return 1; + possibly_supported("Legrand", hd); + return 0; + + case SUPPORTED: + return 1; + + case NOT_SUPPORTED: + default: + return 0; + } +} + +subdriver_t legrand_subdriver = { + LEGRAND_HID_VERSION, + legrand_claim, + legrand_utab, + legrand_hid2nut, + legrand_format_model, + legrand_format_mfr, + legrand_format_serial, + fix_report_desc, +}; diff --git a/drivers/legrand-hid.h b/drivers/legrand-hid.h new file mode 100644 index 0000000..59770cd --- /dev/null +++ b/drivers/legrand-hid.h @@ -0,0 +1,30 @@ +/* legrand-hid.h - subdriver to monitor Legrand USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2009 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef LEGRAND_HID_H +#define LEGRAND_HID_H + +#include "usbhid-ups.h" + +extern subdriver_t legrand_subdriver; + +#endif /* LEGRAND_HID_H */ diff --git a/drivers/libhid.c b/drivers/libhid.c index 6fce6a9..cbc0d8e 100644 --- a/drivers/libhid.c +++ b/drivers/libhid.c @@ -7,7 +7,7 @@ * John Stamp * Peter Selinger * Arjen de Korte - * + * * This program is sponsored by MGE UPS SYSTEMS - opensource.mgeups.com * * The logic of this file is ripped from mge-shut driver (also from @@ -30,8 +30,15 @@ * * -------------------------------------------------------------------------- */ +#include "config.h" /* must be the first header */ + #include -#include +#ifdef HAVE_STRING_H +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif /* #include */ #include "libhid.h" #include "hidparser.h" @@ -42,7 +49,7 @@ #include "libshut.h" communication_subdriver_t *comm_driver = &shut_subdriver; #else - #include "libusb.h" + #include "nut_libusb.h" communication_subdriver_t *comm_driver = &usb_subdriver; #endif @@ -56,6 +63,16 @@ static int path_to_string(char *string, size_t size, const HIDPath_t *path, usag static int8_t get_unit_expo(const HIDData_t *hiddata); static double exponent(double a, int8_t b); +/* Tweak flag for APC Back-UPS */ +size_t max_report_size = 0; + +/* Tweaks for Powercom, at least */ +int interrupt_only = 0; +size_t interrupt_size = 0; + +#define SMIN(a, b) ( ((intmax_t)(a) < (intmax_t)(b)) ? (a) : (b) ) +#define UMIN(a, b) ( ((uintmax_t)(a) < (uintmax_t)(b)) ? (a) : (b) ) + /* ---------------------------------------------------------------------- */ /* report buffering system */ @@ -83,13 +100,14 @@ void free_report_buffer(reportbuf_t *rbuf) /* allocate a new report buffer. Return pointer on success, else NULL with errno set. The returned data structure must later be freed with free_report_buffer(). */ -reportbuf_t *new_report_buffer(HIDDesc_t *pDesc) +reportbuf_t *new_report_buffer(HIDDesc_t *arg_pDesc) { HIDData_t *pData; reportbuf_t *rbuf; - int i, id; + int id; + size_t i; - if (!pDesc) + if (!arg_pDesc) return NULL; rbuf = calloc(1, sizeof(*rbuf)); @@ -98,9 +116,9 @@ reportbuf_t *new_report_buffer(HIDDesc_t *pDesc) } /* now go through all items that are part of this report */ - for (i=0; initems; i++) { + for (i=0; initems; i++) { - pData = &pDesc->item[i]; + pData = &arg_pDesc->item[i]; id = pData->ReportID; @@ -109,7 +127,7 @@ reportbuf_t *new_report_buffer(HIDDesc_t *pDesc) continue; /* first byte holds id */ - rbuf->len[id] = pDesc->replen[id] + 1; + rbuf->len[id] = arg_pDesc->replen[id] + 1; /* skip zero length reports */ if (rbuf->len[id] < 1) { @@ -137,27 +155,97 @@ reportbuf_t *new_report_buffer(HIDDesc_t *pDesc) seconds, then the report is freshly read from the USB device. Otherwise, it is unchanged. Return 0 on success, -1 on error with errno set. */ -static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t *pData, int age) +/* because buggy firmwares from APC return wrong report size, we either + ask the report with the found report size or with the whole buffer size + depending on the max_report_size flag */ +static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t *pData, time_t age) { - int id = pData->ReportID; - int r; + usb_ctrl_repindex id = pData->ReportID; + int ret; + size_t r; - if (rbuf->ts[id] + age > time(NULL)) { + if (interrupt_only || rbuf->ts[id] + age > time(NULL)) { /* buffered report is still good; nothing to do */ upsdebug_hex(3, "Report[buf]", rbuf->data[id], rbuf->len[id]); return 0; } - r = comm_driver->get_report(udev, id, rbuf->data[id], rbuf->len[id]); - if (r <= 0) { + r = max_report_size ? sizeof(rbuf->data[id]) : rbuf->len[id]; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if ((uintmax_t)r > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { + upsdebugx(2, + "%s: suggested buffer size %zu exceeds " + "USB_CTRL_CHARBUFSIZE_MAX %ju; " + "report will be constrained", + __func__, r, (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX); + if ((uintmax_t)USB_CTRL_CHARBUFSIZE_MAX <= (uintmax_t)SIZE_MAX + && USB_CTRL_CHARBUFSIZE_MAX > 0) { + r = (size_t)USB_CTRL_CHARBUFSIZE_MAX - 1; + } else { + /* SIZE_MAX is obviously too much here; least common + * denominator across libs can be UINT8 or UINT16. + * We should never hit this codepath unless definition + * above is bonkers, anyway. + */ + r = (size_t)UINT8_MAX - 1; + } + + /* Avoid overflowing known buffer size, to be sure: */ + r = UMIN(r, sizeof(rbuf->data[id])); + if (!max_report_size) { + r = UMIN(r, rbuf->len[id]); + } + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic pop +#endif + + ret = comm_driver->get_report(udev, id, + (usb_ctrl_charbuf)rbuf->data[id], + (usb_ctrl_charbufsize)r); + + if (ret <= 0) { return -1; } + r = (size_t)ret; if (rbuf->len[id] != r) { - upsdebugx(2, "%s: expected %d bytes, but got %d instead", __func__, rbuf->len[id], r); - upsdebug_hex(3, "Report[err]", rbuf->data[id], r); + /* e.g. if maxreportsize flag was set */ + upsdebugx(2, + "%s: expected %zu bytes, but got %zu instead", + __func__, rbuf->len[id], r); + upsdebug_hex(3, "Report[err]", + (usb_ctrl_charbuf)rbuf->data[id], r); } else { - upsdebug_hex(3, "Report[get]", rbuf->data[id], rbuf->len[id]); + upsdebug_hex(3, "Report[get]", + (usb_ctrl_charbuf)rbuf->data[id], rbuf->len[id]); } /* have (valid) report */ @@ -170,7 +258,7 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa conversion is performed. If age>0, the read operation is buffered if the item's age is less than "age". On success, return 0 and store the answer in *value. On failure, return -1 and set errno. */ -static int get_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t *pData, long *Value, int age) +static int get_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t *pData, long *Value, time_t age) { int id = pData->ReportID; int r; @@ -190,17 +278,70 @@ static int get_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t -1 and set errno. The updated value is sent to the device. */ static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t *pData, long Value) { - int id = pData->ReportID; - int r; + usb_ctrl_repindex id = pData->ReportID; + int ret; + size_t r = rbuf->len[id]; SetValue(pData, rbuf->data[id], Value); - r = comm_driver->set_report(udev, id, rbuf->data[id], rbuf->len[id]); - if (r <= 0) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if ((uintmax_t)r > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { + upsdebugx(2, + "%s: suggested buffer size %zu exceeds " + "USB_CTRL_CHARBUFSIZE_MAX %ju; " + "item setting will be constrained", + __func__, r, (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX); + if ((uintmax_t)USB_CTRL_CHARBUFSIZE_MAX <= (uintmax_t)SIZE_MAX + && USB_CTRL_CHARBUFSIZE_MAX > 0) { + r = (size_t)USB_CTRL_CHARBUFSIZE_MAX - 1; + } else { + /* SIZE_MAX is obviously too much here; least common + * denominator across libs can be UINT8 or UINT16. + * We should never hit this codepath unless definition + * above is bonkers, anyway. + */ + r = (size_t)UINT8_MAX - 1; + } + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic pop +#endif + + ret = comm_driver->set_report(udev, id, + (usb_ctrl_charbuf)rbuf->data[id], + (usb_ctrl_charbufsize)r); + if (ret <= 0) { return -1; } - upsdebug_hex(3, "Report[set]", rbuf->data[id], rbuf->len[id]); + upsdebug_hex(3, "Report[set]", rbuf->data[id], r); /* expire report */ rbuf->ts[id] = 0; @@ -212,7 +353,7 @@ static int set_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t report has been obtained without having been explicitly requested, e.g., it arrived through an interrupt transfer. Returns 0 on success, -1 on error with errno set. */ -static int file_report_buffer(reportbuf_t *rbuf, unsigned char *buf, int buflen) +static int file_report_buffer(reportbuf_t *rbuf, unsigned char *buf, size_t buflen) { int id = buf[0]; @@ -220,7 +361,9 @@ static int file_report_buffer(reportbuf_t *rbuf, unsigned char *buf, int buflen) memcpy(rbuf->data[id], buf, (buflen < rbuf->len[id]) ? buflen : rbuf->len[id]); if (rbuf->len[id] != buflen) { - upsdebugx(2, "%s: expected %d bytes, but got %d instead", __func__, rbuf->len[id], buflen); + upsdebugx(2, + "%s: expected %zu bytes, but got %zu instead", + __func__, rbuf->len[id], buflen); upsdebug_hex(3, "Report[err]", buf, buflen); } else { upsdebug_hex(3, "Report[int]", rbuf->data[id], rbuf->len[id]); @@ -256,14 +399,17 @@ static struct { /* CAUTION: be careful when modifying the output format of this function, * since it's used to produce sub-drivers "stub" using - * scripts/subdriver/path-to-subdriver.sh + * scripts/subdriver/gen-usbhid-subdriver.sh */ -void HIDDumpTree(hid_dev_handle_t udev, usage_tables_t *utab) +void HIDDumpTree(hid_dev_handle_t udev, HIDDevice_t *hd, usage_tables_t *utab) { - int i; -#ifndef SHUT_MODE + size_t i; +#ifdef SHUT_MODE + NUT_UNUSED_VARIABLE(hd); +#else /* extract the VendorId for further testing */ - int vendorID = usb_device((struct usb_dev_handle *)udev)->descriptor.idVendor; + int vendorID = hd->VendorID; + int productID = hd->ProductID; #endif /* Do not go further if we already know nothing will be displayed. @@ -275,6 +421,8 @@ void HIDDumpTree(hid_dev_handle_t udev, usage_tables_t *utab) return; } + upsdebugx(1, "%zu HID objects found", pDesc->nitems); + for (i = 0; i < pDesc->nitems; i++) { double value; @@ -291,6 +439,13 @@ void HIDDumpTree(hid_dev_handle_t udev, usage_tables_t *utab) continue; } } + + /* skip report 0x54 for Tripplite SU3000LCD2UHV due to firmware bug */ + if ((vendorID == 0x09ae) && (productID == 0x1330)) { + if (pData->ReportID == 0x54) { + continue; + } + } #endif /* Get data value */ @@ -338,7 +493,7 @@ HIDData_t *HIDGetItemData(const char *hidpath, usage_tables_t *utab) } /* Get info on object (reportID, offset and size) */ - return FindObject_with_Path(pDesc, &Path, ITEM_FEATURE); + return FindObject_with_Path(pDesc, &Path, interrupt_only ? ITEM_INPUT:ITEM_FEATURE); } char *HIDGetDataItem(const HIDData_t *hiddata, usage_tables_t *utab) @@ -354,7 +509,7 @@ char *HIDGetDataItem(const HIDData_t *hiddata, usage_tables_t *utab) /* Return the physical value associated with the given HIDData path. * return 1 if OK, 0 on fail, -errno otherwise (ie disconnect). */ -int HIDGetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double *Value, int age) +int HIDGetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double *Value, time_t age) { int r; long hValue; @@ -370,10 +525,10 @@ int HIDGetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double *Value, in } *Value = hValue; - + /* Convert Logical Min, Max and Value into Physical */ *Value = logical_to_physical(hiddata, hValue); - + /* Process exponents and units */ *Value *= exponent(10, get_unit_expo(hiddata)); @@ -392,10 +547,78 @@ int HIDGetItemValue(hid_dev_handle_t udev, const char *hidpath, double *Value, u */ char *HIDGetIndexString(hid_dev_handle_t udev, const int Index, char *buf, size_t buflen) { - if (comm_driver->get_string(udev, Index, buf, buflen) < 1) + usb_ctrl_strindex idx; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if ((uintmax_t)Index > (uintmax_t)USB_CTRL_STRINDEX_MAX + || (intmax_t)Index < (intmax_t)USB_CTRL_STRINDEX_MIN + ) { + upsdebugx(2, + "%s: requested index number is out of range, " + "expected %jd < %i < %ju", + __func__, + (intmax_t)USB_CTRL_STRINDEX_MIN, + Index, + (uintmax_t)USB_CTRL_STRINDEX_MAX); + return NULL; + } + idx = (usb_ctrl_strindex)Index; + + if ((uintmax_t)buflen > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { + upsdebugx(2, + "%s: suggested buffer size %zu exceeds " + "USB_CTRL_CHARBUFSIZE_MAX %ju; " + "index string will be constrained", + __func__, buflen, + (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX); + + if ((uintmax_t)USB_CTRL_CHARBUFSIZE_MAX <= (uintmax_t)SIZE_MAX + && USB_CTRL_CHARBUFSIZE_MAX > 0) { + buflen = (size_t)USB_CTRL_CHARBUFSIZE_MAX - 1; + } else { + /* SIZE_MAX is obviously too much here; least common + * denominator across libs can be UINT8 or UINT16. + * We should never hit this codepath unless definition + * above is bonkers, anyway. + */ + buflen = (size_t)UINT8_MAX - 1; + } + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic pop +#endif + + if (comm_driver->get_string(udev, idx, buf, (usb_ctrl_charbufsize)buflen) < 1) buf[0] = '\0'; - return rtrim(buf, '\n'); + return str_rtrim(buf, '\n'); } /* Return pointer to indexed string from HID path (empty if not found) @@ -417,7 +640,7 @@ char *HIDGetItemString(hid_dev_handle_t udev, const char *hidpath, char *buf, si */ int HIDSetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double Value) { - int i, r; + int r; long hValue; if (hiddata == NULL) { @@ -433,7 +656,7 @@ int HIDSetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double Value) /* Process exponents and units */ Value /= exponent(10, get_unit_expo(hiddata)); - + /* Convert Physical Min, Max and Value into Logical */ hValue = physical_to_logical(hiddata, Value); @@ -444,10 +667,8 @@ int HIDSetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double Value) } /* flush the report buffer (data may have changed) */ - for (i=0; i<256; i++) { - reportbuf->ts[i] = 0; - } - + memset(reportbuf->ts, 0, sizeof(reportbuf->ts)); + upsdebugx(4, "Set report succeeded"); return 1; } @@ -467,17 +688,78 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventsize) { unsigned char buf[SMALLBUF]; int itemCount = 0; - int buflen, r, i; + int buflen, ret; + size_t i, r; HIDData_t *pData; /* needs libusb-0.1.8 to work => use ifdef and autoconf */ - buflen = comm_driver->get_interrupt(udev, buf, sizeof(buf), 250); + r = interrupt_size ? interrupt_size : sizeof(buf); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if ((uintmax_t)r > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX) { + /* FIXME: Should we try here, or plain abort? */ + upsdebugx(2, + "%s: suggested buffer size %zu exceeds " + "USB_CTRL_CHARBUFSIZE_MAX %ju; " + "report will be constrained", + __func__, r, (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX); + + if ((uintmax_t)USB_CTRL_CHARBUFSIZE_MAX <= (uintmax_t)SIZE_MAX + && USB_CTRL_CHARBUFSIZE_MAX > 0) { + r = (size_t)USB_CTRL_CHARBUFSIZE_MAX - 1; + } else { + /* SIZE_MAX is obviously too much here; least common + * denominator across libs can be UINT8 or UINT16. + * We should never hit this codepath unless definition + * above is bonkers, anyway. + */ + r = (size_t)UINT8_MAX - 1; + } + + /* Avoid overflowing known buffer size, to be sure: */ + r = UMIN(r, sizeof(buf)); + } +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE) ) +# pragma GCC diagnostic pop +#endif + + buflen = comm_driver->get_interrupt( + udev, (usb_ctrl_charbuf)buf, + (usb_ctrl_charbufsize)r, + 750); + if (buflen <= 0) { return buflen; /* propagate "error" or "no event" code */ } - r = file_report_buffer(reportbuf, buf, buflen); - if (r < 0) { + ret = file_report_buffer(reportbuf, buf, (size_t)buflen); + if (ret < 0) { upsdebug_with_errno(1, "%s: failed to buffer report", __func__); return -errno; } @@ -538,7 +820,7 @@ static double logical_to_physical(HIDData_t *Data, long logical) /* this should not really happen */ return (double)logical; } - + Factor = (double)(Data->PhyMax - Data->PhyMin) / (Data->LogMax - Data->LogMin); /* Convert Value */ physical = (double)((logical - Data->LogMin) * Factor) + Data->PhyMin; @@ -576,7 +858,7 @@ static long physical_to_logical(HIDData_t *Data, double physical) /* this should not really happen */ return (long)physical; } - + Factor = (double)(Data->LogMax - Data->LogMin) / (Data->PhyMax - Data->PhyMin); /* Convert Value */ logical = (long)((physical - Data->PhyMin) * Factor) + Data->LogMin; @@ -627,38 +909,66 @@ static int string_to_path(const char *string, HIDPath_t *path, usage_tables_t *u int i = 0; long usage; char buf[SMALLBUF]; - char *token, *last; - + char *token, *last; + snprintf(buf, sizeof(buf), "%s", string); for (token = strtok_r(buf, ".", &last); token != NULL; token = strtok_r(NULL, ".", &last)) { /* lookup tables first (to override defaults) */ + /* Note/FIXME?: we happen to process "usage" as a "signed long" + * while the HIDNode_t behind it is (currently) uint32_t. + * The method below returns `-1` for entries not found; however + * half of our permissible range may seem negative and be valid. + */ if ((usage = hid_lookup_usage(token, utab)) != -1) { - path->Node[i++] = usage; + path->Node[i++] = (HIDNode_t)usage; continue; + } else { + /* hid_lookup_usage() logs for itself: ... -> not found in lookup table */ + upsdebugx(5, "string_to_path: hid_lookup_usage failed, " + "checking if token %s is a raw value", token); } /* translate unnamed path components such as "ff860024" */ if (strlen(token) == strspn(token, "1234567890abcdefABCDEF")) { - path->Node[i++] = strtol(token, NULL, 16); + long l = strtol(token, NULL, 16); + /* Note: currently per hidtypes.h, HIDNode_t == uint32_t */ + if (l < 0 || (uintmax_t)l > (uintmax_t)UINT32_MAX) { + upsdebugx(5, "string_to_path: badvalue (pathcomp): " + "%ld negative or %ju too large", + l, (uintmax_t)l); + goto badvalue; + } + path->Node[i++] = (HIDNode_t)l; continue; } /* indexed collection */ if (strlen(token) == strspn(token, "[1234567890]")) { - path->Node[i++] = 0x00ff0000 + atoi(token+1); + int l = atoi(token + 1); /* +1: skip the bracket */ + if (l < 0 || (uintmax_t)l > (uintmax_t)UINT32_MAX) { + upsdebugx(5, "string_to_path: badvalue(indexed): " + "%d negative or %ju too large", + l, (uintmax_t)l); + goto badvalue; + } + path->Node[i++] = 0x00ff0000 + (HIDNode_t)l; continue; } +badvalue: /* Uh oh, typo in usage table? */ upsdebugx(1, "string_to_path: couldn't parse %s from %s", token, string); } - path->Size = i; + if (i < 0 || i > (int)UINT8_MAX) { + fatalx(EXIT_FAILURE, "Error: string_to_path(): length exceeded"); + } + path->Size = (uint8_t)i; /* by construct, i>=0; but anyway checked above to be sure */ upsdebugx(4, "string_to_path: depth = %d", path->Size); return i; @@ -671,7 +981,7 @@ static int path_to_string(char *string, size_t size, const HIDPath_t *path, usag const char *p; snprintf(string, size, "%s", ""); - + for (i = 0; i < path->Size; i++) { if (i > 0) @@ -685,20 +995,22 @@ static int path_to_string(char *string, size_t size, const HIDPath_t *path, usag } /* indexed collection */ - if ((path->Node[i] & 0xffff0000) == 0x00ff0000) + if ((path->Node[i] & 0xffff0000) == 0x00ff0000) { snprintfcat(string, size, "[%i]", path->Node[i] & 0x0000ffff); continue; } /* unnamed path components such as "ff860024" */ - snprintfcat(string, size, "%08x", path->Node[i]); + snprintfcat(string, size, "%08x", path->Node[i]); } return i; } -/* usage conversion string -> numeric */ +/* usage conversion string -> numeric + * Returns -1 for error, or a (HIDNode_t) ranged code value + */ static long hid_lookup_usage(const char *name, usage_tables_t *utab) { int i, j; @@ -710,8 +1022,9 @@ static long hid_lookup_usage(const char *name, usage_tables_t *utab) if (strcasecmp(utab[i][j].usage_name, name)) continue; - upsdebugx(5, "hid_lookup_usage: %s -> %08x", name, (unsigned int)utab[i][j].usage_code); - return utab[i][j].usage_code; + /* Note: currently per hidtypes.h, HIDNode_t == uint32_t */ + upsdebugx(5, "hid_lookup_usage: %s -> %08x", name, (uint32_t)utab[i][j].usage_code); + return (long)(utab[i][j].usage_code); } } @@ -801,7 +1114,7 @@ usage_lkp_t hid_usage_lkp[] = { { "SwitchOffControl", 0x00840051 }, { "ToggleControl", 0x00840052 }, { "LowVoltageTransfer", 0x00840053 }, - { "HighVoltageTransfer", 0x00840054 }, + { "HighVoltageTransfer", 0x00840054 }, { "DelayBeforeReboot", 0x00840055 }, { "DelayBeforeStartup", 0x00840056 }, { "DelayBeforeShutdown", 0x00840057 }, @@ -814,7 +1127,7 @@ usage_lkp_t hid_usage_lkp[] = { { "InternalFailure", 0x00840062 }, { "VoltageOutOfRange", 0x00840063 }, { "FrequencyOutOfRange", 0x00840064 }, - { "Overload", 0x00840065 }, + { "Overload", 0x00840065 }, /* Note: the correct spelling is "Overload", not "OverLoad", * according to the official specification, "Universal Serial * Bus Usage Tables for HID Power Devices", Release 1.0, diff --git a/drivers/libhid.h b/drivers/libhid.h index 0e513f9..23e34a1 100644 --- a/drivers/libhid.h +++ b/drivers/libhid.h @@ -26,27 +26,30 @@ * * -------------------------------------------------------------------------- */ -#ifndef _LIBHID_H -#define _LIBHID_H +#ifndef NUT_LIBHID_H_SEEN +#define NUT_LIBHID_H_SEEN #include "config.h" #include +#include "nut_stdint.h" #include "hidtypes.h" #include "timehead.h" #ifdef SHUT_MODE #include "libshut.h" - typedef SHUTDevice_t HIDDevice_t; - typedef char HIDDeviceMatcher_t; - typedef int hid_dev_handle_t; - typedef shut_communication_subdriver_t communication_subdriver_t; + typedef SHUTDevice_t HIDDevice_t; + typedef char HIDDeviceMatcher_t; + typedef usb_dev_handle hid_dev_handle_t; + typedef shut_communication_subdriver_t communication_subdriver_t; + #define HID_DEV_HANDLE_CLOSED (hid_dev_handle_t)(-1) #else - #include "libusb.h" - typedef USBDevice_t HIDDevice_t; - typedef USBDeviceMatcher_t HIDDeviceMatcher_t; - typedef usb_dev_handle * hid_dev_handle_t; - typedef usb_communication_subdriver_t communication_subdriver_t; + #include "nut_libusb.h" /* includes usb-common.h */ + typedef USBDevice_t HIDDevice_t; + typedef USBDeviceMatcher_t HIDDeviceMatcher_t; + typedef usb_dev_handle * hid_dev_handle_t; + typedef usb_communication_subdriver_t communication_subdriver_t; + #define HID_DEV_HANDLE_CLOSED (hid_dev_handle_t)(NULL) #endif /* use explicit booleans */ @@ -82,13 +85,17 @@ extern HIDDesc_t *pDesc; /* parsed Report Descriptor */ /* report buffer structure: holds data about most recent report for each given report id */ typedef struct reportbuf_s { - time_t ts[256]; /* timestamp when report was retrieved */ - int len[256]; /* size of report data */ - unsigned char *data[256]; /* report data (allocated) */ + time_t ts[256]; /* timestamp when report was retrieved */ + size_t len[256]; /* size of report data */ + unsigned char *data[256]; /* report data (allocated) */ } reportbuf_t; extern reportbuf_t *reportbuf; /* buffer for most recent reports */ +extern size_t max_report_size; +extern int interrupt_only; +extern size_t interrupt_size; + /* ---------------------------------------------------------------------- */ /* @@ -119,7 +126,7 @@ char *HIDGetDataItem(const HIDData_t *hiddata, usage_tables_t *utab); /* * HIDGetDataValue * -------------------------------------------------------------------------- */ -int HIDGetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double *Value, int age); +int HIDGetDataValue(hid_dev_handle_t udev, HIDData_t *hiddata, double *Value, time_t age); /* * HIDSetDataValue @@ -139,10 +146,10 @@ int HIDGetEvents(hid_dev_handle_t udev, HIDData_t **event, int eventlen); /* * Support functions * -------------------------------------------------------------------------- */ -void HIDDumpTree(hid_dev_handle_t udev, usage_tables_t *utab); +void HIDDumpTree(hid_dev_handle_t udev, HIDDevice_t *hd, usage_tables_t *utab); const char *HIDDataType(const HIDData_t *hiddata); void free_report_buffer(reportbuf_t *rbuf); reportbuf_t *new_report_buffer(HIDDesc_t *pDesc); -#endif /* _LIBHID_H */ +#endif /* NUT_LIBHID_H_SEEN */ diff --git a/drivers/libshut.c b/drivers/libshut.c index 2028648..817fda4 100644 --- a/drivers/libshut.c +++ b/drivers/libshut.c @@ -29,6 +29,8 @@ * - validate / complete commands and data table in mge-hid from mge-shut */ +#include "config.h" /* must be the first header */ + #include #include #include @@ -41,7 +43,7 @@ #include "common.h" /* for xmalloc, upsdebugx prototypes */ #define SHUT_DRIVER_NAME "SHUT communication driver" -#define SHUT_DRIVER_VERSION "0.82" +#define SHUT_DRIVER_VERSION "0.86" /* communication driver description structure */ upsdrv_info_t comm_upsdrv_info = { @@ -59,13 +61,13 @@ upsdrv_info_t comm_upsdrv_info = { * HID descriptor, completed with desc{type,len} */ struct my_hid_descriptor { - uint8_t bLength; - uint8_t bDescriptorType; - uint16_t bcdHID; - uint8_t bCountryCode; - uint8_t bNumDescriptors; - uint8_t bReportDescriptorType; - uint16_t wDescriptorLength; + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdHID; + uint8_t bCountryCode; + uint8_t bNumDescriptors; + uint8_t bReportDescriptorType; + uint16_t wDescriptorLength; }; /*!******************************************************** @@ -170,7 +172,7 @@ struct my_hid_descriptor { #define SHUT_NOK 0x15 /* sync signals are also used to set the notification level */ #define SHUT_SYNC 0x16 /* complete notifications - not yet managed */ - /* but needed for some early Ellipse models */ + /* but needed for some early Ellipse models */ #define SHUT_SYNC_LIGHT 0x17 /* partial notifications */ #define SHUT_SYNC_OFF 0x18 /* disable notifications - only do polling */ #define SHUT_PKT_LAST 0x80 @@ -180,19 +182,54 @@ struct my_hid_descriptor { /*! * SHUT functions for HID marshalling */ -int shut_get_descriptor(int upsfd, unsigned char type, - unsigned char index, void *buf, int size); -int shut_get_string_simple(int upsfd, int index, - char *buf, size_t buflen); -int libshut_get_report(int upsfd, int ReportId, - unsigned char *raw_buf, int ReportSize ); -int shut_set_report(int upsfd, int id, unsigned char *pkt, int reportlen); -int libshut_get_interrupt(int upsfd, unsigned char *buf, - int bufsize, int timeout); -void libshut_close(int upsfd); +/* Expected evaluated types for the API after typedefs: + * static int shut_get_descriptor(int upsfd, unsigned char type, + * unsigned char index, void *buf, int size); + * static int shut_get_string_simple(int upsfd, int index, + * char *buf, size_t buflen); + * static int libshut_get_report(int upsfd, int ReportId, + * unsigned char *raw_buf, int ReportSize ); + * static int libshut_set_report(int upsfd, int id, unsigned char *pkt, + * int reportlen); + * static int libshut_get_interrupt(int upsfd, unsigned char *buf, + * int bufsize, int timeout); + * static void libshut_close(int upsfd); + */ +static int shut_get_descriptor( + usb_dev_handle upsfd, + usb_ctrl_requesttype type, + usb_ctrl_descindex index, + void *buf, + usb_ctrl_charbufsize size); + +static int shut_get_string_simple( + usb_dev_handle upsfd, + usb_ctrl_strindex index, + char *buf, + usb_ctrl_charbufsize buflen); + +static int libshut_get_report( + usb_dev_handle upsfd, + usb_ctrl_repindex ReportId, + usb_ctrl_charbuf raw_buf, + usb_ctrl_charbufsize ReportSize); + +static int libshut_set_report( + usb_dev_handle upsfd, + usb_ctrl_repindex id, + usb_ctrl_charbuf pkt, + usb_ctrl_charbufsize reportlen); + +static int libshut_get_interrupt( + usb_dev_handle upsfd, + usb_ctrl_charbuf buf, + usb_ctrl_charbufsize bufsize, + usb_ctrl_timeout_msec timeout); + +static void libshut_close(usb_dev_handle upsfd); /* FIXME */ -const char * shut_strerror(void) { return ""; } +static const char * shut_strerror(void) { return ""; } /*! * From SHUT specifications @@ -205,7 +242,7 @@ typedef struct shut_ctrltransfer_s { uint16_t wValue; uint16_t wIndex; uint16_t wLength; - + uint32_t timeout; /* in milliseconds */ /* pointer to data */ @@ -259,28 +296,63 @@ typedef union device_desc_data_t { uint8_t raw_desc[18]; } device_desc_data_t; #endif + /* Low level SHUT (Serial HID UPS Transfer) routines */ -void setline(int upsfd, int set); -int shut_synchronise(int upsfd); -int shut_wait_ack(int upsfd); -int shut_interrupt_read(int upsfd, int ep, unsigned char *bytes, - int size, int timeout); -int shut_control_msg(int upsfd, int requesttype, int request, int value, - int index, unsigned char *bytes, int size, int timeout); +/* Expected evaluated types for the API after typedefs: +static void setline(int upsfd, int set); +static int shut_synchronise(int upsfd); +static int shut_wait_ack(int upsfd); +static int shut_interrupt_read(int upsfd, int ep, unsigned char *bytes, + int size, int timeout); +static int shut_control_msg(int upsfd, int requesttype, int request, int value, + int index, unsigned char *bytes, int size, int timeout); + */ +static void setline( + usb_dev_handle upsfd, + int set); + +static int shut_synchronise(usb_dev_handle upsfd); + +static int shut_wait_ack(usb_dev_handle upsfd); + +static int shut_interrupt_read( + usb_dev_handle upsfd, + usb_ctrl_endpoint ep, + usb_ctrl_charbuf bytes, + usb_ctrl_charbufsize size, + usb_ctrl_timeout_msec timeout); + +static int shut_control_msg( + usb_dev_handle upsfd, + usb_ctrl_requesttype requesttype, + usb_ctrl_request request, + usb_ctrl_msgvalue value, + usb_ctrl_repindex index, + usb_ctrl_charbuf bytes, + usb_ctrl_charbufsize size, + usb_ctrl_timeout_msec timeout); /* Data portability */ /* realign packet data according to Endianess */ -#define BYTESWAP(in) (((in & 0xFF) << 8) + ((in & 0xFF00) >> 8)) -static void align_request(struct shut_ctrltransfer_s *ctrl ) +#define BYTESWAP(in) ((((uint16_t)in & 0x00FF) << 8) + (((uint16_t)in & 0xFF00) >> 8)) +static void align_request(struct shut_ctrltransfer_s *ctrl) { -#if WORDS_BIGENDIAN - /* Sparc/Mips/... are big endian, USB/SHUT little endian */ +#if (defined (WORDS_BIGENDIAN)) && (WORDS_BIGENDIAN) + /* Sparc/Mips/... are big endian, USB/SHUT little endian */ (*ctrl).wValue = BYTESWAP((*ctrl).wValue); (*ctrl).wIndex = BYTESWAP((*ctrl).wIndex); (*ctrl).wLength = BYTESWAP((*ctrl).wLength); +#else + NUT_UNUSED_VARIABLE(ctrl); #endif } +#if (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#if (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif /* On success, fill in the curDevice structure and return the report * descriptor length. On failure, return -1. * Note: When callback is not NULL, the report descriptor will be @@ -288,34 +360,52 @@ static void align_request(struct shut_ctrltransfer_s *ctrl ) * information. This callback should return a value > 0 if the device * is accepted, or < 1 if not. */ -int libshut_open(int *upsfd, SHUTDevice_t *curDevice, char *device_path, - int (*callback)(int upsfd, SHUTDevice_t *hd, unsigned char *rdbuf, int rdlen)) +/* Expected evaluated types for the API after typedefs: + * static int libshut_open(int *arg_upsfd, SHUTDevice_t *curDevice, char *arg_device_path, + * int (*callback)(int arg_upsfd, SHUTDevice_t *hd, + * unsigned char *rdbuf, int rdlen)) + */ +static int libshut_open( + usb_dev_handle *arg_upsfd, + SHUTDevice_t *curDevice, + char *arg_device_path, + int (*callback)(usb_dev_handle arg_upsfd, SHUTDevice_t *hd, + usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen)) { - int ret, res; - unsigned char buf[20]; + int ret, res; + /* Below we cast this buffer as sometimes containing entried of type + * "struct device_descriptor_s" or "struct my_hid_descriptor". + * Currently both of these are sized "2", and I don't see a way + * to require a "max()" of such sizes to align for generally. + */ + usb_ctrl_char buf[20] __attribute__((aligned(4))); char string[MAX_STRING_SIZE]; struct my_hid_descriptor *desc; struct device_descriptor_s *dev_descriptor; - - /* report descriptor */ - unsigned char rdbuf[MAX_REPORT_SIZE]; - int rdlen; - upsdebugx(2, "libshut_open: using port %s", device_path); + /* report descriptor */ + usb_ctrl_char rdbuf[MAX_REPORT_SIZE]; + usb_ctrl_charbufsize rdlen; + /* All devices use HID descriptor at index 0. However, some newer + * Eaton units have a light HID descriptor at index 0, and the full + * version is at index 1 (in which case, bcdDevice == 0x0202) */ + usb_ctrl_descindex hid_desc_index = 0; + + upsdebugx(2, "libshut_open: using port %s", arg_device_path); /* If device is still open, close it */ - if (*upsfd > 0) { - ser_close(*upsfd, device_path); + if (*arg_upsfd > 0) { + ser_close(*arg_upsfd, arg_device_path); } /* initialize serial port */ /* FIXME: add variable baudrate detection */ - *upsfd = ser_open(device_path); - ser_set_speed(*upsfd, device_path, B2400); - setline(*upsfd, 1); + *arg_upsfd = ser_open(arg_device_path); + ser_set_speed(*arg_upsfd, arg_device_path, B2400); + setline(*arg_upsfd, 1); /* initialise communication */ - if (!shut_synchronise(*upsfd)) + if (!shut_synchronise(*arg_upsfd)) { upsdebugx(2, "No communication with UPS"); return -1; @@ -329,8 +419,22 @@ int libshut_open(int *upsfd, SHUTDevice_t *curDevice, char *device_path, } /* Get DEVICE descriptor */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-align" +#endif +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wcast-align" +#endif dev_descriptor = (struct device_descriptor_s *)buf; - res = shut_get_descriptor(*upsfd, USB_DT_DEVICE, 0, buf, USB_DT_DEVICE_SIZE); +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic pop +#endif + res = shut_get_descriptor(*arg_upsfd, USB_DT_DEVICE, 0, buf, USB_DT_DEVICE_SIZE); /* res = shut_control_msg(devp, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, (USB_DT_DEVICE << 8) + 0, 0, buf, 0x9, SHUT_TIMEOUT); */ if (res < 0) @@ -361,9 +465,10 @@ int libshut_open(int *upsfd, SHUTDevice_t *curDevice, char *device_path, curDevice->VendorID = dev_descriptor->idVendor; curDevice->ProductID = dev_descriptor->idProduct; curDevice->Bus = strdup("serial"); + curDevice->bcdDevice = dev_descriptor->bcdDevice; curDevice->Vendor = strdup("Eaton"); if (dev_descriptor->iManufacturer) { - ret = shut_get_string_simple(*upsfd, dev_descriptor->iManufacturer, + ret = shut_get_string_simple(*arg_upsfd, dev_descriptor->iManufacturer, string, MAX_STRING_SIZE); if (ret > 0) { curDevice->Vendor = strdup(string); @@ -372,7 +477,7 @@ int libshut_open(int *upsfd, SHUTDevice_t *curDevice, char *device_path, /* ensure iProduct retrieval */ if (dev_descriptor->iProduct) { - ret = shut_get_string_simple(*upsfd, dev_descriptor->iProduct, string, MAX_STRING_SIZE); + ret = shut_get_string_simple(*arg_upsfd, dev_descriptor->iProduct, string, MAX_STRING_SIZE); } else { ret = 0; } @@ -383,7 +488,7 @@ int libshut_open(int *upsfd, SHUTDevice_t *curDevice, char *device_path, } if (dev_descriptor->iSerialNumber) { - ret = shut_get_string_simple(*upsfd, dev_descriptor->iSerialNumber, string, 0x25); + ret = shut_get_string_simple(*arg_upsfd, dev_descriptor->iSerialNumber, string, 0x25); } else { ret = 0; } @@ -400,11 +505,31 @@ int libshut_open(int *upsfd, SHUTDevice_t *curDevice, char *device_path, upsdebugx(2, "- Product: %s", curDevice->Product); upsdebugx(2, "- Serial Number: %s", curDevice->Serial); upsdebugx(2, "- Bus: %s", curDevice->Bus); + upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice); upsdebugx(2, "Device matches"); + if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { + upsdebugx(1, "Eaton device v2.02. Using full report descriptor"); + hid_desc_index = 1; + } + /* Get HID descriptor */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-align" +#endif +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wcast-align" +#endif desc = (struct my_hid_descriptor *)buf; - res = shut_get_descriptor(*upsfd, USB_DT_HID, 0, buf, 0x9); +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic pop +#endif + res = shut_get_descriptor(*arg_upsfd, USB_DT_HID, hid_desc_index, buf, 0x9); /* res = shut_control_msg(devp, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, (USB_DT_HID << 8) + 0, 0, buf, 0x9, SHUT_TIMEOUT); */ @@ -418,42 +543,92 @@ int libshut_open(int *upsfd, SHUTDevice_t *curDevice, char *device_path, { upsdebugx(2, "HID descriptor too short (expected %d, got %d)", 8, res); return -1; - } + } /* USB_LE16_TO_CPU(desc->wDescriptorLength); */ - desc->wDescriptorLength = buf[7] | (buf[8] << 8); + desc->wDescriptorLength = (uint16_t)(buf[7]); + desc->wDescriptorLength |= (((uint16_t)buf[8]) << 8); upsdebugx(2, "HID descriptor retrieved (Reportlen = %u)", desc->wDescriptorLength); -/* if (!dev->config) { +/* + if (!dev->config) { upsdebugx(2, " Couldn't retrieve descriptors"); return -1; - }*/ + } +*/ rdlen = desc->wDescriptorLength; - if (rdlen > (int)sizeof(rdbuf)) { - upsdebugx(2, "HID descriptor too long %d (max %d)", rdlen, (int)sizeof(rdbuf)); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if (rdlen > sizeof(rdbuf) || rdlen > INT_MAX) { +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + upsdebugx(2, + "HID descriptor too long %" PRI_NUT_USB_CTRL_CHARBUFSIZE + " (max %zu)", + rdlen, sizeof(rdbuf)); return -1; } /* Get REPORT descriptor */ - res = shut_get_descriptor(*upsfd, USB_DT_REPORT, 0, rdbuf, rdlen); + res = shut_get_descriptor(*arg_upsfd, USB_DT_REPORT, hid_desc_index, rdbuf, rdlen); /* res = shut_control_msg(devp, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, - (USB_DT_REPORT << 8) + 0, 0, ReportDesc, + (USB_DT_REPORT << 8) + 0, 0, ReportDesc, desc->wDescriptorLength, SHUT_TIMEOUT); */ - if (res == rdlen) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if (res >= 0 && (uintmax_t)res < (uintmax_t)SIZE_MAX && (size_t)res == rdlen) +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif { - res = callback(*upsfd, curDevice, rdbuf, rdlen); + res = callback(*arg_upsfd, curDevice, rdbuf, rdlen); if (res < 1) { upsdebugx(2, "Caller doesn't like this device"); return -1; } - upsdebugx(2, "Report descriptor retrieved (Reportlen = %d)", rdlen); + upsdebugx(2, + "Report descriptor retrieved (Reportlen = %" + PRI_NUT_USB_CTRL_CHARBUFSIZE ")", rdlen); upsdebugx(2, "Found HID device"); fflush(stdout); - return rdlen; + return (int)rdlen; } if (res < 0) @@ -462,37 +637,53 @@ int libshut_open(int *upsfd, SHUTDevice_t *curDevice, char *device_path, } else { - upsdebugx(2, "Report descriptor too short (expected %d, got %d)", rdlen, res); + upsdebugx(2, + "Report descriptor too short (expected %" + PRI_NUT_USB_CTRL_CHARBUFSIZE ", got %d)", + rdlen, res); } - - upsdebugx(2, "No appropriate HID device found"); + + upsdebugx(2, "libshut: No appropriate HID device found"); fflush(stdout); return -1; } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) ) +# pragma GCC diagnostic pop +#endif -void libshut_close(int upsfd) +/* Expected evaluated types for the API after typedefs: + * static void libshut_close(int arg_upsfd) + */ +static void libshut_close(usb_dev_handle arg_upsfd) { - if (upsfd < 1) { + if (arg_upsfd < 1) { return; } - ser_close(upsfd, NULL); + ser_close(arg_upsfd, NULL); } -/* return the report of ID=type in report +/* return the report of ID=type in report * return -1 on failure, report length on success */ -int libshut_get_report(int upsfd, int ReportId, - unsigned char *raw_buf, int ReportSize ) +/* Expected evaluated types for the API after typedefs: + * static int libshut_get_report(int arg_upsfd, int ReportId, + * unsigned char *raw_buf, int ReportSize) + */ +static int libshut_get_report( + usb_dev_handle arg_upsfd, + usb_ctrl_repindex ReportId, + usb_ctrl_charbuf raw_buf, + usb_ctrl_charbufsize ReportSize) { - if (upsfd < 1) { + if (arg_upsfd < 1) { return 0; } upsdebugx(4, "Entering libshut_get_report"); - return shut_control_msg(upsfd, + return shut_control_msg(arg_upsfd, REQUEST_TYPE_GET_REPORT, /* == USB_ENDPOINT_IN + USB_TYPE_CLASS + USB_RECIP_INTERFACE, */ 0x01, @@ -501,39 +692,61 @@ int libshut_get_report(int upsfd, int ReportId, } /* return ReportSize upon success ; -1 otherwise */ -int libshut_set_report(int upsfd, int ReportId, - unsigned char *raw_buf, int ReportSize ) +/* Expected evaluated types for the API after typedefs: + * static int libshut_set_report(int arg_upsfd, int ReportId, + * unsigned char *raw_buf, int ReportSize) + */ +static int libshut_set_report( + usb_dev_handle arg_upsfd, + usb_ctrl_repindex ReportId, + usb_ctrl_charbuf raw_buf, + usb_ctrl_charbufsize ReportSize) { int ret; - - if (upsfd < 1) { + + if (arg_upsfd < 1) { return 0; } - - upsdebugx(1, "Entering libshut_set_report (report %x, len %i)", + + upsdebugx(1, + "Entering libshut_set_report (report %x, " + "len %" PRI_NUT_USB_CTRL_CHARBUFSIZE ")", ReportId, ReportSize); + if ((uintmax_t)ReportSize > (uintmax_t)INT_MAX) { + upsdebugx(1, "%s: ReportSize exceeds INT_MAX", __func__); + return -1; + } + upsdebug_hex (4, "==> Report after set", raw_buf, ReportSize); - ret = shut_control_msg(upsfd, + ret = shut_control_msg(arg_upsfd, REQUEST_TYPE_SET_REPORT, /* == USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, */ 0x09, ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ 0, raw_buf, ReportSize, SHUT_TIMEOUT); - - return ((ret == 0) ? ReportSize : ret); + + return ((ret == 0) ? (int)ReportSize : ret); } -int libshut_get_string(int upsfd, int StringIdx, char *buf, size_t buflen) +/* Expected evaluated types for the API after typedefs: + * static int libshut_get_string(int arg_upsfd, + * int StringIdx, char *buf, size_t buflen) + */ +static int libshut_get_string( + usb_dev_handle arg_upsfd, + usb_ctrl_strindex StringIdx, + char *buf, + usb_ctrl_charbufsize buflen) { int ret; - - if (upsfd < 1) { + + if (arg_upsfd < 1) { return -1; } - ret = shut_get_string_simple(upsfd, StringIdx, buf, buflen); + ret = shut_get_string_simple(arg_upsfd, StringIdx, buf, buflen); if (ret > 0) upsdebugx(2, "-> String: %s (len = %i/%i)", buf, ret, (int)buflen); else @@ -542,22 +755,29 @@ int libshut_get_string(int upsfd, int StringIdx, char *buf, size_t buflen) return ret; } -int libshut_get_interrupt(int upsfd, unsigned char *buf, - int bufsize, int timeout) +/* Expected evaluated types for the API after typedefs: + * static int libshut_get_interrupt(int arg_upsfd, unsigned char *buf, + * int bufsize, int timeout) + */ +static int libshut_get_interrupt( + usb_dev_handle arg_upsfd, + usb_ctrl_charbuf buf, + usb_ctrl_charbufsize bufsize, + usb_ctrl_timeout_msec timeout) { int ret; - if (upsfd < 1) { + if (arg_upsfd < 1) { return -1; } /* FIXME: hardcoded interrupt EP => need to get EP descr for IF descr */ - ret = shut_interrupt_read(upsfd, 0x81, buf, bufsize, timeout); + ret = shut_interrupt_read(arg_upsfd, 0x81, buf, bufsize, timeout); if (ret > 0) upsdebugx(6, " ok"); else upsdebugx(6, " none (%i)", ret); - + return ret; } @@ -578,41 +798,47 @@ shut_communication_subdriver_t shut_subdriver = { /* * set RTS to on and DTR to off - * + * * set : 1 to set comm * set : 0 to stop commupsh. */ -void setline(int upsfd, int set) +/* Expected evaluated types for the API after typedefs: + * void setline(int arg_upsfd, int set) + */ +void setline(usb_dev_handle arg_upsfd, int set) { - if (upsfd < 1) { + if (arg_upsfd < 1) { return; } upsdebugx(3, "entering setline(%i)", set); if (set == 1) { - ser_set_dtr(upsfd, 0); - ser_set_rts(upsfd, 1); + ser_set_dtr(arg_upsfd, 0); + ser_set_rts(arg_upsfd, 1); } else { - ser_set_dtr(upsfd, 1); - ser_set_rts(upsfd, 0); + ser_set_dtr(arg_upsfd, 1); + ser_set_rts(arg_upsfd, 0); } } /***************************************************************************** * shut_synchronise () - * + * * initiate communication with the UPS * * return TRUE on success, FALSE on failure * *****************************************************************************/ -int shut_synchronise(int upsfd) +/* Expected evaluated types for the API after typedefs: + * int shut_synchronise(int arg_upsfd) + */ +int shut_synchronise(usb_dev_handle arg_upsfd) { int retCode = 0; - u_char c = SHUT_SYNC, reply; + unsigned char c = SHUT_SYNC_OFF, reply; int try; - + upsdebugx (2, "entering shut_synchronise()"); reply = '\0'; @@ -637,13 +863,13 @@ int shut_synchronise(int upsfd) { upsdebugx (3, "Syncing communication (try %i)", try); - if ((ser_send_char(upsfd, c)) == -1) + if ((ser_send_char(arg_upsfd, c)) == -1) { upsdebugx (3, "Communication error while writing to port"); continue; } - ser_get_char(upsfd, &reply, 1, 0); + ser_get_char(arg_upsfd, &reply, 1, 0); if (reply == c) { upsdebugx (3, "Syncing and notification setting done"); @@ -656,37 +882,49 @@ int shut_synchronise(int upsfd) /*! * Compute a SHUT checksum for the packet "buf" */ -u_char shut_checksum(const u_char *buf, int bufsize) +/* Expected evaluated types for the API after typedefs: + * static unsigned char shut_checksum(const unsigned char *buf, int bufsize) + */ +static unsigned char shut_checksum( + const usb_ctrl_charbuf buf, + usb_ctrl_charbufsize bufsize) { - int i; - u_char chk=0; - + usb_ctrl_charbufsize i; + unsigned char chk=0; + for(i=0; i0 && Retry<3) { /* if(serial_read (SHUT_TIMEOUT, &Start[0]) > 0) */ - if(ser_get_char(upsfd, &Start[0], SHUT_TIMEOUT/1000, 0) > 0) + if(ser_get_char(arg_upsfd, &Start[0], SHUT_TIMEOUT/1000, 0) > 0) { /* sdata.shut_pkt.bType = Start[0]; */ if(Start[0]==SHUT_SYNC) @@ -695,25 +933,39 @@ int shut_packet_recv(int upsfd, u_char *Buf, int datalen) memcpy(Buf, Start, 1); return 1; } - else + else if(Start[0]==SHUT_SYNC_OFF) + { + upsdebugx (4, "received SYNC_OFF token"); + memcpy(Buf, Start, 1); + return 1; + } + else { /* if((serial_read (SHUT_TIMEOUT, &Start[1]) > 0) && */ - if( (ser_get_char(upsfd, &Start[1], SHUT_TIMEOUT/1000, 0) > 0) && + if( (ser_get_char(arg_upsfd, &Start[1], SHUT_TIMEOUT/1000, 0) > 0) && ((Start[1]>>4)==(Start[1]&0x0F))) { upsdebug_hex(4, "Receive", Start, 2); Size=Start[1]&0x0F; + if( Size > 8 ) { + upsdebugx (4, + "shut_packet_recv: invalid frame size = %d", + Size); + ser_send_char(arg_upsfd, SHUT_NOK); + Retry++; + break; + } /* sdata.shut_pkt.bLength = Size; */ for(recv=0;recv OK", Chk[0]); @@ -722,63 +974,91 @@ int shut_packet_recv(int upsfd, u_char *Buf, int datalen) Buf+=Size; Pos+=Size; Retry=0; - - ser_send_char(upsfd, SHUT_OK); + + ser_send_char(arg_upsfd, SHUT_OK); /* shut_token_send(SHUT_OK); */ - if(Start[0]&SHUT_PKT_LAST) + /* Check if there are more data to receive */ + if((Start[0] & 0xf0) == SHUT_PKT_LAST) { - if ((Start[0]&SHUT_PKT_LAST) == SHUT_TYPE_NOTIFY) + /* Check if it's a notification */ + if ((Start[0] & 0x0f) == SHUT_TYPE_NOTIFY) { /* TODO: process notification (dropped for now) */ upsdebugx (4, "=> notification"); datalen+=Pos; Pos=0; } - else - return Pos; + else { + return Pos; + } } else - upsdebugx (4, "need more data (%i)!", datalen); + upsdebugx (4, + "need more data (%" PRI_NUT_USB_CTRL_CHARBUFSIZE ")!", + datalen); } else { upsdebugx (4, "shut_checksum: %02x => NOK", Chk[0]); - ser_send_char(upsfd, SHUT_NOK); + ser_send_char(arg_upsfd, SHUT_NOK); /* shut_token_send(SHUT_NOK); */ Retry++; } } - else + else { return 0; + } } } - else + else { Retry++; + } } /* while */ - + return 0; } /**********************************************************************/ -int shut_interrupt_read(int upsfd, int ep, unsigned char *bytes, int size, - int timeout) +/* Expected evaluated types for the API after typedefs: + * static int shut_interrupt_read(int arg_upsfd, int ep, + * unsigned char *bytes, int size, + * int timeout) + */ +static int shut_interrupt_read( + usb_dev_handle arg_upsfd, + usb_ctrl_endpoint ep, + usb_ctrl_charbuf bytes, + usb_ctrl_charbufsize size, + usb_ctrl_timeout_msec timeout) { /* usleep(timeout * 1000); - */ +*/ /* FIXME: to be written */ + NUT_UNUSED_VARIABLE(arg_upsfd); + NUT_UNUSED_VARIABLE(ep); + NUT_UNUSED_VARIABLE(bytes); + NUT_UNUSED_VARIABLE(size); + NUT_UNUSED_VARIABLE(timeout); return 0; } /**********************************************************************/ -int shut_get_string_simple(int upsfd, int index, - char *buf, size_t buflen) +/* Expected evaluated types for the API after typedefs: + * static int shut_get_string_simple(int arg_upsfd, int index, + * char *buf, size_t buflen) + */ +static int shut_get_string_simple( + usb_dev_handle arg_upsfd, + usb_ctrl_strindex index, + char *buf, + usb_ctrl_charbufsize buflen) { - unsigned char tbuf[255]; /* Some devices choke on size > 255 */ + usb_ctrl_char tbuf[255]; /* Some devices choke on size > 255 */ int ret, si, di; - - ret = shut_control_msg(upsfd, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, + + ret = shut_control_msg(arg_upsfd, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) + index, 0x0, tbuf, buflen, SHUT_TIMEOUT); if (ret < 0) return ret; @@ -798,20 +1078,20 @@ int shut_get_string_simple(int upsfd, int index, if (tbuf[si + 1]) /* high byte */ buf[di++] = '?'; else - buf[di++] = tbuf[si]; + buf[di++] = (char)tbuf[si]; } buf[di] = 0; return di; } - -/* + +/* * Human Interface Device (HID) functions *********************************************************************/ /********************************************************************** - * shut_get_descriptor(int desctype, u_char *pkt) - * + * shut_get_descriptor(int desctype, usb_ctrl_charbuf pkt) + * * get descriptor specified by DescType and return it in Buf * * desctype - from shutdataType @@ -820,28 +1100,50 @@ int shut_get_string_simple(int upsfd, int index, * return 0 on success, -1 on failure, -2 on NACK * *********************************************************************/ -int shut_get_descriptor(int upsfd, unsigned char type, - unsigned char index, void *buf, int size) +/* Expected evaluated types for the API after typedefs: + * static int shut_get_descriptor(int arg_upsfd, unsigned char type, + * unsigned char index, void *buf, int size) + */ +static int shut_get_descriptor( + usb_dev_handle arg_upsfd, + usb_ctrl_requesttype type, + usb_ctrl_descindex index, + void *buf, + usb_ctrl_charbufsize size) { memset(buf, 0, size); - upsdebugx (2, "entering shut_get_descriptor(n %02x, %i)", type, size); + upsdebugx (2, + "entering shut_get_descriptor(n %02x, %" PRI_NUT_USB_CTRL_CHARBUFSIZE ")", + type, size); - return shut_control_msg(upsfd, USB_ENDPOINT_IN+(type>=USB_DT_HID?1:0), + return shut_control_msg(arg_upsfd, USB_ENDPOINT_IN+(type>=USB_DT_HID?1:0), USB_REQ_GET_DESCRIPTOR, (type << 8) + index, 0, buf, size, SHUT_TIMEOUT); } /* Take care of a SHUT transfer (sending and receiving data) */ -int shut_control_msg(int upsfd, int requesttype, int request, - int value, int index, unsigned char *bytes, int size, int timeout) +/* Expected evaluated types for the API after typedefs: + * static int shut_control_msg(int arg_upsfd, int requesttype, int request, + * int value, int index, unsigned char *bytes, int size, + * int timeout) + */ +static int shut_control_msg( + usb_dev_handle arg_upsfd, + usb_ctrl_requesttype requesttype, + usb_ctrl_request request, + usb_ctrl_msgvalue value, + usb_ctrl_repindex index, + usb_ctrl_charbuf bytes, + usb_ctrl_charbufsize size, + usb_ctrl_timeout_msec timeout) { unsigned char shut_pkt[11]; short Retry=1, set_pass = -1; - short data_size, remaining_size = size; + usb_ctrl_charbufsize data_size, remaining_size = size; int i; struct shut_ctrltransfer_s ctrl; int ret = 0; - + upsdebugx (3, "entering shut_control_msg"); /* deal for set requests */ @@ -852,14 +1154,43 @@ int shut_control_msg(int upsfd, int requesttype, int request, remaining_size+= 8; } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif + /* Note: checking against limits of protocol struct fields, + * not against USB_CTRL_REQUEST_MAX et al, which are mostly int + */ + if (requesttype < 0 || (uintmax_t)requesttype > UINT8_MAX + || request < 0 || (uintmax_t)request > UINT8_MAX + || value < 0 || (uintmax_t)value > UINT16_MAX + || index < 0 || (uintmax_t)index > UINT16_MAX + || (uintmax_t)size > UINT16_MAX + || (uintmax_t)timeout > UINT32_MAX + ) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic pop +#endif + upsdebugx (1, "%s: input values out of range", __func__); + return -1; + } + /* build the control request */ - ctrl.bRequestType = requesttype; - ctrl.bRequest = request; - ctrl.wValue = value; - ctrl.wIndex = index; - ctrl.wLength = size; + ctrl.bRequestType = (uint8_t)requesttype; + ctrl.bRequest = (uint8_t)request; + ctrl.wValue = (uint16_t)value; + ctrl.wIndex = (uint16_t)index; + ctrl.wLength = (uint16_t)size; ctrl.data = bytes; - ctrl.timeout = timeout; + ctrl.timeout = (uint32_t)timeout; /* in milliseconds */ align_request(&ctrl); @@ -876,28 +1207,68 @@ int shut_control_msg(int upsfd, int requesttype, int request, upsdebug_hex(4, "data", bytes, data_size); } } - else - data_size = (size >= 8) ? 8 : remaining_size; - + else { + /* Always 8 bytes payload for GET_REPORT with SHUT */ + data_size = 8; + } + /* Forge the SHUT Frame */ shut_pkt[0] = SHUT_TYPE_REQUEST + ( ((requesttype == REQUEST_TYPE_SET_REPORT) && (remaining_size>8))? 0 : SHUT_PKT_LAST); - shut_pkt[1] = (data_size<<4) + data_size; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if (data_size < 0 || data_size > UCHAR_MAX) { +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic pop +#endif + upsdebugx(1, "%s: ERROR: data_size %" PRI_NUT_USB_CTRL_CHARBUFSIZE + " is out of range for SHUT packet", + __func__, data_size); + return -1; + } + if (data_size > 0x0F) { + upsdebugx(1, "%s: WARNING: data_size %" PRI_NUT_USB_CTRL_CHARBUFSIZE + " may be too large for SHUT packet?", + __func__, data_size); + /* Do not abort here - maybe there is intentional maths + * in the protocol with overlapping/shifted-away numbers? + */ + } + shut_pkt[1] = (unsigned char)(data_size<<4) + (unsigned char)data_size; if ( (requesttype == REQUEST_TYPE_SET_REPORT) && (remaining_size < 8) ) memcpy(&shut_pkt[2], bytes, data_size); /* we need to send ctrl.data */ else memcpy(&shut_pkt[2], &ctrl, 8); - shut_pkt[(data_size+3) - 1] = shut_checksum(&shut_pkt[2], data_size); - + shut_pkt[(data_size+3) - 1] = shut_checksum(&shut_pkt[2], (unsigned char)data_size); + /* Packets need only to be sent once * NACK handling should take care of the rest */ if (Retry == 1) { - ser_send_buf(upsfd, shut_pkt, data_size+3); + ser_send_buf(arg_upsfd, shut_pkt, data_size+3); upsdebug_hex(3, "shut_control_msg", shut_pkt, data_size+3); /* serial_send (shut_pkt, data_size+3); */ } - i = shut_wait_ack (upsfd); + i = shut_wait_ack (arg_upsfd); switch (i) { case 0: @@ -908,40 +1279,45 @@ int shut_control_msg(int upsfd, int requesttype, int request, Retry=1; break; + case -1: if (Retry >= MAX_TRY) { upsdebugx(2, "Max tries reached while waiting for ACK, still getting errors"); - + /* try to resync, and give one more try */ Retry--; - shut_synchronise(upsfd); + shut_synchronise(arg_upsfd); return i; } else { upsdebugx(4, "Retry = %i", Retry); /* Send a NACK to get a resend from the UPS */ - ser_send_char(upsfd, SHUT_NOK); + ser_send_char(arg_upsfd, SHUT_NOK); Retry++; } break; + case -3: /* FIXME: notification caught => to be processed */ /* Send a NACK for the moment, to get a resend from the UPS */ - ser_send_char(upsfd, SHUT_NOK); + ser_send_char(arg_upsfd, SHUT_NOK); Retry++; + goto fallthrough_default; + default: - ; + fallthrough_default: + break; } } if (remaining_size != 0) return -1; - + /* now receive data, except for SET_REPORT */ if (requesttype != REQUEST_TYPE_SET_REPORT) - ret = shut_packet_recv (upsfd, bytes, size); + ret = shut_packet_recv (arg_upsfd, bytes, size); return ret; } @@ -952,14 +1328,17 @@ int shut_control_msg(int upsfd, int requesttype, int request, * wait for an ACK packet * * returns 0 on success, -1 on error, -2 on NACK, -3 on NOTIFICATION - * + * *********************************************************************/ -int shut_wait_ack(int upsfd) +/* Expected evaluated types for the API after typedefs: + * int shut_wait_ack(int arg_upsfd) + */ +int shut_wait_ack(usb_dev_handle arg_upsfd) { int retCode = -1; - u_char c = '\0'; + unsigned char c = '\0'; - ser_get_char(upsfd, &c, SHUT_TIMEOUT/1000, 0); + ser_get_char(arg_upsfd, &c, SHUT_TIMEOUT/1000, 0); if (c == SHUT_OK) { upsdebugx (2, "shut_wait_ack(): ACK received"); @@ -970,13 +1349,13 @@ int shut_wait_ack(int upsfd) upsdebugx (2, "shut_wait_ack(): NACK received"); retCode = -2; } - else if ((c & SHUT_PKT_LAST) == SHUT_TYPE_NOTIFY) + else if ((c & 0x0f) == SHUT_TYPE_NOTIFY) { upsdebugx (2, "shut_wait_ack(): NOTIFY received"); retCode = -3; } else if (c == '\0') upsdebugx (2, "shut_wait_ack(): Nothing received"); - + return retCode; } diff --git a/drivers/libshut.h b/drivers/libshut.h index 48f48bb..c39fd17 100644 --- a/drivers/libshut.h +++ b/drivers/libshut.h @@ -24,13 +24,88 @@ * * -------------------------------------------------------------------------- */ -#ifndef LIBSHUT_H -#define LIBSHUT_H +#ifndef NUT_LIBSHUT_H_SEEN +#define NUT_LIBSHUT_H_SEEN 1 #include "main.h" /* for subdrv_info_t */ -#include "nut_stdint.h" /* for uint16_t */ +#include "nut_stdint.h" /* for uint16_t, size_t, PRIsize etc. */ -extern upsdrv_info_t comm_upsdrv_info; +extern upsdrv_info_t comm_upsdrv_info; + +/* These typedefs are also named in usb-common.h (=> nut_libusb.h), adhering + * to one or another libusb API version. For consistency of "ifdef SHUT_MODE" + * handling in libhid.c and some drivers, these symbolic names are used in + * all the headers and are expected to match binary code of object files at + * (monolithic) driver build time. + * + * The MIN/MAX definitions here are primarily to generalize range-check + * code (especially if anything is done outside the libraries). + * FIXME: It may make sense to constrain the limits to lowest common + * denominator that should fit alll of libusb-0.1, libusb-1.0 and libshut, + * so that any build of the practical (driver) code knows to not exceed + * any use-case. + * + * Types below were mined from existing method signatures; see also the + * my_hid_descriptor struct in libshut.c for practical fixed-size types. + */ + +/* Essentially the file descriptor type, "int" - as in ser_get_char() etc.: */ +typedef int usb_dev_handle; + +/* Originally "int" cast to "uint8_t" in shut_control_msg(), + * and "unsigned char" in shut_get_descriptor() */ +typedef unsigned char usb_ctrl_requesttype; +#define USB_CTRL_REQUESTTYPE_MIN 0 +#define USB_CTRL_REQUESTTYPE_MAX UCHAR_MAX + +typedef int usb_ctrl_request; +#define USB_CTRL_REQUEST_MIN INT_MIN +#define USB_CTRL_REQUEST_MAX INT_MAX + +typedef int usb_ctrl_endpoint; +#define USB_CTRL_ENDPOINT_MIN INT_MIN +#define USB_CTRL_ENDPOINT_MAX INT_MAX + +typedef int usb_ctrl_msgvalue; +#define USB_CTRL_MSGVALUE_MIN INT_MIN +#define USB_CTRL_MSGVALUE_MAX INT_MAX + +typedef int usb_ctrl_repindex; +#define USB_CTRL_REPINDEX_MIN INT_MIN +#define USB_CTRL_REPINDEX_MAX INT_MAX + +typedef int usb_ctrl_strindex; +#define USB_CTRL_STRINDEX_MIN INT_MIN +#define USB_CTRL_STRINDEX_MAX INT_MAX + +typedef unsigned char usb_ctrl_descindex; +#define USB_CTRL_DESCINDEX_MIN 0 +#define USB_CTRL_DESCINDEX_MAX UCHAR_MAX + +/* Here MIN/MAX should not matter much, type mostly used for casting */ +typedef unsigned char* usb_ctrl_charbuf; +typedef unsigned char usb_ctrl_char; +#define USB_CTRL_CHAR_MIN 0 +#define USB_CTRL_CHAR_MAX UCHAR_MAX + +typedef size_t usb_ctrl_charbufsize; /*typedef int usb_ctrl_charbufsize;*/ +#define USB_CTRL_CHARBUFSIZE_MIN 0 +#define USB_CTRL_CHARBUFSIZE_MAX SIZE_MAX +#define PRI_NUT_USB_CTRL_CHARBUFSIZE PRIsize + +typedef int usb_ctrl_timeout_msec; /* in milliseconds */ +#define USB_CTRL_TIMEOUTMSEC_MIN INT_MIN +#define USB_CTRL_TIMEOUTMSEC_MAX INT_MAX + +/* Same error-code definitions as in usb-common.h for libusb-0.1 API */ +#define ERROR_ACCESS -EACCES +#define ERROR_BUSY -EBUSY +#define ERROR_IO -EIO +#define ERROR_NO_DEVICE -ENODEV +#define ERROR_NOT_FOUND -ENOENT +#define ERROR_OVERFLOW -EOVERFLOW +#define ERROR_PIPE -EPIPE +#define ERROR_TIMEOUT -ETIMEDOUT /*! * SHUTDevice_t: Describe a SHUT device. This structure contains exactly @@ -42,12 +117,14 @@ extern upsdrv_info_t comm_upsdrv_info; * corresponding string did not exist or could not be retrieved. */ typedef struct SHUTDevice_s { - uint16_t VendorID; /*!< Device's Vendor ID */ - uint16_t ProductID; /*!< Device's Product ID */ - char* Vendor; /*!< Device's Vendor Name */ - char* Product; /*!< Device's Product Name */ - char* Serial; /* Product serial number */ - char* Bus; /* Bus name, e.g. "003" */ + uint16_t VendorID; /*!< Device's Vendor ID */ + uint16_t ProductID; /*!< Device's Product ID */ + char* Vendor; /*!< Device's Vendor Name */ + char* Product; /*!< Device's Product Name */ + char* Serial; /*!< Product serial number */ + char* Bus; /*!< Bus name, e.g. "003" */ + uint16_t bcdDevice; /*!< Device release number */ + char *Device; /*!< Device name on the bus, e.g. "001" */ } SHUTDevice_t; /*! @@ -55,20 +132,28 @@ typedef struct SHUTDevice_s { */ typedef struct shut_communication_subdriver_s { const char *name; /* name of this subdriver */ - const char *version; /* version of this subdriver */ - int (*open)(int *upsfd, /* try to open the next available */ + const char *version; /* version of this subdriver */ + + int (*open)(usb_dev_handle *upsfd, /* try to open the next available */ SHUTDevice_t *curDevice, /* device matching USBDeviceMatcher_t */ char *device_path, - int (*callback)(int upsfd, SHUTDevice_t *hd, unsigned char *rdbuf, int rdlen)); - void (*close)(int upsfd); - int (*get_report)(int upsfd, int ReportId, - unsigned char *raw_buf, int ReportSize ); - int (*set_report)(int upsfd, int ReportId, - unsigned char *raw_buf, int ReportSize ); - int (*get_string)(int upsfd, - int StringIdx, char *buf, size_t buflen); - int (*get_interrupt)(int upsfd, - unsigned char *buf, int bufsize, int timeout); + int (*callback)(usb_dev_handle upsfd, SHUTDevice_t *hd, + usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen)); + + void (*close)(usb_dev_handle upsfd); + + int (*get_report)(usb_dev_handle upsfd, usb_ctrl_repindex ReportId, + usb_ctrl_charbuf raw_buf, usb_ctrl_charbufsize ReportSize); + + int (*set_report)(usb_dev_handle upsfd, usb_ctrl_repindex ReportId, + usb_ctrl_charbuf raw_buf, usb_ctrl_charbufsize ReportSize); + + int (*get_string)(usb_dev_handle upsfd, + usb_ctrl_strindex StringIdx, char *buf, usb_ctrl_charbufsize buflen); + + int (*get_interrupt)(usb_dev_handle upsfd, + usb_ctrl_charbuf buf, usb_ctrl_charbufsize bufsize, + usb_ctrl_timeout_msec timeout); } shut_communication_subdriver_t; extern shut_communication_subdriver_t shut_subdriver; @@ -84,4 +169,4 @@ extern shut_communication_subdriver_t shut_subdriver; /* Ellipse models */ #define DEFAULT_NOTIFICATION COMPLETE_NOTIFICATION -#endif /* LIBSHUT_H */ +#endif /* NUT_LIBSHUT_H_SEEN */ diff --git a/drivers/libusb.c b/drivers/libusb.c deleted file mode 100644 index 50bfc7f..0000000 --- a/drivers/libusb.c +++ /dev/null @@ -1,492 +0,0 @@ -/*! - * @file libusb.c - * @brief HID Library - Generic USB communication sub driver (using libusb) - * - * @author Copyright (C) - * 2003 - 2007 Arnaud Quette - * 2005 - 2007 Peter Selinger - * - * This program is sponsored by MGE UPS SYSTEMS - opensource.mgeups.com - * - * The logic of this file is ripped from mge-shut driver (also from - * Arnaud Quette), which is a "HID over serial link" UPS driver for - * Network UPS Tools - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * -------------------------------------------------------------------------- */ - -#include "config.h" /* for HAVE_USB_DETACH_KERNEL_DRIVER_NP flag */ -#include "common.h" /* for xmalloc, upsdebugx prototypes */ -#include "usb-common.h" -#include "libusb.h" - -/* USB standard state 5000, but we've decreased it to - * improve reactivity */ -#define USB_TIMEOUT 4000 - -#define USB_DRIVER_NAME "USB communication driver" -#define USB_DRIVER_VERSION "0.31" - -/* driver description structure */ -upsdrv_info_t comm_upsdrv_info = { - USB_DRIVER_NAME, - USB_DRIVER_VERSION, - NULL, - 0, - { NULL } -}; - -#define MAX_REPORT_SIZE 0x1800 - -static void libusb_close(usb_dev_handle *udev); - -/* From usbutils: workaround libusb API goofs: "byte" should never be sign extended; - * using "char" is trouble. Likewise, sizes should never be negative. - */ - -static inline int typesafe_control_msg(usb_dev_handle *dev, - unsigned char requesttype, unsigned char request, - int value, int index, - unsigned char *bytes, unsigned size, int timeout) -{ - return usb_control_msg(dev, requesttype, request, value, index, - (char *) bytes, (int) size, timeout); -} - -/* invoke matcher against device */ -static inline int matches(USBDeviceMatcher_t *matcher, USBDevice_t *device) { - if (!matcher) { - return 1; - } - return matcher->match_function(device, matcher->privdata); -} - -#define usb_control_msg typesafe_control_msg - -/* On success, fill in the curDevice structure and return the report - * descriptor length. On failure, return -1. - * Note: When callback is not NULL, the report descriptor will be - * passed to this function together with the udev and USBDevice_t - * information. This callback should return a value > 0 if the device - * is accepted, or < 1 if not. If it isn't accepted, the next device - * (if any) will be tried, until there are no more devices left. - */ -static int libusb_open(usb_dev_handle **udevp, USBDevice_t *curDevice, USBDeviceMatcher_t *matcher, - int (*callback)(usb_dev_handle *udev, USBDevice_t *hd, unsigned char *rdbuf, int rdlen)) -{ -#ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP - int retries; -#endif - int rdlen1, rdlen2; /* report descriptor length, method 1+2 */ - USBDeviceMatcher_t *m; - struct usb_device *dev; - struct usb_bus *bus; - usb_dev_handle *udev; - struct usb_interface_descriptor *iface; - - int ret, res; - unsigned char buf[20]; - unsigned char *p; - char string[256]; - int i; - - /* report descriptor */ - unsigned char rdbuf[MAX_REPORT_SIZE]; - int rdlen; - - /* libusb base init */ - usb_init(); - usb_find_busses(); - usb_find_devices(); - -#ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ - /* Causes a double free corruption in linux if device is detached! */ - libusb_close(*udevp); -#endif - - for (bus = usb_busses; bus; bus = bus->next) { - for (dev = bus->devices; dev; dev = dev->next) { - upsdebugx(2, "Checking device (%04X/%04X) (%s/%s)", dev->descriptor.idVendor, - dev->descriptor.idProduct, bus->dirname, dev->filename); - - /* supported vendors are now checked by the - supplied matcher */ - - /* open the device */ - *udevp = udev = usb_open(dev); - if (!udev) { - upsdebugx(2, "Failed to open device, skipping. (%s)", usb_strerror()); - continue; - } - - /* collect the identifying information of this - device. Note that this is safe, because - there's no need to claim an interface for - this (and therefore we do not yet need to - detach any kernel drivers). */ - - free(curDevice->Vendor); - free(curDevice->Product); - free(curDevice->Serial); - free(curDevice->Bus); - memset(curDevice, '\0', sizeof(*curDevice)); - - curDevice->VendorID = dev->descriptor.idVendor; - curDevice->ProductID = dev->descriptor.idProduct; - curDevice->Bus = strdup(bus->dirname); - - if (dev->descriptor.iManufacturer) { - ret = usb_get_string_simple(udev, dev->descriptor.iManufacturer, - string, sizeof(string)); - if (ret > 0) { - curDevice->Vendor = strdup(string); - } - } - - if (dev->descriptor.iProduct) { - ret = usb_get_string_simple(udev, dev->descriptor.iProduct, - string, sizeof(string)); - if (ret > 0) { - curDevice->Product = strdup(string); - } - } - - if (dev->descriptor.iSerialNumber) { - ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, - string, sizeof(string)); - if (ret > 0) { - curDevice->Serial = strdup(string); - } - } - - upsdebugx(2, "- VendorID: %04x", curDevice->VendorID); - upsdebugx(2, "- ProductID: %04x", curDevice->ProductID); - upsdebugx(2, "- Manufacturer: %s", curDevice->Vendor ? curDevice->Vendor : "unknown"); - upsdebugx(2, "- Product: %s", curDevice->Product ? curDevice->Product : "unknown"); - upsdebugx(2, "- Serial Number: %s", curDevice->Serial ? curDevice->Serial : "unknown"); - upsdebugx(2, "- Bus: %s", curDevice->Bus ? curDevice->Bus : "unknown"); - - upsdebugx(2, "Trying to match device"); - for (m = matcher; m; m=m->next) { - ret = matches(m, curDevice); - if (ret==0) { - upsdebugx(2, "Device does not match - skipping"); - goto next_device; - } else if (ret==-1) { - fatal_with_errno(EXIT_FAILURE, "matcher"); - goto next_device; - } else if (ret==-2) { - upsdebugx(2, "matcher: unspecified error"); - goto next_device; - } - } - upsdebugx(2, "Device matches"); - - /* Now we have matched the device we wanted. Claim it. */ - -#ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP - /* this method requires at least libusb 0.1.8: - * it force device claiming by unbinding - * attached driver... From libhid */ - retries = 3; - while (usb_claim_interface(udev, 0) < 0) { - - upsdebugx(2, "failed to claim USB device: %s", usb_strerror()); - - if (usb_detach_kernel_driver_np(udev, 0) < 0) { - upsdebugx(2, "failed to detach kernel driver from USB device: %s", usb_strerror()); - } else { - upsdebugx(2, "detached kernel driver from USB device..."); - } - - if (retries-- > 0) { - continue; - } - - fatalx(EXIT_FAILURE, "Can't claim USB device [%04x:%04x]: %s", curDevice->VendorID, curDevice->ProductID, usb_strerror()); - } -#else - if (usb_claim_interface(udev, 0) < 0) { - fatalx(EXIT_FAILURE, "Can't claim USB device [%04x:%04x]: %s", curDevice->VendorID, curDevice->ProductID, usb_strerror()); - } -#endif - - /* set default interface */ - usb_set_altinterface(udev, 0); - - if (!callback) { - return 1; - } - - if (!dev->config) { /* ?? this should never happen */ - upsdebugx(2, " Couldn't retrieve descriptors"); - goto next_device; - } - - rdlen1 = -1; - rdlen2 = -1; - - /* Get HID descriptor */ - - /* FIRST METHOD: ask for HID descriptor directly. */ - /* res = usb_get_descriptor(udev, USB_DT_HID, 0, buf, 0x9); */ - res = usb_control_msg(udev, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, - (USB_DT_HID << 8) + 0, 0, buf, 0x9, USB_TIMEOUT); - - if (res < 0) { - upsdebugx(2, "Unable to get HID descriptor (%s)", usb_strerror()); - } else if (res < 9) { - upsdebugx(2, "HID descriptor too short (expected %d, got %d)", 8, res); - } else { - - upsdebug_hex(3, "HID descriptor, method 1", buf, 9); - - rdlen1 = buf[7] | (buf[8] << 8); - } - - if (rdlen1 < -1) { - upsdebugx(2, "Warning: HID descriptor, method 1 failed"); - } - - /* SECOND METHOD: find HID descriptor among "extra" bytes of - interface descriptor, i.e., bytes tucked onto the end of - descriptor 2. */ - - /* Note: on some broken UPS's (e.g. Tripp Lite Smart1000LCD), - only this second method gives the correct result */ - - /* for now, we always assume configuration 0, interface 0, - altsetting 0, as above. */ - iface = &dev->config[0].interface[0].altsetting[0]; - for (i=0; iextralen; i+=iface->extra[i]) { - upsdebugx(4, "i=%d, extra[i]=%02x, extra[i+1]=%02x", i, - iface->extra[i], iface->extra[i+1]); - if (i+9 <= iface->extralen && iface->extra[i] >= 9 && iface->extra[i+1] == 0x21) { - p = &iface->extra[i]; - upsdebug_hex(3, "HID descriptor, method 2", p, 9); - rdlen2 = p[7] | (p[8] << 8); - break; - } - } - - if (rdlen2 < -1) { - upsdebugx(2, "Warning: HID descriptor, method 2 failed"); - } - - /* when available, always choose the second value, as it - seems to be more reliable (it is the one reported e.g. by - lsusb). Note: if the need arises, can change this to use - the maximum of the two values instead. */ - rdlen = rdlen2 >= 0 ? rdlen2 : rdlen1; - - if (rdlen < 0) { - upsdebugx(2, "Unable to retrieve any HID descriptor"); - goto next_device; - } - if (rdlen1 >= 0 && rdlen2 >= 0 && rdlen1 != rdlen2) { - upsdebugx(2, "Warning: two different HID descriptors retrieved (Reportlen = %d vs. %d)", rdlen1, rdlen2); - } - - upsdebugx(2, "HID descriptor length %d", rdlen); - - if (rdlen > (int)sizeof(rdbuf)) { - upsdebugx(2, "HID descriptor too long %d (max %d)", rdlen, (int)sizeof(rdbuf)); - goto next_device; - } - - /* res = usb_get_descriptor(udev, USB_DT_REPORT, 0, bigbuf, rdlen); */ - res = usb_control_msg(udev, USB_ENDPOINT_IN+1, USB_REQ_GET_DESCRIPTOR, - (USB_DT_REPORT << 8) + 0, 0, rdbuf, rdlen, USB_TIMEOUT); - - if (res < 0) - { - upsdebug_with_errno(2, "Unable to get Report descriptor"); - goto next_device; - } - - if (res < rdlen) - { - upsdebugx(2, "Warning: report descriptor too short (expected %d, got %d)", rdlen, res); - rdlen = res; /* correct rdlen if necessary */ - } - - res = callback(udev, curDevice, rdbuf, rdlen); - if (res < 1) { - upsdebugx(2, "Caller doesn't like this device"); - goto next_device; - } - - upsdebugx(2, "Report descriptor retrieved (Reportlen = %d)", rdlen); - upsdebugx(2, "Found HID device"); - fflush(stdout); - - return rdlen; - - next_device: - usb_close(udev); - } - } - - *udevp = NULL; - upsdebugx(2, "No appropriate HID device found"); - fflush(stdout); - - return -1; -} - -/* - * Error handler for usb_get/set_* functions. Return value > 0 success, - * 0 unknown or temporary failure (ignored), < 0 permanent failure (reconnect) - */ -static int libusb_strerror(const int ret, const char *desc) -{ - if (ret > 0) { - return ret; - } - - switch(ret) - { - case -EBUSY: /* Device or resource busy */ - case -EPERM: /* Operation not permitted */ - case -ENODEV: /* No such device */ - case -EACCES: /* Permission denied */ - case -EIO: /* I/O error */ - case -ENXIO: /* No such device or address */ - case -ENOENT: /* No such file or directory */ - case -EPIPE: /* Broken pipe */ - case -ENOSYS: /* Function not implemented */ - upslogx(LOG_DEBUG, "%s: %s", desc, usb_strerror()); - return ret; - - case -ETIMEDOUT: /* Connection timed out */ - upsdebugx(2, "%s: Connection timed out", desc); - return 0; - - case -EOVERFLOW: /* Value too large for defined data type */ - case -EPROTO: /* Protocol error */ - upsdebugx(2, "%s: %s", desc, usb_strerror()); - return 0; - - default: /* Undetermined, log only */ - upslogx(LOG_DEBUG, "%s: %s", desc, usb_strerror()); - return 0; - } -} - -/* return the report of ID=type in report - * return -1 on failure, report length on success - */ - -static int libusb_get_report(usb_dev_handle *udev, int ReportId, unsigned char *raw_buf, int ReportSize ) -{ - int ret; - - upsdebugx(4, "Entering libusb_get_report"); - - if (!udev) { - return 0; - } - - ret = usb_control_msg(udev, - USB_ENDPOINT_IN + USB_TYPE_CLASS + USB_RECIP_INTERFACE, - 0x01, /* HID_REPORT_GET */ - ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ - 0, raw_buf, ReportSize, USB_TIMEOUT); - - /* Ignore "protocol stall" (for unsupported request) on control endpoint */ - if (ret == -EPIPE) { - return 0; - } - - return libusb_strerror(ret, __func__); -} - -static int libusb_set_report(usb_dev_handle *udev, int ReportId, unsigned char *raw_buf, int ReportSize ) -{ - int ret; - - if (!udev) { - return 0; - } - - ret = usb_control_msg(udev, - USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, - 0x09, /* HID_REPORT_SET = 0x09*/ - ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ - 0, raw_buf, ReportSize, USB_TIMEOUT); - - /* Ignore "protocol stall" (for unsupported request) on control endpoint */ - if (ret == -EPIPE) { - return 0; - } - - return libusb_strerror(ret, __func__); -} - -static int libusb_get_string(usb_dev_handle *udev, int StringIdx, char *buf, size_t buflen) -{ - int ret; - - if (!udev) { - return -1; - } - - ret = usb_get_string_simple(udev, StringIdx, buf, buflen); - - return libusb_strerror(ret, __func__); -} - -static int libusb_get_interrupt(usb_dev_handle *udev, unsigned char *buf, int bufsize, int timeout) -{ - int ret; - - if (!udev) { - return -1; - } - - /* FIXME: hardcoded interrupt EP => need to get EP descr for IF descr */ - ret = usb_interrupt_read(udev, 0x81, (char *)buf, bufsize, timeout); - - /* Clear stall condition */ - if (ret == -EPIPE) { - ret = usb_clear_halt(udev, 0x81); - } - - return libusb_strerror(ret, __func__); -} - -static void libusb_close(usb_dev_handle *udev) -{ - if (!udev) { - return; - } - - /* usb_release_interface() sometimes blocks and goes - into uninterruptible sleep. So don't do it. */ - /* usb_release_interface(udev, 0); */ - usb_close(udev); -} - -usb_communication_subdriver_t usb_subdriver = { - USB_DRIVER_VERSION, - USB_DRIVER_NAME, - libusb_open, - libusb_close, - libusb_get_report, - libusb_set_report, - libusb_get_string, - libusb_get_interrupt -}; diff --git a/drivers/libusb.h b/drivers/libusb.h deleted file mode 100644 index 66d2633..0000000 --- a/drivers/libusb.h +++ /dev/null @@ -1,66 +0,0 @@ -/*! - * @file libusb.h - * @brief HID Library - Generic USB backend for Generic HID Access (using MGE HIDParser) - * - * @author Copyright (C) - * 2003 - 2006 Arnaud Quette - * 2005 Peter Selinger - * - * This program is sponsored by MGE UPS SYSTEMS - opensource.mgeups.com - * - * The logic of this file is ripped from mge-shut driver (also from - * Arnaud Quette), which is a "HID over serial link" UPS driver for - * Network UPS Tools - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * -------------------------------------------------------------------------- */ - -#ifndef LIBUSB_H -#define LIBUSB_H - -#include "main.h" /* for subdrv_info_t */ -#include "usb-common.h" /* for USBDevice_t and USBDeviceMatcher_t */ - -#include /* libusb header file */ - -extern upsdrv_info_t comm_upsdrv_info; - -/*! - * usb_communication_subdriver_s: structure to describe the communication routines - * @name: can be either "shut" for Serial HID UPS Transfer (from MGE) or "usb" - */ -typedef struct usb_communication_subdriver_s { - const char *name; /* name of this subdriver */ - const char *version; /* version of this subdriver */ - int (*open)(usb_dev_handle **sdevp, /* try to open the next available */ - USBDevice_t *curDevice, /* device matching USBDeviceMatcher_t */ - USBDeviceMatcher_t *matcher, - int (*callback)(usb_dev_handle *udev, USBDevice_t *hd, unsigned char *rdbuf, int rdlen)); - void (*close)(usb_dev_handle *sdev); - int (*get_report)(usb_dev_handle *sdev, int ReportId, - unsigned char *raw_buf, int ReportSize ); - int (*set_report)(usb_dev_handle *sdev, int ReportId, - unsigned char *raw_buf, int ReportSize ); - int (*get_string)(usb_dev_handle *sdev, - int StringIdx, char *buf, size_t buflen); - int (*get_interrupt)(usb_dev_handle *sdev, - unsigned char *buf, int bufsize, int timeout); -} usb_communication_subdriver_t; - -extern usb_communication_subdriver_t usb_subdriver; - -#endif /* LIBUSB_H */ - diff --git a/drivers/libusb0.c b/drivers/libusb0.c new file mode 100644 index 0000000..643a0a7 --- /dev/null +++ b/drivers/libusb0.c @@ -0,0 +1,773 @@ +/*! + * @file libusb0.c + * @brief HID Library - Generic USB communication sub driver (using libusb 0.1) + * + * @author Copyright (C) + * 2003 - 2007 Arnaud Quette + * 2005 - 2007 Peter Selinger + * + * This program is sponsored by MGE UPS SYSTEMS - opensource.mgeups.com + * + * The logic of this file is ripped from mge-shut driver (also from + * Arnaud Quette), which is a "HID over serial link" UPS driver for + * Network UPS Tools + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * -------------------------------------------------------------------------- */ + +#include "config.h" /* for HAVE_USB_DETACH_KERNEL_DRIVER_NP flag */ +#include "common.h" /* for xmalloc, upsdebugx prototypes */ +#include "usb-common.h" +#include "nut_libusb.h" + +#define USB_DRIVER_NAME "USB communication driver (libusb 0.1)" +#define USB_DRIVER_VERSION "0.43" + +/* driver description structure */ +upsdrv_info_t comm_upsdrv_info = { + USB_DRIVER_NAME, + USB_DRIVER_VERSION, + NULL, + 0, + { NULL } +}; + +#define MAX_REPORT_SIZE 0x1800 +#define MAX_RETRY 3 + +static void libusb_close(usb_dev_handle *udev); + +/*! Add USB-related driver variables with addvar() and dstate_setinfo(). + * This removes some code duplication across the USB drivers. + */ +void nut_usb_addvars(void) +{ + /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ + addvar(VAR_VALUE, "vendor", "Regular expression to match UPS Manufacturer string"); + addvar(VAR_VALUE, "product", "Regular expression to match UPS Product string"); + addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial number"); + + addvar(VAR_VALUE, "vendorid", "Regular expression to match UPS Manufacturer numerical ID (4 digits hexadecimal)"); + addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)"); + + addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name"); + addvar(VAR_VALUE, "device", "Regular expression to match USB device name"); + addvar(VAR_VALUE, "usb_set_altinterface", "Force redundant call to usb_set_altinterface() (value=bAlternateSetting; default=0)"); + + dstate_setinfo("driver.version.usb", "libusb-0.1 (or compat)"); +} + +/* From usbutils: workaround libusb (0.1) API goofs: + * "byte" should never be sign extended; + * using "char" is trouble. + * Likewise, sizes should never be negative. + */ + +/* +static inline int typesafe_control_msg(usb_dev_handle *dev, + unsigned char requesttype, unsigned char request, + int value, int index, + unsigned char *bytes, unsigned size, int timeout) +{ + return usb_control_msg(dev, requesttype, request, value, index, + (char *) bytes, (int) size, timeout); +} +*/ + +static inline int typesafe_control_msg( + usb_dev_handle *dev, + unsigned char requesttype, + unsigned char request, + int value, + int index, + usb_ctrl_charbuf bytes, + usb_ctrl_charbufsize size, + usb_ctrl_timeout_msec timeout) +{ + return usb_control_msg(dev, requesttype, request, value, index, + (char *) bytes, (int) size, timeout); +} + + +/* invoke matcher against device */ +static inline int matches(USBDeviceMatcher_t *matcher, USBDevice_t *device) { + if (!matcher) { + return 1; + } + return matcher->match_function(device, matcher->privdata); +} + +/*! If needed, set the USB alternate interface. + * + * In NUT 2.7.2 and earlier, the following call was made unconditionally: + * usb_set_altinterface(udev, 0); + * + * Although harmless on Linux and *BSD, this extra call prevents old Tripp Lite + * devices from working on Mac OS X (presumably the OS is already setting + * altinterface to 0). + */ +static int nut_usb_set_altinterface(usb_dev_handle *udev) +{ + int altinterface = 0, ret = 0; + char *alt_string, *endp = NULL; + + if(testvar("usb_set_altinterface")) { + alt_string = getval("usb_set_altinterface"); + if(alt_string) { + altinterface = (int)strtol(alt_string, &endp, 10); + if(endp && !(endp[0] == 0)) { + upslogx(LOG_WARNING, "%s: '%s' is not a valid number", __func__, alt_string); + } + if(altinterface < 0 || altinterface > 255) { + upslogx(LOG_WARNING, "%s: setting bAlternateInterface to %d will probably not work", __func__, altinterface); + } + } + /* set default interface */ + upsdebugx(2, "%s: calling usb_set_altinterface(udev, %d)", + __func__, altinterface); + ret = usb_set_altinterface(udev, altinterface); + if(ret != 0) { + upslogx(LOG_WARNING, "%s: usb_set_altinterface(udev, %d) returned %d (%s)", + __func__, altinterface, ret, usb_strerror() ); + } + upslogx(LOG_NOTICE, "%s: usb_set_altinterface() should not be necessary - " + "please email the nut-upsdev list with information about your UPS.", __func__); + } else { + upsdebugx(3, "%s: skipped usb_set_altinterface(udev, 0)", __func__); + } + return ret; +} + +#define usb_control_msg typesafe_control_msg + +/* On success, fill in the curDevice structure and return the report + * descriptor length. On failure, return -1. + * Note: When callback is not NULL, the report descriptor will be + * passed to this function together with the udev and USBDevice_t + * information. This callback should return a value > 0 if the device + * is accepted, or < 1 if not. If it isn't accepted, the next device + * (if any) will be tried, until there are no more devices left. + */ +static int libusb_open(usb_dev_handle **udevp, + USBDevice_t *curDevice, USBDeviceMatcher_t *matcher, + int (*callback)(usb_dev_handle *udev, + USBDevice_t *hd, usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen) + ) +{ + int retries; + usb_ctrl_charbufsize rdlen1, rdlen2; /* report descriptor length, method 1+2 */ + USBDeviceMatcher_t *m; + struct usb_device *dev; + struct usb_bus *bus; + usb_dev_handle *udev; + struct usb_interface_descriptor *iface; + + int ret, res; + usb_ctrl_char buf[20]; + usb_ctrl_char *p; + char string[256]; + int i; + int count_open_EACCESS = 0; + int count_open_errors = 0; + int count_open_attempts = 0; + + /* report descriptor */ + usb_ctrl_char rdbuf[MAX_REPORT_SIZE]; + usb_ctrl_charbufsize rdlen; + + /* libusb base init */ + usb_init(); + usb_find_busses(); + usb_find_devices(); + +#ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ + /* Causes a double free corruption in linux if device is detached! */ + libusb_close(*udevp); +#endif + + upsdebugx(3, "usb_busses=%p", (void*)usb_busses); + + for (bus = usb_busses; bus; bus = bus->next) { + for (dev = bus->devices; dev; dev = dev->next) { + /* int if_claimed = 0; */ + + count_open_attempts++; + + upsdebugx(2, "Checking device (%04X/%04X) (%s/%s)", + dev->descriptor.idVendor, dev->descriptor.idProduct, + bus->dirname, dev->filename); + + /* supported vendors are now checked by the + supplied matcher */ + + /* open the device */ + *udevp = udev = usb_open(dev); + if (!udev) { + /* It seems that with libusb-0.1 API we + * can only evaluate the string value of + * usb_strerror() return values - in the + * library source there is magic about + * tracking errors in their string buffer + * or as a printable errno, and no reliably + * usable way to learn of an EACCESS or + * other situation diagnostics otherwise. + * So we have to search for sub-strings + * and hope for locale to be right... + */ + char *libusb_error = usb_strerror(); + upsdebugx(1, "Failed to open device (%04X/%04X), skipping: %s", + dev->descriptor.idVendor, + dev->descriptor.idProduct, + libusb_error); + + count_open_errors++; + if (strcasestr(libusb_error, "Access denied") + || strcasestr(libusb_error, "insufficient permissions") + ) { + count_open_EACCESS++; + } + + continue; + } + + /* collect the identifying information of this + device. Note that this is safe, because + there's no need to claim an interface for + this (and therefore we do not yet need to + detach any kernel drivers). */ + + free(curDevice->Vendor); + free(curDevice->Product); + free(curDevice->Serial); + free(curDevice->Bus); + free(curDevice->Device); + memset(curDevice, '\0', sizeof(*curDevice)); + + curDevice->VendorID = dev->descriptor.idVendor; + curDevice->ProductID = dev->descriptor.idProduct; + curDevice->Bus = xstrdup(bus->dirname); + curDevice->Device = xstrdup(dev->filename); + curDevice->bcdDevice = dev->descriptor.bcdDevice; + + if (dev->descriptor.iManufacturer) { + retries = MAX_RETRY; + while (retries > 0) { + ret = usb_get_string_simple(udev, dev->descriptor.iManufacturer, + string, sizeof(string)); + if (ret > 0) { + curDevice->Vendor = xstrdup(string); + break; + } + retries--; + upsdebugx(1, "%s get iManufacturer failed, retrying...", __func__); + } + } + + if (dev->descriptor.iProduct) { + retries = MAX_RETRY; + while (retries > 0) { + ret = usb_get_string_simple(udev, dev->descriptor.iProduct, + string, sizeof(string)); + if (ret > 0) { + curDevice->Product = xstrdup(string); + break; + } + retries--; + upsdebugx(1, "%s get iProduct failed, retrying...", __func__); + } + } + + if (dev->descriptor.iSerialNumber) { + retries = MAX_RETRY; + while (retries > 0) { + ret = usb_get_string_simple(udev, dev->descriptor.iSerialNumber, + string, sizeof(string)); + if (ret > 0) { + curDevice->Serial = xstrdup(string); + break; + } + retries--; + upsdebugx(1, "%s get iSerialNumber failed, retrying...", __func__); + } + } + + upsdebugx(2, "- VendorID: %04x", curDevice->VendorID); + upsdebugx(2, "- ProductID: %04x", curDevice->ProductID); + upsdebugx(2, "- Manufacturer: %s", curDevice->Vendor ? curDevice->Vendor : "unknown"); + upsdebugx(2, "- Product: %s", curDevice->Product ? curDevice->Product : "unknown"); + upsdebugx(2, "- Serial Number: %s", curDevice->Serial ? curDevice->Serial : "unknown"); + upsdebugx(2, "- Bus: %s", curDevice->Bus ? curDevice->Bus : "unknown"); + upsdebugx(2, "- Device: %s", curDevice->Device ? curDevice->Device : "unknown"); + upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice); + + /* FIXME: extend to Eaton OEMs (HP, IBM, ...) */ + if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { + usb_subdriver.hid_desc_index = 1; + } + + upsdebugx(2, "Trying to match device"); + for (m = matcher; m; m=m->next) { + ret = matches(m, curDevice); + if (ret==0) { + upsdebugx(2, "Device does not match - skipping"); + goto next_device; + } else if (ret==-1) { + fatal_with_errno(EXIT_FAILURE, "matcher"); +#ifndef HAVE___ATTRIBUTE__NORETURN +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunreachable-code" +# endif + goto next_device; +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic pop +# endif +#endif + } else if (ret==-2) { + upsdebugx(2, "matcher: unspecified error"); + goto next_device; + } + } + upsdebugx(2, "Device matches"); + + /* Now we have matched the device we wanted. Claim it. */ + +#ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP + /* this method requires at least libusb 0.1.8: + * it force device claiming by unbinding + * attached driver... From libhid */ + retries = MAX_RETRY; + while (usb_claim_interface(udev, usb_subdriver.hid_rep_index) < 0) { + + upsdebugx(2, "failed to claim USB device: %s", + usb_strerror()); + + if (usb_detach_kernel_driver_np(udev, usb_subdriver.hid_rep_index) < 0) { + upsdebugx(2, "failed to detach kernel driver from USB device: %s", + usb_strerror()); + } else { + upsdebugx(2, "detached kernel driver from USB device..."); + } + + if (retries-- > 0) { + continue; + } + + fatalx(EXIT_FAILURE, + "Can't claim USB device [%04x:%04x]@%d/%d: %s", + curDevice->VendorID, curDevice->ProductID, + usb_subdriver.hid_rep_index, + usb_subdriver.hid_desc_index, + usb_strerror()); + } +#else + if (usb_claim_interface(udev, usb_subdriver.hid_rep_index) < 0) { + fatalx(EXIT_FAILURE, + "Can't claim USB device [%04x:%04x]@%d/%d: %s", + curDevice->VendorID, curDevice->ProductID, + usb_subdriver.hid_rep_index, + usb_subdriver.hid_desc_index, + usb_strerror()); + } +#endif + /* if_claimed = 1; */ + + nut_usb_set_altinterface(udev); + + if (!callback) { + return 1; + } + + if (!dev->config) { /* ?? this should never happen */ + upsdebugx(2, " Couldn't retrieve descriptors"); + goto next_device; + } + + rdlen1 = -1; + rdlen2 = -1; + + /* Get HID descriptor */ + + /* FIRST METHOD: ask for HID descriptor directly. */ + /* res = usb_get_descriptor(udev, USB_DT_HID, hid_desc_index, buf, 0x9); */ + res = usb_control_msg(udev, + USB_ENDPOINT_IN + 1, + USB_REQ_GET_DESCRIPTOR, + (USB_DT_HID << 8) + usb_subdriver.hid_desc_index, + usb_subdriver.hid_rep_index, + buf, 0x9, USB_TIMEOUT); + + if (res < 0) { + upsdebugx(2, "Unable to get HID descriptor (%s)", + usb_strerror()); + } else if (res < 9) { + upsdebugx(2, "HID descriptor too short (expected %d, got %d)", 8, res); + } else { + + upsdebug_hex(3, "HID descriptor, method 1", buf, 9); + + rdlen1 = ((uint8_t)buf[7]) | (((uint8_t)buf[8]) << 8); + } + + if (rdlen1 < -1) { + upsdebugx(2, "Warning: HID descriptor, method 1 failed"); + } + upsdebugx(3, + "HID descriptor length (method 1) %" PRI_NUT_USB_CTRL_CHARBUFSIZE, + rdlen1); + + /* SECOND METHOD: find HID descriptor among "extra" bytes of + interface descriptor, i.e., bytes tucked onto the end of + descriptor 2. */ + + /* Note: on some broken UPS's (e.g. Tripp Lite Smart1000LCD), + only this second method gives the correct result */ + + /* for now, we always assume configuration 0, interface 0, + altsetting 0, as above. */ + iface = &dev->config[0].interface[usb_subdriver.hid_rep_index].altsetting[0]; + for (i=0; iextralen; i+=iface->extra[i]) { + upsdebugx(4, "i=%d, extra[i]=%02x, extra[i+1]=%02x", i, + iface->extra[i], iface->extra[i+1]); + + if (i+9 <= iface->extralen + && iface->extra[i] >= 9 + && iface->extra[i+1] == 0x21 + ) { + p = (usb_ctrl_char *)&iface->extra[i]; + upsdebug_hex(3, "HID descriptor, method 2", p, 9); + rdlen2 = ((uint8_t)p[7]) | (((uint8_t)p[8]) << 8); + break; + } + } + + if (rdlen2 < -1) { + upsdebugx(2, "Warning: HID descriptor, method 2 failed"); + } + upsdebugx(3, + "HID descriptor length (method 2) %" PRI_NUT_USB_CTRL_CHARBUFSIZE, + rdlen2); + + /* when available, always choose the second value, as it + seems to be more reliable (it is the one reported e.g. by + lsusb). Note: if the need arises, can change this to use + the maximum of the two values instead. */ + if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { + upsdebugx(1, "Eaton device v2.02. Using full report descriptor"); + rdlen = rdlen1; + } + else { + rdlen = rdlen2 >= 0 ? rdlen2 : rdlen1; + } + + if (rdlen < 0) { + upsdebugx(2, "Unable to retrieve any HID descriptor"); + goto next_device; + } + if (rdlen1 >= 0 && rdlen2 >= 0 && rdlen1 != rdlen2) { + upsdebugx(2, "Warning: two different HID descriptors retrieved " + "(Reportlen = %" PRI_NUT_USB_CTRL_CHARBUFSIZE + " vs. %" PRI_NUT_USB_CTRL_CHARBUFSIZE ")", + rdlen1, rdlen2); + } + + upsdebugx(2, + "HID descriptor length %" PRI_NUT_USB_CTRL_CHARBUFSIZE, + rdlen); + + if ((uintmax_t)rdlen > sizeof(rdbuf)) { + upsdebugx(2, + "HID descriptor too long %" PRI_NUT_USB_CTRL_CHARBUFSIZE + " (max %zu)", + rdlen, sizeof(rdbuf)); + goto next_device; + } + + /* Note: rdlen is safe to cast to unsigned below, + * since the <0 case was ruled out above */ + /* res = usb_get_descriptor(udev, USB_DT_REPORT, hid_desc_index, bigbuf, rdlen); */ + res = usb_control_msg(udev, + USB_ENDPOINT_IN + 1, + USB_REQ_GET_DESCRIPTOR, + (USB_DT_REPORT << 8) + usb_subdriver.hid_desc_index, + usb_subdriver.hid_rep_index, + rdbuf, rdlen, USB_TIMEOUT); + + if (res < 0) + { + upsdebug_with_errno(2, "Unable to get Report descriptor"); + goto next_device; + } + + if (res < rdlen) + { + upsdebugx(2, "Warning: report descriptor too short " + "(expected %" PRI_NUT_USB_CTRL_CHARBUFSIZE + ", got %d)", rdlen, res); + rdlen = res; /* correct rdlen if necessary */ + } + + res = callback(udev, curDevice, rdbuf, rdlen); + if (res < 1) { + upsdebugx(2, "Caller doesn't like this device"); + goto next_device; + } + + upsdebugx(2, + "Report descriptor retrieved (Reportlen = %" + PRI_NUT_USB_CTRL_CHARBUFSIZE ")", rdlen); + upsdebugx(2, "Found HID device"); + fflush(stdout); + + return rdlen; + + next_device: + /* usb_release_interface() sometimes blocks + * and goes into uninterruptible sleep. + * So don't do it. */ + /* if (if_claimed) + usb_release_interface(udev, 0); */ + usb_close(udev); + } + } + + *udevp = NULL; + upsdebugx(2, "libusb0: No appropriate HID device found"); + fflush(stdout); + + if (count_open_attempts == 0) { + upslogx(LOG_WARNING, + "libusb0: Could not open any HID devices: " + "no USB buses found"); + } + else + if (count_open_errors > 0 + && count_open_errors == count_open_EACCESS + ) { + upslogx(LOG_WARNING, + "libusb0: Could not open any HID devices: " + "insufficient permissions on everything"); + } + + return -1; +} + +/* + * Error handler for usb_get/set_* functions. Return value > 0 success, + * 0 unknown or temporary failure (ignored), < 0 permanent failure (reconnect) + */ +static int libusb_strerror(const int ret, const char *desc) +{ + if (ret > 0) { + return ret; + } + + switch(ret) + { + case -EBUSY: /* Device or resource busy */ + case -EPERM: /* Operation not permitted */ + case -ENODEV: /* No such device */ + case -EACCES: /* Permission denied */ + case -EIO: /* I/O error */ + case -ENXIO: /* No such device or address */ + case -ENOENT: /* No such file or directory */ + case -EPIPE: /* Broken pipe */ + case -ENOSYS: /* Function not implemented */ + upslogx(LOG_DEBUG, "%s: %s", desc, usb_strerror()); + return ret; + + case -ETIMEDOUT: /* Connection timed out */ + upsdebugx(2, "%s: Connection timed out", desc); + return 0; + + case -EOVERFLOW: /* Value too large for defined data type */ +#ifdef EPROTO + case -EPROTO: /* Protocol error */ +#endif + upsdebugx(2, "%s: %s", desc, usb_strerror()); + return 0; + + default: /* Undetermined, log only */ + upslogx(LOG_DEBUG, "%s: %s", desc, usb_strerror()); + return 0; + } +} + +/* return the report of ID=type in report + * return -1 on failure, report length on success + */ + +/* Expected evaluated types for the API: + * static int libusb_get_report(usb_dev_handle *udev, + * int ReportId, unsigned char *raw_buf, int ReportSize) + */ +static int libusb_get_report( + usb_dev_handle *udev, + usb_ctrl_repindex ReportId, + usb_ctrl_charbuf raw_buf, + usb_ctrl_charbufsize ReportSize) +{ + int ret; + + upsdebugx(4, "Entering libusb_get_report"); + + if (!udev) { + return 0; + } + + ret = usb_control_msg(udev, + USB_ENDPOINT_IN + USB_TYPE_CLASS + USB_RECIP_INTERFACE, + 0x01, /* HID_REPORT_GET */ + ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ + usb_subdriver.hid_rep_index, + raw_buf, ReportSize, USB_TIMEOUT); + + /* Ignore "protocol stall" (for unsupported request) on control endpoint */ + if (ret == -EPIPE) { + return 0; + } + + return libusb_strerror(ret, __func__); +} + +/* Expected evaluated types for the API: + * static int libusb_set_report(usb_dev_handle *udev, + * int ReportId, unsigned char *raw_buf, int ReportSize) + */ +static int libusb_set_report( + usb_dev_handle *udev, + usb_ctrl_repindex ReportId, + usb_ctrl_charbuf raw_buf, + usb_ctrl_charbufsize ReportSize) +{ + int ret; + + if (!udev) { + return 0; + } + + ret = usb_control_msg(udev, + USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, + 0x09, /* HID_REPORT_SET = 0x09*/ + ReportId+(0x03<<8), /* HID_REPORT_TYPE_FEATURE */ + usb_subdriver.hid_rep_index, + raw_buf, ReportSize, USB_TIMEOUT); + + /* Ignore "protocol stall" (for unsupported request) on control endpoint */ + if (ret == -EPIPE) { + return 0; + } + + return libusb_strerror(ret, __func__); +} + +/* Expected evaluated types for the API: + * static int libusb_get_string(usb_dev_handle *udev, + * int StringIdx, char *buf, size_t buflen) + */ +static int libusb_get_string( + usb_dev_handle *udev, + usb_ctrl_strindex StringIdx, + char *buf, + usb_ctrl_charbufsize buflen) +{ + int ret; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif + /* + * usb.h:int usb_get_string_simple(usb_dev_handle *dev, int index, + * usb.h- char *buf, size_t buflen); + */ + if (!udev + || StringIdx < 0 || (uintmax_t)StringIdx > INT_MAX + || buflen < 0 || (uintmax_t)buflen > (uintmax_t)SIZE_MAX + ) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic pop +#endif + return -1; + } + + ret = usb_get_string_simple(udev, StringIdx, buf, (size_t)buflen); + + return libusb_strerror(ret, __func__); +} + +/* Expected evaluated types for the API: + * static int libusb_get_interrupt(usb_dev_handle *udev, + * unsigned char *buf, int bufsize, int timeout) + */ +static int libusb_get_interrupt( + usb_dev_handle *udev, + usb_ctrl_charbuf buf, + usb_ctrl_charbufsize bufsize, + usb_ctrl_timeout_msec timeout) +{ + int ret; + + if (!udev) { + return -1; + } + + /* Interrupt EP is USB_ENDPOINT_IN with offset defined in hid_ep_in, which is 0 by default, unless overridden in subdriver. */ + ret = usb_interrupt_read(udev, USB_ENDPOINT_IN + usb_subdriver.hid_ep_in, (char *)buf, bufsize, timeout); + + /* Clear stall condition */ + if (ret == -EPIPE) { + ret = usb_clear_halt(udev, 0x81); + } + + return libusb_strerror(ret, __func__); +} + +static void libusb_close(usb_dev_handle *udev) +{ + if (!udev) { + return; + } + + /* usb_release_interface() sometimes blocks and goes + * into uninterruptible sleep. So don't do it. + */ + /* usb_release_interface(udev, 0); */ + usb_close(udev); +} + +usb_communication_subdriver_t usb_subdriver = { + USB_DRIVER_NAME, + USB_DRIVER_VERSION, + libusb_open, + libusb_close, + libusb_get_report, + libusb_set_report, + libusb_get_string, + libusb_get_interrupt, + LIBUSB_DEFAULT_INTERFACE, + LIBUSB_DEFAULT_DESC_INDEX, + LIBUSB_DEFAULT_HID_EP_IN, + LIBUSB_DEFAULT_HID_EP_OUT +}; diff --git a/drivers/libusb1.c b/drivers/libusb1.c new file mode 100644 index 0000000..55c0b03 --- /dev/null +++ b/drivers/libusb1.c @@ -0,0 +1,907 @@ +/*! + * @file libusb1.c + * @brief Generic USB communication backend (using libusb 1.0) + * + * @author Copyright (C) 2016 Eaton + * Copyright (C) 2016 Arnaud Quette + * Copyright (C) 2021 Jim Klimov + * + * The logic of this file is ripped from mge-shut driver (also from + * Arnaud Quette), which is a "HID over serial link" UPS driver for + * Network UPS Tools + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * -------------------------------------------------------------------------- */ + +#include "config.h" /* for HAVE_LIBUSB_DETACH_KERNEL_DRIVER flag */ +#include "common.h" /* for xmalloc, upsdebugx prototypes */ +#include "usb-common.h" +#include "nut_libusb.h" +#include "nut_stdint.h" + +#define USB_DRIVER_NAME "USB communication driver (libusb 1.0)" +#define USB_DRIVER_VERSION "0.43" + +/* driver description structure */ +upsdrv_info_t comm_upsdrv_info = { + USB_DRIVER_NAME, + USB_DRIVER_VERSION, + NULL, + 0, + { NULL } +}; + +#define MAX_REPORT_SIZE 0x1800 +#define MAX_RETRY 3 + +static void nut_libusb_close(libusb_device_handle *udev); + +/*! Add USB-related driver variables with addvar() and dstate_setinfo(). + * This removes some code duplication across the USB drivers. + */ +void nut_usb_addvars(void) +{ + const struct libusb_version *v = libusb_get_version(); + + /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ + addvar(VAR_VALUE, "vendor", "Regular expression to match UPS Manufacturer string"); + addvar(VAR_VALUE, "product", "Regular expression to match UPS Product string"); + addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial number"); + + addvar(VAR_VALUE, "vendorid", "Regular expression to match UPS Manufacturer numerical ID (4 digits hexadecimal)"); + addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)"); + + addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name"); + addvar(VAR_VALUE, "device", "Regular expression to match USB device name"); + addvar(VAR_VALUE, "usb_set_altinterface", "Force redundant call to usb_set_altinterface() (value=bAlternateSetting; default=0)"); + +#ifdef LIBUSB_API_VERSION + dstate_setinfo("driver.version.usb", "libusb-%u.%u.%u (API: 0x%x)", v->major, v->minor, v->micro, LIBUSB_API_VERSION); +#else /* no LIBUSB_API_VERSION */ + dstate_setinfo("driver.version.usb", "libusb-%u.%u.%u", v->major, v->minor, v->micro); +#endif /* LIBUSB_API_VERSION */ +} + +/* invoke matcher against device */ +static inline int matches(USBDeviceMatcher_t *matcher, USBDevice_t *device) { + if (!matcher) { + return 1; + } + return matcher->match_function(device, matcher->privdata); +} + +/*! If needed, set the USB alternate interface. + * + * In NUT 2.7.2 and earlier, the following call was made unconditionally: + * usb_set_altinterface(udev, 0); + * + * Although harmless on Linux and *BSD, this extra call prevents old Tripp Lite + * devices from working on Mac OS X (presumably the OS is already setting + * altinterface to 0). + */ +static int nut_usb_set_altinterface(libusb_device_handle *udev) +{ + int altinterface = 0, ret = 0; + char *alt_string, *endp = NULL; + + if(testvar("usb_set_altinterface")) { + alt_string = getval("usb_set_altinterface"); + if(alt_string) { + altinterface = (int)strtol(alt_string, &endp, 10); + if(endp && !(endp[0] == 0)) { + upslogx(LOG_WARNING, "%s: '%s' is not a valid number", __func__, alt_string); + } + if(altinterface < 0 || altinterface > 255) { + upslogx(LOG_WARNING, "%s: setting bAlternateInterface to %d will probably not work", __func__, altinterface); + } + } + /* set default interface */ + upsdebugx(2, "%s: calling libusb_set_interface_alt_setting(udev, %d, %d)", + __func__, usb_subdriver.hid_rep_index, altinterface); + ret = libusb_set_interface_alt_setting(udev, usb_subdriver.hid_rep_index, altinterface); + if(ret != 0) { + upslogx(LOG_WARNING, "%s: libusb_set_interface_alt_setting(udev, %d, %d) returned %d (%s)", + __func__, usb_subdriver.hid_rep_index, altinterface, ret, libusb_strerror((enum libusb_error)ret) ); + } + upslogx(LOG_NOTICE, "%s: libusb_set_interface_alt_setting() should not be necessary - " + "please email the nut-upsdev list with information about your UPS.", __func__); + } else { + upsdebugx(3, "%s: skipped libusb_set_interface_alt_setting(udev, %d, 0)", + __func__, usb_subdriver.hid_rep_index); + } + return ret; +} + +/* On success, fill in the curDevice structure and return the report + * descriptor length. On failure, return -1. + * Note: When callback is not NULL, the report descriptor will be + * passed to this function together with the udev and USBDevice_t + * information. This callback should return a value > 0 if the device + * is accepted, or < 1 if not. If it isn't accepted, the next device + * (if any) will be tried, until there are no more devices left. + */ +static int nut_libusb_open(libusb_device_handle **udevp, + USBDevice_t *curDevice, USBDeviceMatcher_t *matcher, + int (*callback)(libusb_device_handle *udev, + USBDevice_t *hd, usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen) + ) +{ + int retries; + /* libusb-1.0 usb_ctrl_charbufsize is uint16_t and we + * want the rdlen vars signed - so taking a wider type */ + int32_t rdlen1, rdlen2; /* report descriptor length, method 1+2 */ + USBDeviceMatcher_t *m; + libusb_device **devlist; + ssize_t devcount = 0; + size_t devnum; + struct libusb_device_descriptor dev_desc; + struct libusb_config_descriptor *conf_desc = NULL; + const struct libusb_interface_descriptor *if_desc; + libusb_device_handle *udev; + uint8_t bus; + int ret, res; + unsigned char buf[20]; + const unsigned char *p; + char string[256]; + int i; + int count_open_EACCESS = 0; + int count_open_errors = 0; + + /* report descriptor */ + unsigned char rdbuf[MAX_REPORT_SIZE]; + int32_t rdlen; + + /* libusb base init */ + if (libusb_init(NULL) < 0) { + libusb_exit(NULL); + fatal_with_errno(EXIT_FAILURE, "Failed to init libusb 1.0"); + } + +#ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ + /* Causes a double free corruption in linux if device is detached! */ + /* nut_libusb_close(*udevp); */ + if (*udevp) + libusb_close(*udevp); +#endif + + devcount = libusb_get_device_list(NULL, &devlist); + + /* devcount may be < 0, loop will get skipped; + * its SSIZE_MAX < SIZE_MAX for devnum */ + for (devnum = 0; (ssize_t)devnum < devcount; devnum++) { + /* int if_claimed = 0; */ + libusb_device *device = devlist[devnum]; + + libusb_get_device_descriptor(device, &dev_desc); + upsdebugx(2, "Checking device %zu of %zu (%04X/%04X)", + devnum + 1, devcount, + dev_desc.idVendor, dev_desc.idProduct); + + /* supported vendors are now checked by the supplied matcher */ + + /* open the device */ + ret = libusb_open(device, udevp); + if (ret != 0) { + upsdebugx(1, "Failed to open device (%04X/%04X), skipping: %s", + dev_desc.idVendor, + dev_desc.idProduct, + libusb_strerror((enum libusb_error)ret)); + count_open_errors++; + if (ret == LIBUSB_ERROR_ACCESS) { + count_open_EACCESS++; + } + continue; + } + udev = *udevp; + + /* collect the identifying information of this + device. Note that this is safe, because + there's no need to claim an interface for + this (and therefore we do not yet need to + detach any kernel drivers). */ + + free(curDevice->Vendor); + free(curDevice->Product); + free(curDevice->Serial); + free(curDevice->Bus); + free(curDevice->Device); + memset(curDevice, '\0', sizeof(*curDevice)); + + bus = libusb_get_bus_number(device); + curDevice->Bus = (char *)malloc(4); + if (curDevice->Bus == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + sprintf(curDevice->Bus, "%03d", bus); + curDevice->VendorID = dev_desc.idVendor; + curDevice->ProductID = dev_desc.idProduct; + curDevice->bcdDevice = dev_desc.bcdDevice; + + if (dev_desc.iManufacturer) { + retries = MAX_RETRY; + while (retries > 0) { + ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iManufacturer, + (unsigned char*)string, sizeof(string)); + if (ret > 0) { + curDevice->Vendor = strdup(string); + if (curDevice->Vendor == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + break; + } + retries--; + upsdebugx(1, "%s get iManufacturer failed, retrying...", __func__); + } + } + + if (dev_desc.iProduct) { + retries = MAX_RETRY; + while (retries > 0) { + ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iProduct, + (unsigned char*)string, sizeof(string)); + if (ret > 0) { + curDevice->Product = strdup(string); + if (curDevice->Product == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + break; + } + retries--; + upsdebugx(1, "%s get iProduct failed, retrying...", __func__); + } + } + + if (dev_desc.iSerialNumber) { + retries = MAX_RETRY; + while (retries > 0) { + ret = libusb_get_string_descriptor_ascii(udev, dev_desc.iSerialNumber, + (unsigned char*)string, sizeof(string)); + if (ret > 0) { + curDevice->Serial = strdup(string); + if (curDevice->Serial == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + break; + } + retries--; + upsdebugx(1, "%s get iSerialNumber failed, retrying...", __func__); + } + } + + upsdebugx(2, "- VendorID: %04x", curDevice->VendorID); + upsdebugx(2, "- ProductID: %04x", curDevice->ProductID); + upsdebugx(2, "- Manufacturer: %s", curDevice->Vendor ? curDevice->Vendor : "unknown"); + upsdebugx(2, "- Product: %s", curDevice->Product ? curDevice->Product : "unknown"); + upsdebugx(2, "- Serial Number: %s", curDevice->Serial ? curDevice->Serial : "unknown"); + upsdebugx(2, "- Bus: %s", curDevice->Bus ? curDevice->Bus : "unknown"); + upsdebugx(2, "- Device: %s", curDevice->Device ? curDevice->Device : "unknown"); + upsdebugx(2, "- Device release number: %04x", curDevice->bcdDevice); + + /* FIXME: extend to Eaton OEMs (HP, IBM, ...) */ + if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { + usb_subdriver.hid_desc_index = 1; + } + + upsdebugx(2, "Trying to match device"); + for (m = matcher; m; m=m->next) { + ret = matches(m, curDevice); + if (ret==0) { + upsdebugx(2, "Device does not match - skipping"); + goto next_device; + } else if (ret==-1) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "matcher"); +#ifndef HAVE___ATTRIBUTE__NORETURN +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunreachable-code" +# endif + goto next_device; +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic pop +# endif +#endif + } else if (ret==-2) { + upsdebugx(2, "matcher: unspecified error"); + goto next_device; + } + } + upsdebugx(2, "Device matches"); + + + upsdebugx(2, "Reading first configuration descriptor"); + ret = libusb_get_config_descriptor(device, + (uint8_t)usb_subdriver.hid_rep_index, + &conf_desc); + /*ret = libusb_get_active_config_descriptor(device, &conf_desc);*/ + if (ret < 0) + upsdebugx(2, "result: %i (%s)", + ret, libusb_strerror((enum libusb_error)ret)); + + /* Now we have matched the device we wanted. Claim it. */ + +#if defined(HAVE_LIBUSB_KERNEL_DRIVER_ACTIVE) && defined(HAVE_LIBUSB_SET_AUTO_DETACH_KERNEL_DRIVER) + /* Due to the way FreeBSD implements libusb_set_auto_detach_kernel_driver(), + * check to see if the kernel driver is active before setting + * the auto-detach flag. Otherwise, libusb_claim_interface() + * with the auto-detach flag only works if the driver is + * running as root. + * + * Is the kernel driver active? Consider the unimplemented + * return code to be equivalent to inactive here. + */ + if((ret = libusb_kernel_driver_active(udev, usb_subdriver.hid_rep_index)) == 1) { + upsdebugx(3, "libusb_kernel_driver_active() returned 1 (driver active)"); + /* Try the auto-detach kernel driver method. + * This function is not available on FreeBSD 10.1-10.3 */ + if ((ret = libusb_set_auto_detach_kernel_driver (udev, 1)) != LIBUSB_SUCCESS) { + upsdebugx(1, "failed to set kernel driver auto-detach " + "driver flag for USB device: %s", + libusb_strerror((enum libusb_error)ret)); + } else { + upsdebugx(2, "successfully set kernel driver auto-detach flag"); + } + } else { + upsdebugx(3, "libusb_kernel_driver_active() returned %d", ret); + } +#endif + +#if (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER) || (defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP) + /* Then, try the explicit detach method. + * This function is available on FreeBSD 10.1-10.3 */ + retries = MAX_RETRY; + while ((ret = libusb_claim_interface(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { + upsdebugx(2, "failed to claim USB device: %s", + libusb_strerror((enum libusb_error)ret)); + +# ifdef HAVE_LIBUSB_DETACH_KERNEL_DRIVER + if ((ret = libusb_detach_kernel_driver(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { +# else /* if defined HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP) */ + if ((ret = libusb_detach_kernel_driver_np(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS) { +# endif + if (ret == LIBUSB_ERROR_NOT_FOUND) + upsdebugx(2, "Kernel driver already detached"); + else + upsdebugx(1, "failed to detach kernel driver from USB device: %s", + libusb_strerror((enum libusb_error)ret)); + } else { + upsdebugx(2, "detached kernel driver from USB device..."); + } + + if (retries-- > 0) { + continue; + } + + libusb_free_config_descriptor(conf_desc); + libusb_free_device_list(devlist, 1); + fatalx(EXIT_FAILURE, + "Can't claim USB device [%04x:%04x]@%d/%d: %s", + curDevice->VendorID, curDevice->ProductID, + usb_subdriver.hid_rep_index, + usb_subdriver.hid_desc_index, + libusb_strerror((enum libusb_error)ret)); + } +#else + if ((ret = libusb_claim_interface(udev, usb_subdriver.hid_rep_index)) != LIBUSB_SUCCESS ) { + libusb_free_config_descriptor(conf_desc); + libusb_free_device_list(devlist, 1); + fatalx(EXIT_FAILURE, + "Can't claim USB device [%04x:%04x]@%d/%d: %s", + curDevice->VendorID, curDevice->ProductID, + usb_subdriver.hid_rep_index, + usb_subdriver.hid_desc_index, + libusb_strerror((enum libusb_error)ret)); + } +#endif + /* if_claimed = 1; */ + upsdebugx(2, "Claimed interface %d successfully", + usb_subdriver.hid_rep_index); + + nut_usb_set_altinterface(udev); + + if (!callback) { + libusb_free_config_descriptor(conf_desc); + libusb_free_device_list(devlist, 1); + return 1; + } + + if (!conf_desc) { /* ?? this should never happen */ + upsdebugx(2, " Couldn't retrieve descriptors"); + goto next_device; + } + + rdlen1 = -1; + rdlen2 = -1; + + /* Get HID descriptor */ + + /* FIRST METHOD: ask for HID descriptor directly. */ + /* libusb0: USB_ENDPOINT_IN + 1 */ + res = libusb_control_transfer(udev, + LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_STANDARD|LIBUSB_RECIPIENT_INTERFACE, + LIBUSB_REQUEST_GET_DESCRIPTOR, + (LIBUSB_DT_HID << 8) + usb_subdriver.hid_desc_index, + usb_subdriver.hid_rep_index, + buf, 0x9, USB_TIMEOUT); + + if (res < 0) { + upsdebugx(2, "Unable to get HID descriptor (%s)", + libusb_strerror((enum libusb_error)res)); + } else if (res < 9) { + upsdebugx(2, "HID descriptor too short (expected %d, got %d)", 8, res); + } else { + + upsdebug_hex(3, "HID descriptor, method 1", buf, 9); + + rdlen1 = ((uint8_t)buf[7]) | (((uint8_t)buf[8]) << 8); + } + + if (rdlen1 < -1) { + upsdebugx(2, "Warning: HID descriptor, method 1 failed"); + } + upsdebugx(3, "HID descriptor length (method 1) %d", rdlen1); + + /* SECOND METHOD: find HID descriptor among "extra" bytes of + interface descriptor, i.e., bytes tucked onto the end of + descriptor 2. */ + + /* Note: on some broken UPS's (e.g. Tripp Lite Smart1000LCD), + only this second method gives the correct result */ + + /* for now, we always assume configuration 0, interface 0, + altsetting 0, as above. */ + + if_desc = &(conf_desc->interface[usb_subdriver.hid_rep_index].altsetting[0]); + for (i = 0; i < if_desc->extra_length; i += if_desc->extra[i]) { + upsdebugx(4, "i=%d, extra[i]=%02x, extra[i+1]=%02x", i, + if_desc->extra[i], if_desc->extra[i+1]); + if (i+9 <= if_desc->extra_length && if_desc->extra[i] >= 9 && if_desc->extra[i+1] == 0x21) { + p = &if_desc->extra[i]; + upsdebug_hex(3, "HID descriptor, method 2", p, 9); + rdlen2 = ((uint8_t)p[7]) | (((uint8_t)p[8]) << 8); + break; + } + } + + /* we can now free the config descriptor */ + libusb_free_config_descriptor(conf_desc); + + if (rdlen2 < -1) { + upsdebugx(2, "Warning: HID descriptor, method 2 failed"); + } + upsdebugx(3, "HID descriptor length (method 2) %d", rdlen2); + + /* when available, always choose the second value, as it + seems to be more reliable (it is the one reported e.g. by + lsusb). Note: if the need arises, can change this to use + the maximum of the two values instead. */ + if ((curDevice->VendorID == 0x463) && (curDevice->bcdDevice == 0x0202)) { + upsdebugx(1, "Eaton device v2.02. Using full report descriptor"); + rdlen = rdlen1; + } + else { + rdlen = rdlen2 >= 0 ? rdlen2 : rdlen1; + } + + if (rdlen < 0) { + upsdebugx(2, "Unable to retrieve any HID descriptor"); + goto next_device; + } + if (rdlen1 >= 0 && rdlen2 >= 0 && rdlen1 != rdlen2) { + upsdebugx(2, "Warning: two different HID descriptors retrieved " + "(Reportlen = %d vs. %d)", rdlen1, rdlen2); + } + + upsdebugx(2, "HID descriptor length %d", rdlen); + + if (rdlen > (int)sizeof(rdbuf)) { + upsdebugx(2, "HID descriptor too long %d (max %d)", + rdlen, (int)sizeof(rdbuf)); + goto next_device; + } + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif + if ((uintmax_t)rdlen > UINT16_MAX) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic pop +#endif + upsdebugx(2, "HID descriptor too long %d (max %u)", + rdlen, UINT16_MAX); + goto next_device; + } + + /* libusb0: USB_ENDPOINT_IN + 1 */ + res = libusb_control_transfer(udev, + LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_STANDARD|LIBUSB_RECIPIENT_INTERFACE, + LIBUSB_REQUEST_GET_DESCRIPTOR, + (LIBUSB_DT_REPORT << 8) + usb_subdriver.hid_desc_index, + usb_subdriver.hid_rep_index, + rdbuf, (uint16_t)rdlen, USB_TIMEOUT); + + if (res < 0) + { + upsdebug_with_errno(2, "Unable to get Report descriptor"); + goto next_device; + } + + if (res < rdlen) + { + upsdebugx(2, "Warning: report descriptor too short " + "(expected %d, got %d)", rdlen, res); + rdlen = res; /* correct rdlen if necessary */ + } + + if (rdlen < USB_CTRL_CHARBUFSIZE_MIN + || (uintmax_t)rdlen > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX + ) { + upsdebugx(2, + "Report descriptor length is out of range on this device: " + "should be %ji < %d < %ju", + (intmax_t)USB_CTRL_CHARBUFSIZE_MIN, rdlen, + (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX); + goto next_device; + } + + res = callback(udev, curDevice, rdbuf, (usb_ctrl_charbufsize)rdlen); + if (res < 1) { + upsdebugx(2, "Caller doesn't like this device"); + goto next_device; + } + + upsdebugx(2, "Report descriptor retrieved (Reportlen = %d)", rdlen); + upsdebugx(2, "Found HID device"); + fflush(stdout); + libusb_free_device_list(devlist, 1); + + return rdlen; + + next_device: + /* usb_release_interface() sometimes blocks and goes + into uninterruptible sleep. So don't do it. */ + /* if (if_claimed) + libusb_release_interface(udev, usb_subdriver.hid_rep_index); */ + libusb_close(udev); + } + + *udevp = NULL; + libusb_free_device_list(devlist, 1); + upsdebugx(2, "libusb1: No appropriate HID device found"); + fflush(stdout); + + if (devcount < 1) { + upslogx(LOG_WARNING, + "libusb1: Could not open any HID devices: " + "no USB buses found"); + } + else + if (count_open_errors > 0 + || count_open_errors == count_open_EACCESS + ) { + upslogx(LOG_WARNING, + "libusb1: Could not open any HID devices: " + "insufficient permissions on everything"); + } + + return -1; +} + +/* + * Error handler for usb_get/set_* functions. Return value > 0 success, + * 0 unknown or temporary failure (ignored), < 0 permanent failure (reconnect) + */ +static int nut_libusb_strerror(const int ret, const char *desc) +{ + if (ret > 0) { + return ret; + } + + switch(ret) + { +#if 0 + /* FIXME: not sure how to map these ones! */ + case LIBUSB_ERROR_INVALID_PARAM: /** Invalid parameter */ + /** System call interrupted (perhaps due to signal) */ + case LIBUSB_ERROR_INTERRUPTED: + /** Insufficient memory */ + case LIBUSB_ERROR_NO_MEM: +#endif + + case LIBUSB_ERROR_BUSY: /** Resource busy */ + case LIBUSB_ERROR_NO_DEVICE: /** No such device (it may have been disconnected) */ + case LIBUSB_ERROR_ACCESS: /** Access denied (insufficient permissions) */ + case LIBUSB_ERROR_IO: /** Input/output error */ + case LIBUSB_ERROR_NOT_FOUND: /** Entity not found */ + case LIBUSB_ERROR_PIPE: /** Pipe error */ + /** Operation not supported or unimplemented on this platform */ + case LIBUSB_ERROR_NOT_SUPPORTED: + upslogx(LOG_DEBUG, "%s: %s", desc, libusb_strerror((enum libusb_error)ret)); + return ret; + + case LIBUSB_ERROR_TIMEOUT: /** Operation timed out */ + upsdebugx(2, "%s: Connection timed out", desc); + return 0; + + case LIBUSB_ERROR_OVERFLOW: /** Overflow */ +#ifdef EPROTO +/* FIXME: not sure how to map this one! */ + case -EPROTO: /* Protocol error */ +#endif + upsdebugx(2, "%s: %s", desc, libusb_strerror((enum libusb_error)ret)); + return 0; + + case LIBUSB_ERROR_OTHER: /** Other error */ + default: /** Undetermined, log only */ + upslogx(LOG_DEBUG, "%s: %s", desc, libusb_strerror((enum libusb_error)ret)); + return 0; + } +} + +/* return the report of ID=type in report + * return -1 on failure, report length on success + */ + +/* Expected evaluated types for the API: + * static int nut_libusb_get_report(libusb_device_handle *udev, + * int ReportId, unsigned char *raw_buf, int ReportSize) + */ +static int nut_libusb_get_report( + libusb_device_handle *udev, + usb_ctrl_repindex ReportId, + usb_ctrl_charbuf raw_buf, + usb_ctrl_charbufsize ReportSize) +{ + int ret; + + upsdebugx(4, "Entering libusb_get_report"); + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif + if (!udev + || ReportId < 0 || (uintmax_t)ReportId > UINT16_MAX + || ReportSize < 0 || (uintmax_t)ReportSize > UINT16_MAX + ) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic pop +#endif + return 0; + } + + /* libusb0: USB_ENDPOINT_IN + USB_TYPE_CLASS + USB_RECIP_INTERFACE */ + ret = libusb_control_transfer(udev, + LIBUSB_ENDPOINT_IN|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE, + 0x01, /* HID_REPORT_GET */ + (uint16_t)ReportId + (0x03<<8), /* HID_REPORT_TYPE_FEATURE */ + usb_subdriver.hid_rep_index, + raw_buf, (uint16_t)ReportSize, USB_TIMEOUT); + + /* Ignore "protocol stall" (for unsupported request) on control endpoint */ + if (ret == LIBUSB_ERROR_PIPE) { + return 0; + } + + return nut_libusb_strerror(ret, __func__); +} + +/* Expected evaluated types for the API: + * static int nut_libusb_set_report(libusb_device_handle *udev, + * int ReportId, unsigned char *raw_buf, int ReportSize) + */ +static int nut_libusb_set_report( + libusb_device_handle *udev, + usb_ctrl_repindex ReportId, + usb_ctrl_charbuf raw_buf, + usb_ctrl_charbufsize ReportSize) +{ + int ret; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif + if (!udev + || ReportId < 0 || (uintmax_t)ReportId > UINT16_MAX + || ReportSize < 0 || (uintmax_t)ReportSize > UINT16_MAX + ) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic pop +#endif + return 0; + } + + /* libusb0: USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE */ + ret = libusb_control_transfer(udev, + LIBUSB_ENDPOINT_OUT|LIBUSB_REQUEST_TYPE_CLASS|LIBUSB_RECIPIENT_INTERFACE, + 0x09, /* HID_REPORT_SET = 0x09*/ + (uint16_t)ReportId + (0x03<<8), /* HID_REPORT_TYPE_FEATURE */ + usb_subdriver.hid_rep_index, + raw_buf, (uint16_t)ReportSize, USB_TIMEOUT); + + /* Ignore "protocol stall" (for unsupported request) on control endpoint */ + if (ret == LIBUSB_ERROR_PIPE) { + return 0; + } + + return nut_libusb_strerror(ret, __func__); +} + +/* Expected evaluated types for the API: + * static int nut_libusb_get_string(libusb_device_handle *udev, + * int StringIdx, char *buf, int buflen) + */ +static int nut_libusb_get_string( + libusb_device_handle *udev, + usb_ctrl_strindex StringIdx, + char *buf, + usb_ctrl_charbufsize buflen) +{ + int ret; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif + if (!udev + || StringIdx < 0 || (uintmax_t)StringIdx > UINT8_MAX + || buflen < 0 || (uintmax_t)buflen > (uintmax_t)INT_MAX + ) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic pop +#endif + return -1; + } + + ret = libusb_get_string_descriptor_ascii(udev, (uint8_t)StringIdx, + (unsigned char*)buf, (int)buflen); + + return nut_libusb_strerror(ret, __func__); +} + +/* Expected evaluated types for the API: + * static int nut_libusb_get_interrupt(libusb_device_handle *udev, + * unsigned char *buf, int bufsize, int timeout) + */ +static int nut_libusb_get_interrupt( + libusb_device_handle *udev, + usb_ctrl_charbuf buf, + usb_ctrl_charbufsize bufsize, + usb_ctrl_timeout_msec timeout) +{ + int ret, tmpbufsize; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif + if (!udev + || bufsize < 0 || (uintmax_t)bufsize > (uintmax_t)INT_MAX + ) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic pop +#endif + return -1; + } + + /* NOTE: With all the fuss about word sized arguments, + * the libusb_interrupt_transfer() lengths are about ints: + * int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, + * unsigned char endpoint, unsigned char *data, int length, + * int *actual_length, unsigned int timeout); + */ + tmpbufsize = (int)bufsize; + + /* FIXME: hardcoded interrupt EP => need to get EP descr for IF descr */ + /* ret = libusb_interrupt_transfer(udev, 0x81, buf, bufsize, &bufsize, timeout); */ + /* libusb0: ret = usb_interrupt_read(udev, USB_ENDPOINT_IN + usb_subdriver.hid_ep_in, (char *)buf, bufsize, timeout); */ + /* Interrupt EP is LIBUSB_ENDPOINT_IN with offset defined in hid_ep_in, which is 0 by default, unless overridden in subdriver. */ + ret = libusb_interrupt_transfer(udev, + LIBUSB_ENDPOINT_IN + usb_subdriver.hid_ep_in, + (unsigned char *)buf, tmpbufsize, &tmpbufsize, timeout); + + /* Clear stall condition */ + if (ret == LIBUSB_ERROR_PIPE) { + ret = libusb_clear_halt(udev, 0x81); + } + + /* In case of success, return the operation size, as done with libusb 0.1 */ + if (ret == LIBUSB_SUCCESS) { + if (tmpbufsize < 0 + || (uintmax_t)tmpbufsize > (uintmax_t)USB_CTRL_CHARBUFSIZE_MAX + ) { + return -1; + } + ret = (usb_ctrl_charbufsize)bufsize; + } + + return nut_libusb_strerror(ret, __func__); +} + +static void nut_libusb_close(libusb_device_handle *udev) +{ + if (!udev) { + return; + } + + /* usb_release_interface() sometimes blocks and goes + * into uninterruptible sleep. So don't do it. + */ + /* libusb_release_interface(udev, usb_subdriver.hid_rep_index); */ + libusb_close(udev); + libusb_exit(NULL); +} + +usb_communication_subdriver_t usb_subdriver = { + USB_DRIVER_NAME, + USB_DRIVER_VERSION, + nut_libusb_open, + nut_libusb_close, + nut_libusb_get_report, + nut_libusb_set_report, + nut_libusb_get_string, + nut_libusb_get_interrupt, + LIBUSB_DEFAULT_INTERFACE, + LIBUSB_DEFAULT_DESC_INDEX, + LIBUSB_DEFAULT_HID_EP_IN, + LIBUSB_DEFAULT_HID_EP_OUT +}; diff --git a/drivers/liebert-esp2.c b/drivers/liebert-esp2.c index fca7c5e..f8e8e85 100644 --- a/drivers/liebert-esp2.c +++ b/drivers/liebert-esp2.c @@ -2,6 +2,7 @@ * * Copyright (C) * 2009 Richard Gregory + * 2017 Nash Kaminski * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,53 +28,53 @@ #define IsBitSet(val, bit) ((val) & (1 << (bit))) #define DRIVER_NAME "Liebert ESP-II serial UPS driver" -#define DRIVER_VERSION "0.03" +#define DRIVER_VERSION "0.05" #define UPS_SHUTDOWN_DELAY 12 /* it means UPS will be shutdown 120 sec */ #define SHUTDOWN_CMD_LEN 8 /* values for sending to UPS */ enum mult_enum { - M_10, - M_0_1, - M_VOLTAGE_I, - M_VOLTAGE_O, - M_VOLTAGE_B, - M_CURRENT_I, - M_CURRENT_O, - M_CURRENT_B, - M_LOAD_VA, - M_LOAD_WATT, - M_FREQUENCY, - M_VOLT_DC, - M_TEMPERATURE, - M_CURRENT_DC , - M_BAT_RUNTIME, - M_NOMPOWER, - M_POWER, - M_REALPOWER, - M_LOADPERC + M_10, + M_0_1, + M_VOLTAGE_I, + M_VOLTAGE_O, + M_VOLTAGE_B, + M_CURRENT_I, + M_CURRENT_O, + M_CURRENT_B, + M_LOAD_VA, + M_LOAD_WATT, + M_FREQUENCY, + M_VOLT_DC, + M_TEMPERATURE, + M_CURRENT_DC , + M_BAT_RUNTIME, + M_NOMPOWER, + M_POWER, + M_REALPOWER, + M_LOADPERC }; static float multi[19]={ - 10.0, - 0.1, - 0.1, /* volt */ - 0.1, - 0.1, - 0.1, /* curr */ - 0.1, - 0.1, - 100.0, /* va */ - 100.0, /* W */ - 0.01, /* FREQ */ - 0.1, /* V DC*/ - 0.1, /* TEMP*/ - 0.01, /* CUR DC*/ - 60.0, /* BAT RUNTIME*/ - 100.0, /* NOMPOWER*/ - 100.0, /* POWER*/ - 100.0, /* REAL POWER*/ - 1.0 /* LOADPERC*/ + 10.0, + 0.1, + 0.1, /* volt */ + 0.1, + 0.1, + 0.1, /* curr */ + 0.1, + 0.1, + 100.0, /* va */ + 100.0, /* W */ + 0.01, /* FREQ */ + 0.1, /* V DC*/ + 0.1, /* TEMP*/ + 0.01, /* CUR DC*/ + 60.0, /* BAT RUNTIME*/ + 100.0, /* NOMPOWER*/ + 100.0, /* POWER*/ + 100.0, /* REAL POWER*/ + 1.0 /* LOADPERC*/ }; static int instcmd(const char *cmdname, const char *extra); @@ -84,7 +85,8 @@ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Richard Gregory \n" \ - "Robert Jobbagy ", DRV_EXPERIMENTAL, { NULL } }; @@ -98,19 +100,27 @@ static const unsigned char cmd_bitfield7[] = { 1,148,2,1,7,159 }, /* AMBIENT_OVERTEMP (2) */ cmd_battestres[] = { 1,148,2,1,12,164 }, /* BATTERY_TEST_RESULT */ cmd_selftestres[] = { 1,148,2,1,13,165 }, /* SELF_TEST_RESULT */ - cmd_upstype[] = { 1,136,2,1,1,141}, /* type bits + number of phases in bit groups*/ - cmd_scaling1[] = { 1,131,2,1,2,137}, /* part of multiplier information*/ + cmd_upstype[] = { 1,136,2,1,1,141}, /* type bits + number of phases in bit groups*/ + cmd_scaling1[] = { 1,131,2,1,2,137}, /* part of multiplier information*/ - /* Shutdown commands by Robert Jobbagy */ + /* Shutdown commands by Robert Jobbagy */ cmd_setOutOffMode[] = { 1,156,4,1,6,0,1,169}, /* UPS OutOffMode command */ cmd_setOutOffDelay[] = {1,156,4,1,5,0,UPS_SHUTDOWN_DELAY,167+UPS_SHUTDOWN_DELAY}, /* UPS Shutdown with delay */ - cmd_sysLoadKey[] = {1,156,2,1,7,167}, /* UPS SysLoadKey */ - cmd_shutdown[] = {1,156,4,1,136,76,76,194}; /* UPS shutdown */ + cmd_sysLoadKey[] = {1,156,2,1,7,167}, /* UPS SysLoadKey */ + cmd_shutdown[] = {1,156,4,1,136,76,76,194}; /* UPS shutdown */ -static int num_inphases = 1, num_outphases = 1; +/* Quiesce the compiler warnings about the fields below */ +static void NUT_UNUSED_FUNCTION_dummy_bitfields(void) +{ + NUT_UNUSED_VARIABLE(cmd_battestres); + NUT_UNUSED_VARIABLE(cmd_selftestres); + NUT_UNUSED_VARIABLE(cmd_bitfield7); +} + +static unsigned int num_inphases = 1, num_outphases = 1; static char cksum(const char *buf, const size_t len) - { +{ char sum = 0; size_t i; @@ -121,34 +131,34 @@ static char cksum(const char *buf, const size_t len) return sum; } -static int do_command(const unsigned char *command, char *reply, int cmd_len) +static ssize_t do_command(const unsigned char *command, char *reply, size_t cmd_len) { - int ret; + ssize_t ret; ret = ser_send_buf(upsfd, command, cmd_len); if (ret < 0) { upsdebug_with_errno(2, "send"); return -1; - } else if (ret < cmd_len) { - upsdebug_hex(2, "send: truncated", command, ret); + } else if ((size_t)ret < cmd_len) { + upsdebug_hex(2, "send: truncated", command, (size_t)ret); return -1; } - upsdebug_hex(2, "send", command, ret); + upsdebug_hex(2, "send", command, (size_t)ret); ret = ser_get_buf_len(upsfd, reply, 8, 1, 0); /* it needs that this driver works with USB to Serial cable */ if (ret < 0) { upsdebug_with_errno(2, "read"); return -1; } else if (ret < 6) { - upsdebug_hex(2, "read: truncated", reply, ret); + upsdebug_hex(2, "read: truncated", reply, (size_t)ret); return -1; } else if (reply[7] != cksum(reply, 7)) { - upsdebug_hex(2, "read: checksum error", reply, ret); + upsdebug_hex(2, "read: checksum error", reply, (size_t)ret); return -1; } - upsdebug_hex(2, "read", reply, ret); + upsdebug_hex(2, "read", reply, (size_t)ret); return ret; } @@ -158,15 +168,16 @@ void upsdrv_initinfo(void) const char *var; unsigned char len; } vartab[] = { - { "ups.model",15 }, - { "ups.firmware",8 }, - { "ups.serial",10 }, - { "ups.mfr.date",4 }, - { NULL } + { "ups.model", 15 }, + { "ups.firmware", 8 }, + { "ups.serial", 10 }, + { "ups.mfr.date", 4 }, + { NULL, 0 } }; char buf[LARGEBUF]; - int i,bitn,vari,ret=0,offset=4,readok=0; + int i, bitn, vari, offset=4, readok=0; + ssize_t ret=0; char command[6], reply[8]; unsigned int value; @@ -177,20 +188,20 @@ void upsdrv_initinfo(void) for (i = 0; i < vartab[vari].len; i++) { snprintf(command, sizeof(command), "\x01\x88\x02\x01%c", i+offset); - command[5] = cksum(command, 5); + command[5] = cksum(command, 5); - ret = do_command((unsigned char *)command, reply, 6); + ret = do_command((unsigned char *)command, reply, 6); if (ret < 8) { - upsdebug_hex(2, "send: truncated", command, ret); + upsdebug_hex(2, "send: truncated", command, (size_t)ret); break; } buf[i<<1] = reply[6]; buf[(i<<1)+1] = reply[5]; - } + } - buf[i<<1] = 0; - upsdebugx(1, "return: %d (8=success)", ret); + buf[i<<1] = 0; + upsdebugx(1, "return: %zd (8=success)", ret); if (ret == 8) { /* last command successful */ dstate_setinfo(vartab[vari].var,"%s",buf); @@ -206,7 +217,7 @@ void upsdrv_initinfo(void) memcpy(command,cmd_upstype,6); ret = do_command((unsigned char *)command, reply, 6); if (ret < 8) { - upsdebug_hex(2, "send: phase detection: truncated", command, ret); + upsdebug_hex(2, "send: phase detection: truncated", command, (size_t)ret); } else { /* input: from bit 0 to bit 1 (2 bits) */ @@ -237,18 +248,19 @@ void upsdrv_initinfo(void) } /* determine scaling */ - /* full scaling output not defined yet, but we can differentiate sets of + /* full scaling output not defined yet, but we can differentiate sets of * multipliers based on a sample scaling reading */ memcpy(command,cmd_scaling1,6); ret = do_command((unsigned char *)command, reply, 6); if (ret < 8) { - upsdebug_hex(2, "send: scaling detection: truncated", command, ret); + upsdebug_hex(2, "send: scaling detection: truncated", command, (size_t)ret); } else { /* add here multipliers that differentiate between models */ switch (reply[6]) { case 1: /* GXT-2 */ multi[M_FREQUENCY]=0.1; - multi[M_VOLT_DC]=1.0; + /* Confirmed correct on a Liebert GXT2-6000RT208 */ + multi[M_VOLT_DC]=0.1; multi[M_POWER]=1.0; multi[M_NOMPOWER]=1.0; break; @@ -310,16 +322,27 @@ void upsdrv_updateinfo(void) { { 0 }, NULL, NULL, 0 } }; + static cmd_s vartab2o[] = { /*split-phase out, only V line-line is reported*/ + { { 1,144,2,1,24,172 }, "output.L1.power.percent", "%.0f", M_LOADPERC }, + { { 1,145,2,1,24,173 }, "output.L2.power.percent", "%.0f", M_LOADPERC }, + { { 1,144,2,1,22,170 }, "output.L1.power", "%.0f", M_POWER }, + { { 1,145,2,1,22,171 }, "output.L2.power", "%.0f", M_POWER }, + { { 1,144,2,1,21,169 }, "output.L1.realpower", "%.0f", M_POWER }, + { { 1,145,2,1,21,170 }, "output.L2.realpower", "%.0f", M_POWER }, + { { 1,144,2,1,3,151 }, "output.voltage", "%.1f", M_VOLTAGE_O }, + { { 0 }, NULL, NULL, 0 } + }; + static cmd_s vartab3o[] = { /*3-phase out */ - { { 1,144,2,1,24,172 }, "ups.L1.load", "%.0f", M_LOADPERC }, - { { 1,145,2,1,24,173 }, "ups.L2.load", "%.0f", M_LOADPERC }, - { { 1,146,2,1,24,174 }, "ups.L3.load", "%.0f", M_LOADPERC }, - { { 1,144,2,1,22,170 }, "ups.L1.power", "%.0f", M_POWER }, - { { 1,145,2,1,22,171 }, "ups.L2.power", "%.0f", M_POWER }, - { { 1,146,2,1,22,172 }, "ups.L3.power", "%.0f", M_POWER }, - { { 1,144,2,1,21,169 }, "ups.L1.realpower", "%.0f", M_POWER }, - { { 1,145,2,1,21,170 }, "ups.L2.realpower", "%.0f", M_POWER }, - { { 1,146,2,1,21,171 }, "ups.L3.realpower", "%.0f", M_POWER }, + { { 1,144,2,1,24,172 }, "output.L1.power.percent", "%.0f", M_LOADPERC }, + { { 1,145,2,1,24,173 }, "output.L2.power.percent", "%.0f", M_LOADPERC }, + { { 1,146,2,1,24,174 }, "output.L3.power.percent", "%.0f", M_LOADPERC }, + { { 1,144,2,1,22,170 }, "output.L1.power", "%.0f", M_POWER }, + { { 1,145,2,1,22,171 }, "output.L2.power", "%.0f", M_POWER }, + { { 1,146,2,1,22,172 }, "output.L3.power", "%.0f", M_POWER }, + { { 1,144,2,1,21,169 }, "output.L1.realpower", "%.0f", M_POWER }, + { { 1,145,2,1,21,170 }, "output.L2.realpower", "%.0f", M_POWER }, + { { 1,146,2,1,21,171 }, "output.L3.realpower", "%.0f", M_POWER }, { { 1,144,2,1,3,151 }, "output.L1-N.voltage", "%.1f", M_VOLTAGE_O }, { { 1,145,2,1,3,152 }, "output.L2-N.voltage", "%.1f", M_VOLTAGE_O }, { { 1,146,2,1,3,153 }, "output.L3-N.voltage", "%.1f", M_VOLTAGE_O }, @@ -329,6 +352,17 @@ void upsdrv_updateinfo(void) { { 0 }, NULL, NULL, 0 } }; + static cmd_s vartab2i[] = { /*split-phase in, only reports V L-L */ + { { 1,144,2,1,1,149 }, "input.voltage", "%.1f", M_VOLTAGE_I }, + + { { 1,144,2,1,5,153 }, "input.bypass.voltage", "%.1f", M_VOLTAGE_B }, + + { { 1,144,2,1,6,154 }, "input.bypass.current", "%.1f", M_CURRENT_B }, + + { { 1,144,2,1,2,150 }, "input.current", "%.1f", M_CURRENT_I }, + { { 0 }, NULL, NULL, 0 } + }; + static cmd_s vartab3i[] = { /*3-phase in */ { { 1,144,2,1,1,149 }, "input.L1-N.voltage", "%.1f", M_VOLTAGE_I }, { { 1,145,2,1,1,150 }, "input.L2-N.voltage", "%.1f", M_VOLTAGE_I }, @@ -347,62 +381,105 @@ void upsdrv_updateinfo(void) { { 1,146,2,1,2,152 }, "input.L3.current", "%.1f", M_CURRENT_I }, { { 0 }, NULL, NULL, 0 } }; - + static cmd_s * cmdin_p; static cmd_s * cmdout_p; const char *val; char reply[8]; - int ret, i; + ssize_t ret; + int i; for (i = 0; vartab[i].var; i++) { - int16_t val; + int16_t intval; ret = do_command(vartab[i].cmd, reply, 6); if (ret < 8) { continue; } - val = (unsigned char)reply[5]; - val <<= 8; - val += (unsigned char)reply[6]; - dstate_setinfo(vartab[i].var, vartab[i].fmt, val * multi[vartab[i].multindex]); + intval = (unsigned char)reply[5]; + intval <<= 8; + intval += (unsigned char)reply[6]; +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + dstate_setinfo(vartab[i].var, vartab[i].fmt, multi[vartab[i].multindex] * intval); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif } - if (num_inphases>1){ + if (num_inphases==3){ cmdin_p=vartab3i; } + else if(num_inphases==2){ + cmdin_p=vartab2i; + } else { cmdin_p=vartab1i; } - if (num_outphases>1){ + if (num_outphases==3){ cmdout_p=vartab3o; } + else if(num_outphases==2){ + cmdout_p=vartab2o; + } else { cmdout_p=vartab1o; } for (i = 0; cmdin_p[i].var; i++) { - int16_t val; + int16_t intval; ret = do_command(cmdin_p[i].cmd, reply, 6); if (ret < 8) { continue; } - val = (unsigned char)reply[5]; - val <<= 8; - val += (unsigned char)reply[6]; - dstate_setinfo(cmdin_p[i].var, cmdin_p[i].fmt, val * multi[cmdin_p[i].multindex]); + intval = (unsigned char)reply[5]; + intval <<= 8; + intval += (unsigned char)reply[6]; +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + dstate_setinfo(cmdin_p[i].var, cmdin_p[i].fmt, multi[cmdin_p[i].multindex] * intval); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif } for (i = 0; cmdout_p[i].var; i++) { - int16_t val; + int16_t intval; ret = do_command(cmdout_p[i].cmd, reply, 6); if (ret < 8) { continue; } - val = (unsigned char)reply[5]; - val <<= 8; - val += (unsigned char)reply[6]; - dstate_setinfo(cmdout_p[i].var, cmdout_p[i].fmt, val * multi[cmdout_p[i].multindex]); + intval = (unsigned char)reply[5]; + intval <<= 8; + intval += (unsigned char)reply[6]; +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + dstate_setinfo(cmdout_p[i].var, cmdout_p[i].fmt, multi[cmdout_p[i].multindex] * intval); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif } status_init(); @@ -476,12 +553,12 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { char reply[8]; - + if(!(do_command(cmd_setOutOffMode, reply, 8) != -1) && - (do_command(cmd_setOutOffDelay, reply, 8) != -1) && - (do_command(cmd_sysLoadKey, reply, 6) != -1) && - (do_command(cmd_shutdown, reply, 8) != -1)) - upslogx(LOG_ERR, "Failed to shutdown UPS"); + (do_command(cmd_setOutOffDelay, reply, 8) != -1) && + (do_command(cmd_sysLoadKey, reply, 6) != -1) && + (do_command(cmd_shutdown, reply, 8) != -1)) + upslogx(LOG_ERR, "Failed to shutdown UPS"); } static int instcmd(const char *cmdname, const char *extra) @@ -491,8 +568,8 @@ static int instcmd(const char *cmdname, const char *extra) ser_send_buf(upsfd, ...); return STAT_INSTCMD_HANDLED; } - */ - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); +*/ + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -504,8 +581,8 @@ static int setvar(const char *varname, const char *val) ser_send_buf(upsfd, ...); return STAT_SET_HANDLED; } - */ - upslogx(LOG_NOTICE, "setvar: unknown variable [%s]", varname); +*/ + upslogx(LOG_NOTICE, "setvar: unknown variable [%s] [%s]", varname, val); return STAT_SET_UNKNOWN; } @@ -524,6 +601,9 @@ void upsdrv_initups(void) const char *val = getval("baudrate"); speed_t baudrate = B2400; + /* No-op, just made to quiesce the compiler warnings */ + NUT_UNUSED_FUNCTION_dummy_bitfields(); + if (val) { switch (atoi(val)) { diff --git a/drivers/liebert-hid.c b/drivers/liebert-hid.c index 2cce0ee..23851d8 100644 --- a/drivers/liebert-hid.c +++ b/drivers/liebert-hid.c @@ -4,6 +4,7 @@ * 2003 - 2008 Arnaud Quette * 2005 - 2006 Peter Selinger * 2007 Charles Lepple + * 2018 Markus "Links2004" * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,19 +27,24 @@ #include "liebert-hid.h" #include "usb-common.h" -#define LIEBERT_HID_VERSION "Liebert HID 0.3" +#define LIEBERT_HID_VERSION "Phoenixtec/Liebert HID 0.41" /* FIXME: experimental flag to be put in upsdrv_info */ -/* Phoenixtec */ +/* Phoenixtec Power Co., Ltd */ #define LIEBERT_VENDORID 0x06da -/* USB IDs device table */ +/*! USB IDs device table. + * + * Note that this subdriver was named before the USB VendorID was determined to + * actually belong to Phoenixtec. The belkin-hid.c file covers the other + * Liebert units which share some of the same incorrect exponents as the + * Belkin HID firmware. */ static usb_device_id_t liebert_usb_device_table[] = { /* various models */ { USB_DEVICE(LIEBERT_VENDORID, 0xffff), NULL }, /* Terminating entry */ - { -1, -1, NULL } + { 0, 0, NULL } }; /* --------------------------------------------------------------- */ @@ -73,14 +79,28 @@ static hid_info_t liebert_hid2nut[] = { { "unmapped.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%.0f", 0, NULL }, #endif - { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.0f", 0, NULL }, - { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.0f", 0, NULL }, + { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.2f", 0, NULL }, + { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.2f", HU_FLAG_STATIC, NULL }, { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_conversion }, { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", NULL, "%.0f", 0, NULL }, + { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.1f", 0, NULL }, + { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", NULL, "%.2f", 0, NULL }, + + { "output.transfer.high", 0, 0, "UPS.PowerConverter.Output.HighVoltageTransfer", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, + { "output.transfer.low", 0, 0, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, + + { "input.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Voltage", NULL, "%.1f", 0, NULL }, + { "input.frequency", 0, 0, "UPS.PowerConverter.Input.[1].Frequency", NULL, "%.2f", 0, NULL }, + + { "input.transfer.low", 0, 0, "UPS.PowerConverter.Output.ffff0057", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "input.transfer.high", 0, 0, "UPS.PowerConverter.Output.ffff0058", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL}, + { "input.frequency.transfer.low", 0, 0, "UPS.PowerConverter.Output.ffff00f9", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "input.frequency.transfer.high", 0, 0, "UPS.PowerConverter.Output.ffff00f8", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL}, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, "%.0f", HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, "%.0f", HU_FLAG_QUICK_POLL, lowbatt_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, "%.0f", HU_FLAG_QUICK_POLL, charging_info }, @@ -108,8 +128,7 @@ static const char *liebert_format_serial(HIDDevice_t *hd) { * the device is supported by this subdriver, else 0. */ static int liebert_claim(HIDDevice_t *hd) { - int status = is_usb_device_supported(liebert_usb_device_table, hd->VendorID, - hd->ProductID); + int status = is_usb_device_supported(liebert_usb_device_table, hd); switch (status) { @@ -138,4 +157,5 @@ subdriver_t liebert_subdriver = { liebert_format_model, liebert_format_mfr, liebert_format_serial, + fix_report_desc, }; diff --git a/drivers/liebert-hid.h b/drivers/liebert-hid.h index 5adb5d2..8f84fc4 100644 --- a/drivers/liebert-hid.h +++ b/drivers/liebert-hid.h @@ -2,7 +2,7 @@ * * Copyright (C) * 2003 - 2005 Arnaud Quette - * 2005 - 2006 Peter Selinger + * 2005 - 2006 Peter Selinger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/drivers/liebert.c b/drivers/liebert.c index b2faa29..65bb539 100644 --- a/drivers/liebert.c +++ b/drivers/liebert.c @@ -21,11 +21,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" #include "main.h" #include "serial.h" +#include "attribute.h" #define DRIVER_NAME "Liebert MultiLink UPS driver" -#define DRIVER_VERSION "1.02" +#define DRIVER_VERSION "1.03" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -39,6 +41,9 @@ upsdrv_info_t upsdrv_info = { #define ML_ONBATTERY 0x55 +void upsdrv_shutdown(void) + __attribute__((noreturn)); + void upsdrv_shutdown(void) { /* XXX: replace with a proper shutdown function (raise DTR) */ @@ -92,7 +97,7 @@ void upsdrv_updateinfo(void) if (c == ML_ONBATTERY) ob = 1; } - + if (ser_get_dcd(upsfd)) lb = 1; diff --git a/drivers/macosx-ups.c b/drivers/macosx-ups.c new file mode 100644 index 0000000..94b8b01 --- /dev/null +++ b/drivers/macosx-ups.c @@ -0,0 +1,469 @@ +/* Bridge driver to read Mac OS X UPS status (as displayed in Energy Saver control panel) + * + * Copyright (C) 2011-2012, 2015 Charles Lepple + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "config.h" +#include "main.h" +#include "attribute.h" + +#include + +#include "CoreFoundation/CoreFoundation.h" +#include "IOKit/ps/IOPowerSources.h" +#include "IOKit/ps/IOPSKeys.h" + +#define DRIVER_NAME "Mac OS X UPS meta-driver" +#define DRIVER_VERSION "1.3" + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Charles Lepple ", + DRV_EXPERIMENTAL, + { NULL } +}; + +#if 0 +#define CFRelease(ref) do { upsdebugx(3, "%s:%d: CFRelease(%p)", __FILE__, __LINE__, ref); CFRelease(ref); } while(0) +#endif + +static CFStringRef g_power_source_name = NULL; +static double max_capacity_value = 100.0; + +/*! Copy the current power dictionary. + * + * Caller must release power dictionary when finished with it. + */ +static CFDictionaryRef copy_power_dictionary(CFStringRef power_source_name) +{ + CFTypeRef power_sources_info, power_source; + CFArrayRef sources_list; + CFDictionaryRef this_power_dictionary, power_dictionary = NULL; + CFStringRef this_power_source_name; + CFIndex num_keys, index; + + power_sources_info = IOPSCopyPowerSourcesInfo(); + + assert(power_sources_info); + upsdebugx(6, "%s: Got power_sources_info:", __func__); + if(nut_debug_level >= 6) CFShow(power_sources_info); + + upsdebugx(5, "power_source_name = "); + if(nut_debug_level >= 5) CFShow(power_source_name); + upsdebugx(6, "end power_source_name"); + + sources_list = IOPSCopyPowerSourcesList(power_sources_info); + + num_keys = CFArrayGetCount(sources_list); + for(index=0; index < num_keys; index++) { + upsdebugx(6, "%s: Getting power source %ld/%ld...", __func__, index+1, num_keys); + power_source = CFArrayGetValueAtIndex(sources_list, index); + assert(power_source); + + upsdebugx(6, "%s: power source %ld = ", __func__, index+1); + if(nut_debug_level >= 6) CFShow(power_source); + + this_power_dictionary = IOPSGetPowerSourceDescription(power_sources_info, power_source); + assert(this_power_dictionary); + + this_power_source_name = CFDictionaryGetValue(this_power_dictionary, CFSTR(kIOPSNameKey)); + assert(this_power_source_name); + + if(!CFStringCompare(this_power_source_name, power_source_name, 0)) { + power_dictionary = this_power_dictionary; + CFRetain(power_dictionary); + break; + } + } + + if(power_dictionary) { + upsdebugx(5, "CFShowing 'power_dictionary'"); + if(nut_debug_level >= 5) CFShow(power_dictionary); + } + + /* Get a new power_sources_info next time: */ + CFRelease(power_sources_info); + CFRelease(sources_list); + + return power_dictionary; +} + +void upsdrv_initinfo(void) +{ + /* try to detect the UPS here - call fatal_with_errno(EXIT_FAILURE, ) if it fails */ + + char device_name[80] = ""; + CFStringRef device_type_cfstr, device_name_cfstr; + CFPropertyListRef power_dictionary; + CFNumberRef max_capacity; + + upsdebugx(1, "upsdrv_initinfo()"); + + dstate_setinfo("device.mfr", "(unknown)"); + dstate_setinfo("ups.mfr", "(unknown)"); + + power_dictionary = copy_power_dictionary(g_power_source_name); + + device_type_cfstr = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSTypeKey)); + if(device_type_cfstr && !CFStringCompare(device_type_cfstr, CFSTR(kIOPSInternalBatteryType), 0)) { + dstate_setinfo("device.type", "battery"); + } + + upsdebugx(2, "Getting 'Name' key"); + + device_name_cfstr = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSNameKey)); + + if (!device_name_cfstr) { + fatalx(EXIT_FAILURE, "Couldn't retrieve 'Name' key from power dictionary."); + } + + CFRetain(device_name_cfstr); + + CFStringGetCString(device_name_cfstr, device_name, sizeof(device_name), kCFStringEncodingUTF8); + upsdebugx(2, "Got name: %s", device_name); + + CFRelease(device_name_cfstr); + + dstate_setinfo("device.model", "%s", device_name); + dstate_setinfo("ups.model", "%s", device_name); + + max_capacity = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSMaxCapacityKey)); + if(max_capacity) { + CFRetain(max_capacity); + + CFNumberGetValue(max_capacity, kCFNumberDoubleType, &max_capacity_value); + CFRelease(max_capacity); + + upsdebugx(3, "Max Capacity = %.f units (usually 100)", max_capacity_value); + if(max_capacity_value != 100) { + upsdebugx(1, "Max Capacity: %f != 100", max_capacity_value); + } + } + + /* upsh.instcmd = instcmd; */ + CFRelease(power_dictionary); +} + +void upsdrv_updateinfo(void) +{ + CFPropertyListRef power_dictionary; + CFStringRef power_source_state; + CFNumberRef battery_voltage, battery_runtime; + CFNumberRef current_capacity; + CFBooleanRef is_charging; + double max_capacity_value = 100.0, current_capacity_value; + + upsdebugx(1, "upsdrv_updateinfo()"); + + power_dictionary = copy_power_dictionary( g_power_source_name ); + if(!power_dictionary) { + dstate_datastale(); + return; + } + + status_init(); + + /* Retrieve OL/OB state */ + power_source_state = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSPowerSourceStateKey)); + assert(power_source_state); + CFRetain(power_source_state); + + upsdebugx(3, "Power Source State:"); + if(nut_debug_level >= 3) CFShow(power_source_state); + + if(!CFStringCompare(power_source_state, CFSTR(kIOPSACPowerValue), 0)) { + status_set("OL"); + } else { + status_set("OB"); + } + + CFRelease(power_source_state); + + /* Retrieve CHRG state */ + is_charging = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSIsChargingKey)); + if(is_charging) { + Boolean is_charging_value; + + is_charging_value = CFBooleanGetValue(is_charging); + if(is_charging_value) { + status_set("CHRG"); + } + } + + status_commit(); + + /* Retrieve battery voltage */ + + battery_voltage = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSVoltageKey)); + if(battery_voltage) { + int battery_voltage_value; + + CFNumberGetValue(battery_voltage, kCFNumberIntType, &battery_voltage_value); + upsdebugx(2, "battery_voltage = %d mV", battery_voltage_value); + dstate_setinfo("battery.voltage", "%.3f", battery_voltage_value/1000.0); + } + + /* Retrieve battery runtime */ + battery_runtime = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSTimeToEmptyKey)); + if(battery_runtime) { + double battery_runtime_value; + + CFNumberGetValue(battery_runtime, kCFNumberDoubleType, &battery_runtime_value); + + upsdebugx(2, "battery_runtime = %.f minutes", battery_runtime_value); + if(battery_runtime_value > 0) { + dstate_setinfo("battery.runtime", "%d", (int)(battery_runtime_value*60)); + } else { + dstate_delinfo("battery.runtime"); + } + } else { + dstate_delinfo("battery.runtime"); + } + + /* Retrieve current capacity */ + current_capacity = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSCurrentCapacityKey)); + if(current_capacity) { + CFNumberGetValue(current_capacity, kCFNumberDoubleType, ¤t_capacity_value); + + upsdebugx(2, "Current Capacity = %.f/%.f units", current_capacity_value, max_capacity_value); + if(max_capacity_value > 0) { + dstate_setinfo("battery.charge", "%.f", 100.0 * current_capacity_value / max_capacity_value); + } + } + + /* TODO: it should be possible to set poll_interval (and maxage in the + * server) to an absurdly large value, and use notify(3) to get + * updates. + */ + + /* + * poll_interval = 2; + */ + + dstate_dataok(); + CFRelease(power_dictionary); +} + +void upsdrv_shutdown(void) + __attribute__((noreturn)); + +void upsdrv_shutdown(void) +{ + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ + + /* maybe try to detect the UPS here, but try a shutdown even if + it doesn't respond at first if possible */ + + /* NOTE: Mac OS X already has shutdown routines - this driver is more + for monitoring and notification purposes. Still, there is a key that + might be useful to set in SystemConfiguration land. */ + fatalx(EXIT_FAILURE, "shutdown not supported"); + + /* you may have to check the line status since the commands + for toggling power are frequently different for OL vs. OB */ + + /* OL: this must power cycle the load if possible */ + + /* OB: the load must remain off until the power returns */ +} + +/* +static int instcmd(const char *cmdname, const char *extra) +{ + if (!strcasecmp(cmdname, "test.battery.stop")) { + ser_send_buf(upsfd, ...); + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + return STAT_INSTCMD_UNKNOWN; +} +*/ + +/* TODO: + There is a configuration file here: + /Library/Preferences/SystemConfiguration/com.apple.PowerManagement.plist + with several keys under UPSDefaultThresholds: + + * UPSShutdownAfterMinutes + * UPSShutdownAtLevel + * UPSShutdownAtMinutesLeft + + It is not likely that these keys can be written, but they might be good values for NUT variables. + + In conjunction with 'ignorelb' and a delta, this could be used to synthesize a + LB status right before the computer shuts down. +*/ + +/* +static int setvar(const char *varname, const char *val) +{ + if (!strcasecmp(varname, "ups.test.interval")) { + ser_send_buf(upsfd, ...); + return STAT_SET_HANDLED; + } + + upslogx(LOG_NOTICE, "setvar: unknown variable [%s]", varname); + return STAT_SET_UNKNOWN; +} +*/ + +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + /* allow '-x xyzzy' */ + /* addvar(VAR_FLAG, "xyzzy", "Enable xyzzy mode"); */ + + /* allow '-x foo=' */ + /* addvar(VAR_VALUE, "foo", "Override foo setting"); */ + + addvar(VAR_VALUE, "model", "Regular Expression to match power source model name"); +} + +void upsdrv_initups(void) +{ + CFArrayRef power_source_key_list; + CFIndex num_keys, index; + CFDictionaryRef power_dictionary; + CFTypeRef power_blob; + CFStringRef potential_key, potential_model; + char *model_name; /* regex(3) */ + char potential_model_name[256]; + regex_t model_regex; + int ret; + + upsdebugx(3, "upsdrv_initups(): Power Sources blob:"); + /* upsfd = ser_open(device_path); */ + /* ser_set_speed(upsfd, device_path, B1200); */ + power_blob = IOPSCopyPowerSourcesInfo(); + if(!power_blob) { + fatalx(EXIT_FAILURE, "Couldn't retrieve Power Sources blob"); + } + + if(nut_debug_level >= 3) CFShow(power_blob); + +/* The CFDictionary through 10.9 has changed to a CFArray, so this part is no longer applicable: */ +#if 0 + if(!strcmp(device_name, "auto")) { + device_name = "/UPS"; + } + + upsdebugx(2, "Matching power supply key names against regex '%s'", device_name); + + ret = regcomp(&name_regex, device_name, REG_EXTENDED|REG_NOSUB|REG_ICASE); + + if(ret) { + fatalx(EXIT_FAILURE, + "Failed to compile regex from 'port' parameter: '%s'.", + device_name); + } +#endif + + if((model_name = getval("model"))) { + upsdebugx(2, "Matching power supply model names against regex '%s'", model_name); + ret = regcomp(&model_regex, model_name, REG_EXTENDED|REG_NOSUB|REG_ICASE); + + if(ret) { + fatalx(EXIT_FAILURE, + "Failed to compile regex from 'model' parameter: '%s'.", + model_name); + } + } + + power_source_key_list = IOPSCopyPowerSourcesList(power_blob); + + num_keys = CFArrayGetCount(power_source_key_list); + upsdebugx(1, "Number of power supplies found: %d", (int)num_keys); + + if(nut_debug_level >= 3) CFShow(power_source_key_list); + + if(num_keys < 1) { + /* bail */ + fatalx(EXIT_FAILURE, "Couldn't find any UPS or battery. Is your UPS shown in the 'Energy Saver' control panel?"); + } + + for(index=0; index < num_keys; index++) { + upsdebugx(2, "Retrieving power source #%ld", index+1); + potential_key = CFArrayGetValueAtIndex(power_source_key_list, index); + upsdebugx(3, "Power source key/index:"); + if(nut_debug_level >= 3) CFShow(potential_key); + + power_dictionary = IOPSGetPowerSourceDescription(power_blob, potential_key); + assert(power_dictionary); + CFRetain(power_dictionary); + + upsdebugx(2, "Getting 'Name' key (UPS model)"); + + potential_model = CFDictionaryGetValue(power_dictionary, CFSTR(kIOPSNameKey)); + CFRetain(potential_model); + if(CFStringGetCString(potential_model, potential_model_name, sizeof(potential_model_name), kCFStringEncodingUTF8)) { + upsdebugx(1, " model name: %s", potential_model_name); + } else { + upsdebugx(1, " (failed to retrieve 'Name')"); + } + + CFRelease(power_dictionary); + + if(model_name) { + ret = regexec(&model_regex, potential_model_name, 0,0,0); + if(!ret) { + upsdebugx(2, "Matched model name"); + break; + } + } + CFRelease(potential_model); + } + + if(model_name) { + regfree(&model_regex); + } + + if(ret) { + if(model_name) { + fatalx(EXIT_FAILURE, "Couldn't find UPS or battery matching 'model' (%s)", + model_name); + } else { + fatalx(EXIT_FAILURE, "Couldn't find an UPS or battery."); + } + } + + g_power_source_name = potential_model; + + /* the upsh handlers can't be done here, as they get initialized + * shortly after upsdrv_initups returns to main. + */ + + /* don't try to detect the UPS here */ + + /* do stuff */ +} + +void upsdrv_cleanup(void) +{ + upsdebugx(1, "Cleanup: release references"); + CFRelease(g_power_source_name); + + /* free(dynamic_mem); */ + /* ser_close(upsfd, device_path); */ +} diff --git a/drivers/main-hal.c b/drivers/main-hal.c deleted file mode 100644 index 8cd3275..0000000 --- a/drivers/main-hal.c +++ /dev/null @@ -1,571 +0,0 @@ -/* main-hal.c - Network UPS Tools driver core for HAL - - Copyright (C) 2006 Arnaud Quette - Copyright (C) 1999 Russell Kroll - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* TODO list: - * -more cleanup - * - dstate-hal: expose all data on org.freedesktop.NUT to prepare - * a full HAL/DBus enabled nut (remove the need of {d,s}state layer - * - use HAL logging functions - */ - -#include "main-hal.h" -#include "dstate-hal.h" -#include - -#ifdef HAVE_POLKIT -#include -#endif - - /* HAL specific */ - extern LibHalContext *halctx; - extern char *udi; - - /* data which may be useful to the drivers */ - int upsfd = -1; - char *device_path = NULL; - const char *progname = NULL, *upsname = NULL, - *device_name = NULL; - - /* may be set by the driver to wake up while in dstate_poll_fds */ - int extrafd = -1; - - /* for ser_open */ - int do_lock_port = 1; - - /* set by the drivers */ - int experimental_driver = 0; - int broken_driver = 0; - - static vartab_t *vartab_h = NULL; - - /* variables possibly set by the global part of ups.conf */ - unsigned int poll_interval = 2; - static char *chroot_path = NULL; - - /* signal handling */ - int exit_flag = 0; - - /* everything else */ - static char *pidfn = NULL; - GMainLoop *gmain; - char *dbus_methods_introspection; - -/* retrieve the value of variable if possible */ -char *getval(const char *var) -{ - vartab_t *tmp = vartab_h; - - while (tmp) { - if (!strcasecmp(tmp->var, var)) - return(tmp->val); - tmp = tmp->next; - } - - return NULL; -} - -/* see if has been defined, even if no value has been given to it */ -int testvar(const char *var) -{ - vartab_t *tmp = vartab_h; - - while (tmp) { - if (!strcasecmp(tmp->var, var)) - return tmp->found; - tmp = tmp->next; - } - - return 0; /* not found */ -} - -/* callback from driver - create the table for -x/conf entries */ -void addvar(int vartype, const char *name, const char *desc) -{ - vartab_t *tmp, *last; - - tmp = last = vartab_h; - - while (tmp) { - last = tmp; - tmp = tmp->next; - } - - tmp = xmalloc(sizeof(vartab_t)); - - tmp->vartype = vartype; - tmp->var = xstrdup(name); - tmp->val = NULL; - tmp->desc = xstrdup(desc); - tmp->found = 0; - tmp->next = NULL; - - if (last) - last->next = tmp; - else - vartab_h = tmp; -} - -static void vartab_free(void) -{ - vartab_t *tmp, *next; - - tmp = vartab_h; - - while (tmp) { - next = tmp->next; - - free(tmp->var); - free(tmp->val); - free(tmp->desc); - free(tmp); - - tmp = next; - } -} - -static void exit_cleanup(int sig) -{ - upsdebugx(2, "exit_cleanup(%i", sig); - - exit_flag = sig; - - upsdrv_cleanup(); - - free(chroot_path); - free(device_path); - - if (pidfn) { - unlink(pidfn); - free(pidfn); - } - - dstate_free(); - vartab_free(); - - /* break the main loop */ - g_main_loop_quit(gmain); -} - -static void setup_signals(void) -{ - struct sigaction sa; - - sigemptyset(&sa.sa_mask); - sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; - - sa.sa_handler = exit_cleanup; - sigaction(SIGTERM, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGQUIT, &sa, NULL); - -/* sa.sa_handler = SIG_IGN; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGPIPE, &sa, NULL);*/ -} - -/* (*GSourceFunc) wrapper */ -static gboolean update_data (gpointer data) -{ - upsdrv_updateinfo(); - return (exit_flag == 0); -} - -int main(int argc, char **argv) -{ - DBusError dbus_error; - DBusConnection *dbus_connection; - struct passwd *new_uid = NULL; - char *hal_debug_level; -/* int i, do_forceshutdown = 0; */ - - if (experimental_driver) { - printf("Warning: This is an experimental driver.\n"); - printf("Some features may not function correctly.\n\n"); - } - - progname = xbasename(argv[0]); - open_syslog(progname); - - dbus_methods_introspection = xmalloc(1024); - - /* Register the basic supported method (Shutdown) - * Leave other methods registration to driver core calls to - * dstate_addcmd(), at initinfo() time - */ - snprintf(dbus_methods_introspection, 1024, "%s", - " \n" -/* " \n" */ - " \n" - " \n"); - - /* initialise HAL and DBus interface*/ - halctx = NULL; - udi = getenv ("UDI"); - if (udi == NULL) { - fprintf(stderr, "Error: UDI is null.\n"); - exit(EXIT_FAILURE); - } - - dbus_error_init (&dbus_error); - if ((halctx = libhal_ctx_init_direct (&dbus_error)) == NULL) { - fprintf(stderr, "Error: can't initialise libhal.\n"); - exit(EXIT_FAILURE); - } - if (dbus_error_is_set (&dbus_error)) - { - fatalx(EXIT_FAILURE, "Error in context creation: %s\n", dbus_error.message); - dbus_error_free (&dbus_error); - } - - if ((dbus_connection = libhal_ctx_get_dbus_connection(halctx)) == NULL) { - fprintf(stderr, "Error: can't get DBus connection.\n"); - exit(EXIT_FAILURE); - } - - /* FIXME: rework HAL param interface! or get path/regex from UDI - * Example: - * /org/freedesktop/Hal/devices/usb_device_463_ffff_1H2E300AH - * => linux.device_file = /dev/bus/usb/002/065 - */ - /* FIXME: the naming should be abstracted to os.device_file! */ - device_path = xstrdup("auto"); - - /* FIXME: bridge debug/warning on HAL equivalent (need them - * to externalize these in a lib... */ - hal_debug_level = getenv ("NUT_HAL_DEBUG"); - if (hal_debug_level == NULL) - nut_debug_level = 0; - else - nut_debug_level = atoi(hal_debug_level); - - /* Sleep 2 seconds to be able to view the device through usbfs! */ - sleep(2); - - /* build the driver's extra (-x) variable table */ - upsdrv_makevartable(); - - /* Switch to the HAL user */ - new_uid = get_user_pwent(HAL_USER); - become_user(new_uid); - - /* Only switch to statepath if we're not powering off */ - /* This avoid case where ie /var is umounted */ -/* ?! Not needed for HAL !? - if (!do_forceshutdown) - if (chdir(dflt_statepath())) - fatal_with_errno(EXIT_FAILURE, "Can't chdir to %s", dflt_statepath()); -*/ - setup_signals(); - - /* clear out callback handler data */ - memset(&upsh, '\0', sizeof(upsh)); - - upsdrv_initups(); - -#if 0 - if (do_forceshutdown) - forceshutdown(); -#endif - - /* get the supported data and commands before allowing connections */ - upsdrv_initinfo(); - upsdrv_updateinfo(); - - /* now we can start servicing requests */ - dstate_init(NULL, NULL); - - /* Commit DBus methods */ - if (!libhal_device_claim_interface(halctx, udi, DBUS_INTERFACE, - dbus_methods_introspection, &dbus_error)) { - fprintf(stderr, "Cannot claim interface: %s\n", dbus_error.message); - } - else - fprintf(stdout, "Claimed the following DBus interfaces on %s:\n%s", - DBUS_INTERFACE, dbus_methods_introspection); - - /* Complete DBus binding */ - dbus_connection_setup_with_g_main(dbus_connection, NULL); - dbus_connection_add_filter(dbus_connection, dbus_filter_function, NULL, NULL); - dbus_connection_set_exit_on_disconnect(dbus_connection, 0); - - dbus_init_local(); - /* end of HAL init */ - -#if 0 - /* publish the top-level data: version number, driver name */ - dstate_setinfo("driver.version", "%s", UPS_VERSION); - dstate_setinfo("driver.name", "%s", progname); - - /* The poll_interval may have been changed from the default */ - dstate_setinfo("driver.parameter.pollinterval", "%d", poll_interval); - -/* FIXME: needed? */ - if (nut_debug_level == 0) { - background(); - writepid(pidfn); - } -#endif - /* End HAL init */ - dbus_error_init (&dbus_error); - if (!libhal_device_addon_is_ready (halctx, udi, &dbus_error)) { - fprintf(stderr, "Error (libhal): device addon is not ready\n"); - exit(EXIT_FAILURE); - } - - /* add a timer for data update */ -#ifdef HAVE_G_TIMEOUT_ADD_SECONDS - g_timeout_add_seconds (poll_interval, -#else - g_timeout_add (1000 * poll_interval, /* seconds */ -#endif - (GSourceFunc)update_data, - NULL); - - /* setup and run the main loop */ - gmain = g_main_loop_new(NULL, FALSE); - g_main_loop_run(gmain); - - /* reached upon addon exit */ - upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); - exit(EXIT_SUCCESS); -} - -/******************************************************************** - * DBus interface functions and data - *******************************************************************/ - -static DBusHandlerResult dbus_filter_function_local(DBusConnection *connection, - DBusMessage *message, - void *user_data) -{ - if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL, "Disconnected")) { - upsdebugx(1, "DBus daemon disconnected. Trying to reconnect..."); - dbus_connection_unref(connection); - g_timeout_add(5000, (GSourceFunc)dbus_init_local, NULL); - } - return DBUS_HANDLER_RESULT_HANDLED; -} - -/* returns FALSE on success because it's used as a callback */ -gboolean dbus_init_local(void) -{ - DBusConnection *dbus_connection; - DBusError dbus_error; - - dbus_error_init(&dbus_error); - - dbus_connection = dbus_bus_get(DBUS_BUS_SYSTEM, &dbus_error); - if (dbus_error_is_set(&dbus_error)) { - upsdebugx(1, "Cannot get D-Bus connection"); -/* dbus_error_free (&dbus_error); */ - return TRUE; - } - - dbus_connection_setup_with_g_main(dbus_connection, NULL); - dbus_connection_add_filter(dbus_connection, dbus_filter_function_local, - NULL, NULL); - dbus_connection_set_exit_on_disconnect(dbus_connection, 0); - return FALSE; -} - -#ifdef HAVE_POLKIT -/** - * dbus_is_privileged: - * @connection: connection to D-Bus - * @message: Message - * @error: the error - * - * Returns: TRUE if the caller is privileged - * - * checks if caller of message possesses the CPUFREQ_POLKIT_PRIVILGE - */ -static gboolean -dbus_is_privileged (DBusConnection *connection, DBusMessage *message, DBusError *error) -{ - gboolean ret; - char *polkit_result; - const char *invoked_by_syscon_name; - - ret = FALSE; - polkit_result = NULL; -/* FIXME: CPUFREQ_POLKIT_PRIVILEGE, CPUFREQ_ERROR_GENERAL */ - invoked_by_syscon_name = dbus_message_get_sender (message); - - polkit_result = libhal_device_is_caller_privileged (halctx, - udi, - CPUFREQ_POLKIT_PRIVILEGE, - invoked_by_syscon_name, - error); - if (polkit_result == NULL) { - dbus_raise_error (connection, message, CPUFREQ_ERROR_GENERAL, - "Cannot determine if caller is privileged"); - } - else { - if (strcmp (polkit_result, "yes") != 0) { - - dbus_raise_error (connection, message, - "org.freedesktop.Hal.Device.PermissionDeniedByPolicy", - "%s %s <-- (action, result)", - CPUFREQ_POLKIT_PRIVILEGE, polkit_result); - } - else - ret = TRUE; - } - - if (polkit_result != NULL) - libhal_free_string (polkit_result); - return ret; -} -#endif - -/** - * dbus_send_reply: - * @connection: connection to D-Bus - * @message: Message - * @type: the type of data param - * @data: data to send - * - * Returns: TRUE/FALSE - * - * sends a reply to message with the given data and its dbus_type - */ -static gboolean dbus_send_reply(DBusConnection *connection, DBusMessage *message, - int dbus_type, void *data) -{ - DBusMessage *reply; - - if ((reply = dbus_message_new_method_return(message)) == NULL) { - upslogx(LOG_WARNING, "Could not allocate memory for the DBus reply"); - return FALSE; - } - - if (data != NULL) - dbus_message_append_args(reply, dbus_type, data, DBUS_TYPE_INVALID); - - if (!dbus_connection_send(connection, reply, NULL)) { - upslogx(LOG_WARNING, "Could not sent reply"); - return FALSE; - } - dbus_connection_flush(connection); - dbus_message_unref(reply); - - return TRUE; -} - -/** - * dbus_get_argument: - * @connection: connection to D-Bus - * @message: Message - * @dbus_error: the D-Bus error - * @type: the type of arg param - * @arg: the value to get from the message - * - * Returns: TRUE/FALSE - * - * gets one argument from message with the given dbus_type and stores it in arg - */ -static gboolean dbus_get_argument(DBusConnection *connection, DBusMessage *message, - DBusError *dbus_error, int dbus_type, void *arg) -{ - dbus_message_get_args(message, dbus_error, dbus_type, arg, - DBUS_TYPE_INVALID); - if (dbus_error_is_set(dbus_error)) { - upslogx(LOG_WARNING, "Could not get argument of DBus message: %s", - dbus_error->message); - dbus_error_free(dbus_error); - return FALSE; - } - return TRUE; -} - -/** - * dbus_filter_function: - * @connection: connection to D-Bus - * @message: message - * @user_data: pointer to the data - * - * Returns: the result - * - * @raises UnknownMethod - * - * D-Bus filter function - */ -DBusHandlerResult dbus_filter_function(DBusConnection *connection, - DBusMessage *message, - void *user_data) -{ - DBusError dbus_error; - int retcode = -1; - const char *member = dbus_message_get_member(message); - const char *path = dbus_message_get_path(message); -/* int ret = DBUS_HANDLER_RESULT_NOT_YET_HANDLED; */ - - /* upsdebugx(2, "Received DBus message with member %s path %s", member, path); */ - fprintf(stdout, "Received DBus message with member %s on path %s\n", member, path); - - dbus_error_init(&dbus_error); - if (dbus_error_is_set (&dbus_error)) - { - fprintf (stderr, "an error occurred: %s\n", dbus_error.message); -/* dbus_error_free (&dbus_error); */ - } - else - { -#ifdef HAVE_POLKIT - if (!dbus_is_privileged(connection, message, &dbus_error)) - return DBUS_HANDLER_RESULT_HANDLED; -#endif - - if (dbus_message_is_method_call(message, DBUS_INTERFACE, "Shutdown")) { - - fprintf(stdout, "executing Shutdown\n"); - upsdrv_shutdown(); - dbus_send_reply(connection, message, DBUS_TYPE_INVALID, NULL); - - } else if (dbus_message_is_method_call(message, - DBUS_INTERFACE, "SetBeeper")) { - fprintf(stdout, "executing SetBeeper\n"); - gboolean b_enable; - if (!dbus_get_argument(connection, message, &dbus_error, - DBUS_TYPE_BOOLEAN, &b_enable)) { - fprintf(stderr, "Error receiving boolean argument\n"); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - fprintf(stdout, "Received argument: %s\n", (b_enable==TRUE)?"true":"false"); - - if (b_enable==TRUE) { - if (upsh.instcmd("beeper.enable", NULL) != STAT_INSTCMD_HANDLED) { - dbus_send_reply(connection, message, DBUS_TYPE_INT32, &retcode); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - } - else { - if (upsh.instcmd("beeper.disable", NULL) != STAT_INSTCMD_HANDLED) { - dbus_send_reply(connection, message, DBUS_TYPE_INT32, &retcode); - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - } - } else { - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; - } - } - dbus_send_reply(connection, message, DBUS_TYPE_INVALID, NULL); - return DBUS_HANDLER_RESULT_HANDLED; -} diff --git a/drivers/main-hal.h b/drivers/main-hal.h deleted file mode 100644 index e612133..0000000 --- a/drivers/main-hal.h +++ /dev/null @@ -1,51 +0,0 @@ -#include "common.h" -#include "config.h" -/* #include "upsconf.h" */ -#include "dstate.h" -#include "extstate.h" - -/* public functions & variables from main.c */ -extern const char *progname; -extern char *device_path; -extern const char *device_name; -extern int upsfd, extrafd, broken_driver, experimental_driver, exit_flag; -extern unsigned int poll_interval; - -/* functions & variables required in each driver */ -void upsdrv_initups(void); /* open connection to UPS, fail if not found */ -void upsdrv_initinfo(void); /* prep data, settings for UPS monitoring */ -void upsdrv_updateinfo(void); /* update state data if possible */ -void upsdrv_shutdown(void); /* make the UPS power off the load */ -void upsdrv_help(void); /* tack on anything useful for the -h text */ -void upsdrv_banner(void); /* print your version information */ -void upsdrv_cleanup(void); /* free any resources before shutdown */ - -/* --- details for the variable/value sharing --- */ - -/* main calls this driver function - it needs to call addvar */ -void upsdrv_makevartable(void); - -/* retrieve the value of variable if possible */ -char *getval(const char *var); - -/* see if has been defined, even if no value has been given to it */ -int testvar(const char *var); - -/* extended variable table - used for -x defines/flags */ -typedef struct vartab_s { - int vartype; /* VAR_* value, below */ - char *var; /* left side of =, or whole word if none */ - char *val; /* right side of = */ - char *desc; /* 40 character description for -h text */ - int found; /* set once encountered, for testvar() */ - struct vartab_s *next; -} vartab_t; - -/* flags to define types in the vartab */ - -#define VAR_FLAG 0x0001 /* argument is a flag (no value needed) */ -#define VAR_VALUE 0x0002 /* argument requires a value setting */ -#define VAR_SENSITIVE 0x0004 /* do not publish in driver.parameter */ - -/* callback from driver - create the table for future -x entries */ -void addvar(int vartype, const char *name, const char *desc); diff --git a/drivers/main.c b/drivers/main.c index 66caa36..63d329b 100644 --- a/drivers/main.c +++ b/drivers/main.c @@ -1,6 +1,10 @@ /* main.c - Network UPS Tools driver core - Copyright (C) 1999 Russell Kroll + Copyright (C) + 1999 Russell Kroll + 2005 - 2017 Arnaud Quette + 2017 Eaton (author: Emilien Kia ) + 2017 - 2022 Jim Klimov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,34 +21,71 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "common.h" #include "main.h" +#include "nut_stdint.h" #include "dstate.h" +#include "attribute.h" - /* data which may be useful to the drivers */ - int upsfd = -1; - char *device_path = NULL; - const char *progname = NULL, *upsname = NULL, *device_name = NULL; +#include +#include +#include +#include - /* may be set by the driver to wake up while in dstate_poll_fds */ - int extrafd = -1; +/* data which may be useful to the drivers */ +int upsfd = -1; +char *device_path = NULL; +const char *progname = NULL, *upsname = NULL, *device_name = NULL; - /* for ser_open */ - int do_lock_port = 1; +/* may be set by the driver to wake up while in dstate_poll_fds */ +int extrafd = -1; - /* for detecting -a values that don't match anything */ - static int upsname_found = 0; +/* for ser_open */ +int do_lock_port = 1; - static vartab_t *vartab_h = NULL; +/* for dstate->sock_connect, default to effectively + * asynchronous (0) with fallback to synchronous (1) */ +int do_synchronous = -1; - /* variables possibly set by the global part of ups.conf */ - unsigned int poll_interval = 2; - static char *chroot_path = NULL, *user = NULL; +/* for detecting -a values that don't match anything */ +static int upsname_found = 0; - /* signal handling */ - int exit_flag = 0; +static vartab_t *vartab_h = NULL; - /* everything else */ - static char *pidfn = NULL; +/* variables possibly set by the global part of ups.conf + * user and group may be set globally or per-driver + */ +time_t poll_interval = 2; +static char *chroot_path = NULL, *user = NULL, *group = NULL; +static int user_from_cmdline = 0, group_from_cmdline = 0; + +/* signal handling */ +int exit_flag = 0; + +/* should this driver instance go to background (default) + * or stay foregrounded (default if -D/-d options are set on + * command line)? + * Value is tri-state: + * -1 (default) Background the driver process + * 0 User required to not background explicitly, + * or passed -D (or -d) and current value was -1 + * 1 User required to background even if with -D or dump_mode + */ +static int background_flag = -1; + +/* Users can pass a -D[...] option to enable debugging. + * For the service tracing purposes, also the ups.conf + * can define a debug_min value in the global or device + * section, to set the minimal debug level (CLI provided + * value less than that would not have effect, can only + * have more). + */ +static int nut_debug_level_global = -1; +static int nut_debug_level_driver = -1; + +/* everything else */ +static char *pidfn = NULL; +static int dump_data = 0; /* Store the update_count requested */ /* print the driver banner */ void upsdrv_banner (void) @@ -70,6 +111,9 @@ void upsdrv_banner (void) } /* power down the attached load immediately */ +static void forceshutdown(void) + __attribute__((noreturn)); + static void forceshutdown(void) { upslogx(LOG_NOTICE, "Initiating UPS shutdown"); @@ -84,20 +128,32 @@ static void help_msg(void) { vartab_t *tmp; - printf("\nusage: %s -a [OPTIONS]\n", progname); + printf("\nusage: %s (-a |-s ) [OPTIONS]\n", progname); printf(" -a - autoconfig using ups.conf section \n"); printf(" - note: -x after -a overrides ups.conf settings\n\n"); + printf(" -s - configure directly from cmd line arguments\n"); + printf(" - note: must specify all driver parameters with successive -x\n"); + printf(" - note: at least 'port' variable should be set\n"); + printf(" - note: to explore the current values on a device from an\n"); + printf(" unprivileged user account (with sufficient media access in\n"); + printf(" the OS - e.g. to query networked devices), you can specify\n"); + printf(" '-d 1' argument and `export NUT_STATEPATH=/tmp` beforehand\n\n"); + printf(" -V - print version, then exit\n"); printf(" -L - print parseable list of driver variables\n"); - printf(" -D - raise debugging level\n"); + printf(" -D - raise debugging level (and stay foreground by default)\n"); + printf(" -d - dump data to stdout after 'count' updates loop and exit\n"); + printf(" -F - stay foregrounded even if no debugging is enabled\n"); + printf(" -B - stay backgrounded even if debugging is bumped\n"); printf(" -q - raise log level threshold\n"); printf(" -h - display this help\n"); printf(" -k - force shutdown\n"); printf(" -i - poll interval\n"); printf(" -r - chroot to \n"); printf(" -u - switch to (if started as root)\n"); + printf(" -g - set pipe access to (if started as root)\n"); printf(" -x = - set driver variable to \n"); printf(" - example: -x cable=940-0095B\n\n"); @@ -250,7 +306,12 @@ void addvar(int vartype, const char *name, const char *desc) /* handle -x / ups.conf config details that are for this part of the code */ static int main_arg(char *var, char *val) { - /* flags for main: just 'nolock' for now */ + /* flags for main */ + + upsdebugx(3, "%s: var='%s' val='%s'", + __func__, + var ? var : "", /* null should not happen... but... */ + val ? val : ""); if (!strcmp(var, "nolock")) { do_lock_port = 0; @@ -265,7 +326,7 @@ static int main_arg(char *var, char *val) /* any other flags are for the driver code */ if (!val) - return 0; + return 0; /* unhandled, pass it through to the driver */ /* variables for main: port */ @@ -276,11 +337,57 @@ static int main_arg(char *var, char *val) return 1; /* handled */ } + /* user specified at the driver level overrides that on global level + * or the built-in default + */ + if (!strcmp(var, "user")) { + if (user_from_cmdline) { + upsdebugx(0, "User '%s' specified in driver section " + "was ignored due to '%s' specified on command line", + val, user); + } else { + upsdebugx(1, "Overriding previously specified user '%s' " + "with '%s' specified for driver section", + user, val); + free(user); + user = xstrdup(val); + } + return 1; /* handled */ + } + + if (!strcmp(var, "group")) { + if (group_from_cmdline) { + upsdebugx(0, "Group '%s' specified in driver section " + "was ignored due to '%s' specified on command line", + val, group); + } else { + upsdebugx(1, "Overriding previously specified group '%s' " + "with '%s' specified for driver section", + group, val); + free(group); + group = xstrdup(val); + } + return 1; /* handled */ + } + if (!strcmp(var, "sddelay")) { upslogx(LOG_INFO, "Obsolete value sddelay found in ups.conf"); return 1; /* handled */ } + /* allow per-driver overrides of the global setting */ + if (!strcmp(var, "synchronous")) { + if (!strcmp(val, "yes")) + do_synchronous=1; + else + if (!strcmp(val, "auto")) + do_synchronous=-1; + else + do_synchronous=0; + + return 1; /* handled */ + } + /* only for upsdrvctl - ignored here */ if (!strcmp(var, "sdorder")) return 1; /* handled */ @@ -289,13 +396,36 @@ static int main_arg(char *var, char *val) if (!strcmp(var, "desc")) return 1; /* handled */ + /* Allow each driver to specify its minimal debugging level - + * admins can set more with command-line args, but can't set + * less without changing config. Should help debug of services. */ + if (!strcmp(var, "debug_min")) { + int lvl = -1; // typeof common/common.c: int nut_debug_level + if ( str_to_int (val, &lvl, 10) && lvl >= 0 ) { + nut_debug_level_driver = lvl; + } else { + upslogx(LOG_INFO, "WARNING : Invalid debug_min value found in ups.conf for the driver"); + } + return 1; /* handled */ + } + return 0; /* unhandled, pass it through to the driver */ } static void do_global_args(const char *var, const char *val) { + upsdebugx(3, "%s: var='%s' val='%s'", + __func__, + var ? var : "", /* null should not happen... but... */ + val ? val : ""); + if (!strcmp(var, "pollinterval")) { - poll_interval = atoi(val); + int ipv = atoi(val); + if (ipv > 0) { + poll_interval = (time_t)ipv; + } else { + fatalx(EXIT_FAILURE, "Error: invalid pollinterval: %d", ipv); + } return; } @@ -305,10 +435,54 @@ static void do_global_args(const char *var, const char *val) } if (!strcmp(var, "user")) { - free(user); - user = xstrdup(val); + if (user_from_cmdline) { + upsdebugx(0, "User specified in global section '%s' " + "was ignored due to '%s' specified on command line", + val, user); + } else { + upsdebugx(1, "Overriding previously specified user '%s' " + "with '%s' specified in global section", + user, val); + free(user); + user = xstrdup(val); + } } + if (!strcmp(var, "group")) { + if (group_from_cmdline) { + upsdebugx(0, "Group specified in global section '%s' " + "was ignored due to '%s' specified on command line", + val, group); + } else { + upsdebugx(1, "Overriding previously specified group '%s' " + "with '%s' specified in global section", + group, val); + free(group); + group = xstrdup(val); + } + } + + if (!strcmp(var, "synchronous")) { + if (!strcmp(val, "yes")) + do_synchronous=1; + else + if (!strcmp(val, "auto")) + do_synchronous=-1; + else + do_synchronous=0; + } + + /* Allow to specify its minimal debugging level for all drivers - + * admins can set more with command-line args, but can't set + * less without changing config. Should help debug of services. */ + if (!strcmp(var, "debug_min")) { + int lvl = -1; // typeof common/common.c: int nut_debug_level + if ( str_to_int (val, &lvl, 10) && lvl >= 0 ) { + nut_debug_level_global = lvl; + } else { + upslogx(LOG_INFO, "WARNING : Invalid debug_min value found in ups.conf global settings"); + } + } /* unrecognized */ } @@ -345,15 +519,38 @@ void do_upsconf_args(char *confupsname, char *var, char *val) /* don't let the user shoot themselves in the foot */ if (!strcmp(var, "driver")) { - if (strcmp(val, progname) != 0) + /* Accomodate for libtool wrapped developer iterations + * running e.g. `drivers/.libs/lt-dummy-ups` filenames + */ + size_t tmplen = strlen("lt-"); + if (strncmp("lt-", progname, tmplen) == 0 + && strcmp(val, progname + tmplen) == 0) { + /* debug level may be not initialized yet, and situation + * should not happen in end-user builds, so ok to yell: */ + upsdebugx(0, "Seems this driver binary %s is a libtool " + "wrapped build for driver %s", progname, val); + /* progname points to xbasename(argv[0]) in-place; + * roll the pointer forward a bit, we know we can: + */ + progname = progname + tmplen; + } + + if (strcmp(val, progname) != 0) { fatalx(EXIT_FAILURE, "Error: UPS [%s] is for driver %s, but I'm %s!\n", confupsname, val, progname); + } return; } /* allow per-driver overrides of the global setting */ if (!strcmp(var, "pollinterval")) { - poll_interval = atoi(val); + int ipv = atoi(val); + if (ipv > 0) { + poll_interval = (time_t)ipv; + } else { + fatalx(EXIT_FAILURE, "Error: UPS [%s]: invalid pollinterval: %d", + confupsname, ipv); + } return; } @@ -433,6 +630,7 @@ static void exit_cleanup(void) free(chroot_path); free(device_path); free(user); + free(group); if (pidfn) { unlink(pidfn); @@ -460,7 +658,14 @@ static void setup_signals(void) sigaction(SIGINT, &sa, NULL); sigaction(SIGQUIT, &sa, NULL); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif sa.sa_handler = SIG_IGN; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic pop +#endif sigaction(SIGHUP, &sa, NULL); sigaction(SIGPIPE, &sa, NULL); } @@ -469,12 +674,16 @@ int main(int argc, char **argv) { struct passwd *new_uid = NULL; int i, do_forceshutdown = 0; + int update_count = 0; atexit(exit_cleanup); /* pick up a default from configure --with-user */ user = xstrdup(RUN_AS_USER); /* xstrdup: this gets freed at exit */ + /* pick up a default from configure --with-group */ + group = xstrdup(RUN_AS_GROUP); /* xstrdup: this gets freed at exit */ + progname = xbasename(argv[0]); open_syslog(progname); @@ -488,7 +697,7 @@ int main(int argc, char **argv) /* build the driver's extra (-x) variable table */ upsdrv_makevartable(); - while ((i = getopt(argc, argv, "+a:kDhx:Lqr:u:Vi:")) != -1) { + while ((i = getopt(argc, argv, "+a:s:kFBDd:hx:Lqr:u:g:Vi:")) != -1) { switch (i) { case 'a': upsname = optarg; @@ -499,11 +708,31 @@ int main(int argc, char **argv) fatalx(EXIT_FAILURE, "Error: Section %s not found in ups.conf", optarg); break; + case 's': + upsname = optarg; + upsname_found = 1; + break; + case 'F': + background_flag = 0; + break; + case 'B': + background_flag = 1; + break; case 'D': nut_debug_level++; break; - case 'i': - poll_interval = atoi(optarg); + case 'd': + dump_data = atoi(optarg); + break; + case 'i': { /* scope */ + int ipv = atoi(optarg); + if (ipv > 0) { + poll_interval = (time_t)ipv; + } else { + fatalx(EXIT_FAILURE, "Error: command-line: invalid pollinterval: %d", + ipv); + } + } break; case 'k': do_lock_port = 0; @@ -519,7 +748,36 @@ int main(int argc, char **argv) chroot_path = xstrdup(optarg); break; case 'u': + if (user_from_cmdline) { + upsdebugx(1, "Previously specified user for drivers '%s' " + "was ignored due to '%s' specified on command line" + " (again?)", + user, optarg); + } else { + upsdebugx(1, "Built-in default or configured user " + "for drivers '%s' was ignored due to '%s' " + "specified on command line", + user, optarg); + } + free(user); user = xstrdup(optarg); + user_from_cmdline = 1; + break; + case 'g': + if (group_from_cmdline) { + upsdebugx(1, "Previously specified group for drivers '%s' " + "was ignored due to '%s' specified on command line" + " (again?)", + group, optarg); + } else { + upsdebugx(1, "Built-in default or configured group " + "for drivers '%s' was ignored due to '%s' " + "specified on command line", + group, optarg); + } + free(group); + group = xstrdup(optarg); + group_from_cmdline = 1; break; case 'V': /* already printed the banner, so exit */ @@ -536,6 +794,21 @@ int main(int argc, char **argv) } } + if (nut_debug_level > 0 || dump_data) { + if ( background_flag < 0 ) { + /* Only flop from default - stay foreground with debug on */ + background_flag = 0; + } else { + upsdebugx (0, + "Debug level is %d, dump data count is %s, " + "but backgrounding mode requested as %s", + nut_debug_level, + dump_data ? "on" : "off", + background_flag ? "on" : "off" + ); + } + } /* else: default remains `background_flag==-1` where nonzero is true */ + argc -= optind; argv += optind; @@ -546,15 +819,32 @@ int main(int argc, char **argv) if (!upsname_found) { fatalx(EXIT_FAILURE, - "Error: specifying '-a id' is now mandatory. Try -h for help."); + "Error: specifying '-a id' or '-s id' is now mandatory. Try -h for help."); } /* we need to get the port from somewhere */ if (!device_path) { fatalx(EXIT_FAILURE, - "Error: you must specify a port name in ups.conf. Try -h for help."); + "Error: you must specify a port name in ups.conf or in '-x port=...' argument.\n" + "Try -h for help."); } + /* CLI debug level can not be smaller than debug_min specified + * in ups.conf, and value specified for a driver config section + * overrides the global one. Note that non-zero debug_min does + * not impact foreground running mode. + */ + { + int nut_debug_level_upsconf = -1 ; + if ( nut_debug_level_global >= 0 && nut_debug_level_driver >= 0 ) { + nut_debug_level_upsconf = nut_debug_level_driver; + } else { + if ( nut_debug_level_global >= 0 ) nut_debug_level_upsconf = nut_debug_level_global; + if ( nut_debug_level_driver >= 0 ) nut_debug_level_upsconf = nut_debug_level_driver; + } + if ( nut_debug_level_upsconf > nut_debug_level ) + nut_debug_level = nut_debug_level_upsconf; + } upsdebugx(1, "debug level is '%d'", nut_debug_level); new_uid = get_user_pwent(user); @@ -564,13 +854,14 @@ int main(int argc, char **argv) become_user(new_uid); - /* Only switch to statepath if we're not powering off */ - /* This avoid case where ie /var is umounted */ - if ((!do_forceshutdown) && (chdir(dflt_statepath()))) + /* Only switch to statepath if we're not powering off + * or not just dumping data (for discovery) */ + /* This avoids case where ie /var is unmounted already */ + if ((!do_forceshutdown) && (!dump_data) && (chdir(dflt_statepath()))) fatal_with_errno(EXIT_FAILURE, "Can't chdir to %s", dflt_statepath()); /* Setup signals to communicate with driver once backgrounded. */ - if ((nut_debug_level == 0) && (!do_forceshutdown)) { + if ((background_flag != 0) && (!do_forceshutdown)) { char buffer[SMALLBUF]; setup_signals(); @@ -593,19 +884,26 @@ int main(int argc, char **argv) break; } - upslogx(LOG_WARNING, "Duplicate driver instance detected! Terminating other driver!"); + upslogx(LOG_WARNING, "Duplicate driver instance detected (PID file %s exists)! Terminating other driver!", buffer); /* Allow driver some time to quit */ sleep(5); } - pidfn = xstrdup(buffer); - writepid(pidfn); /* before backgrounding */ + /* Only write pid if we're not just dumping data, for discovery */ + if (!dump_data) { + pidfn = xstrdup(buffer); + writepid(pidfn); /* before backgrounding */ + } } /* clear out callback handler data */ memset(&upsh, '\0', sizeof(upsh)); + /* note: device.type is set early to be overridden by the driver + * when its a pdu! */ + dstate_setinfo("device.type", "ups"); + upsdrv_initups(); /* UPS is detected now, cleanup upon exit */ @@ -619,15 +917,19 @@ int main(int argc, char **argv) if (do_forceshutdown) forceshutdown(); - /* note: device.type is set early to be overriden by the driver - * when its a pdu! */ - dstate_setinfo("device.type", "ups"); - - /* publish the top-level data: version number, driver name */ + /* publish the top-level data: version numbers, driver name */ dstate_setinfo("driver.version", "%s", UPS_VERSION); dstate_setinfo("driver.version.internal", "%s", upsdrv_info.version); dstate_setinfo("driver.name", "%s", progname); + /* + * If we are not debugging, send the early startup logs generated by + * upsdrv_initinfo() and upsdrv_updateinfo() to syslog, not just stderr. + * Otherwise these logs are lost. + */ + if ((nut_debug_level == 0) && (!dump_data)) + syslogbit_set(); + /* get the base data established before allowing connections */ upsdrv_initinfo(); upsdrv_updateinfo(); @@ -655,10 +957,87 @@ int main(int argc, char **argv) } /* now we can start servicing requests */ - dstate_init(progname, upsname); + /* Only write pid if we're not just dumping data, for discovery */ + if (!dump_data) { + char * sockname = dstate_init(progname, upsname); + /* Normally we stick to the built-in account info, + * so if they were not over-ridden - no-op here: + */ + if (strcmp(group, RUN_AS_GROUP) + || strcmp(user, RUN_AS_USER) + ) { + int allOk = 1; + /* Tune group access permission to the pipe, + * so that upsd can access it (using the + * specified or retained default group): + */ + struct group *grp = getgrnam(group); + upsdebugx(1, "Group and/or user account for this driver " + "was customized ('%s:%s') compared to built-in " + "defaults. Fixing socket '%s' ownership/access.", + user, group, sockname); + + if (grp == NULL) { + upsdebugx(1, "WARNING: could not resolve " + "group name '%s': %s", + group, strerror(errno) + ); + allOk = 0; + } else { + struct stat statbuf; + mode_t mode; + if (chown(sockname, -1, grp->gr_gid)) { + upsdebugx(1, "WARNING: chown failed: %s", + strerror(errno) + ); + allOk = 0; + } + + if (stat(sockname, &statbuf)) { + /* Logically we'd fail chown above if file + * does not exist or is not accessible, but + * practically we only need stat for chmod + */ + upsdebugx(1, "WARNING: stat failed: %s", + strerror(errno) + ); + allOk = 0; + } else { + /* chmod g+rw sockname */ + mode = statbuf.st_mode; + mode |= S_IWGRP; + mode |= S_IRGRP; + if (chmod(sockname, mode)) { + upsdebugx(1, "WARNING: chmod failed: %s", + strerror(errno) + ); + allOk = 0; + } + } + } + + if (allOk) { + upsdebugx(1, "Group access for this driver successfully fixed"); + } else { + upsdebugx(0, "WARNING: Needed to fix group access " + "to filesystem socket of this driver, but failed; " + "run the driver with more debugging to see how exactly.\n" + "Consumers of the socket, such as upsd data server, " + "can fail to interact with the driver and represent " + "the device: %s", + sockname); + } + + } + free(sockname); + } /* The poll_interval may have been changed from the default */ - dstate_setinfo("driver.parameter.pollinterval", "%d", poll_interval); + dstate_setinfo("driver.parameter.pollinterval", "%jd", (intmax_t)poll_interval); + + /* The synchronous option may have been changed from the default */ + dstate_setinfo("driver.parameter.synchronous", "%s", + (do_synchronous==1)?"yes":((do_synchronous==0)?"no":"auto")); /* remap the device.* info from ups.* for the transition period */ if (dstate_getinfo("ups.mfr") != NULL) @@ -668,7 +1047,7 @@ int main(int argc, char **argv) if (dstate_getinfo("ups.serial") != NULL) dstate_setinfo("device.serial", "%s", dstate_getinfo("ups.serial")); - if (nut_debug_level == 0) { + if (background_flag != 0) { background(); writepid(pidfn); /* PID changes when backgrounding */ } @@ -682,13 +1061,27 @@ int main(int argc, char **argv) upsdrv_updateinfo(); - while (!dstate_poll_fds(timeout, extrafd) && !exit_flag) { - /* repeat until time is up or extrafd has data */ + /* Dump the data tree (in upsc-like format) to stdout and exit */ + if (dump_data) { + /* Wait for 'dump_data' update loops to ensure data completion */ + if (update_count == dump_data) { + dstate_dump(); + exit_flag = 1; + } + else + update_count++; + } + else { + while (!dstate_poll_fds(timeout, extrafd) && !exit_flag) { + /* repeat until time is up or extrafd has data */ + } } } /* if we get here, the exit flag was set by a signal handler */ - upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); + /* however, avoid to "pollute" data dump output! */ + if (!dump_data) + upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); exit(EXIT_SUCCESS); } diff --git a/drivers/main.h b/drivers/main.h index 3e3e84e..523d908 100644 --- a/drivers/main.h +++ b/drivers/main.h @@ -1,5 +1,5 @@ -#ifndef MAIN_H -#define MAIN_H +#ifndef NUT_MAIN_H_SEEN +#define NUT_MAIN_H_SEEN #include "common.h" #include "upsconf.h" @@ -10,7 +10,7 @@ extern const char *progname, *upsname, *device_name; extern char *device_path; extern int upsfd, extrafd, broken_driver, experimental_driver, do_lock_port, exit_flag; -extern unsigned int poll_interval; +extern time_t poll_interval; /* functions & variables required in each driver */ void upsdrv_initups(void); /* open connection to UPS, fail if not found */ @@ -53,7 +53,7 @@ void addvar(int vartype, const char *name, const char *desc); /* subdriver description structure */ typedef struct upsdrv_info_s { - const char *name; /* driver full name, for banner printing, ... */ + const char *name; /* driver full name, for banner printing, ... */ const char *version; /* driver version */ const char *authors; /* authors name */ const int status; /* driver development status */ @@ -76,4 +76,4 @@ typedef struct upsdrv_info_s { /* public driver information from the driver file */ extern upsdrv_info_t upsdrv_info; -#endif /* MAIN_H */ +#endif /* NUT_MAIN_H_SEEN */ diff --git a/drivers/masterguard.c b/drivers/masterguard.c index f480ad3..46364f6 100644 --- a/drivers/masterguard.c +++ b/drivers/masterguard.c @@ -4,6 +4,11 @@ masterguard.c created on 15.8.2001 + OBSOLETION WARNING: Please to not base new development on this + codebase, instead create a new subdriver for nutdrv_qx which + generally covers all Megatec/Qx protocol family and aggregates + device support from such legacy drivers over time. + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or @@ -19,22 +24,11 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ - -/******************************************************************** - * - * Please if you edit this code convert tabs to spaces and use - * four characters indent. - * If you don't know what this means use the vim editor. - * - * Have fun - * Michael - * - ********************************************************************/ #include "main.h" #include "serial.h" #define DRIVER_NAME "MASTERGUARD UPS driver" -#define DRIVER_VERSION "0.24" +#define DRIVER_VERSION "0.25" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -54,76 +48,76 @@ upsdrv_info_t upsdrv_info = { #define DEBUG 1 -int type; -char name[31]; -char firmware[6]; +static int type; +static char name[31]; +static char firmware[6]; /******************************************************************** * - * Helper function to split a sting into words by splitting at the + * Helper function to split a sting into words by splitting at the * SPACE character. * - * Adds up to maxlen characters to the char word. + * Adds up to maxlen characters to the char word. * Returns NULL on reaching the end of the string. - * + * ********************************************************************/ -static char *StringSplit( char *source, char *word, int maxlen ) +static char *StringSplit( char *source, char *word, size_t maxlen ) { - int i; - int len; - int wc=0; - - word[0] = '\0'; - len = strlen( source ); - for( i = 0; i < len; i++ ) - { - if( source[i] == ' ' ) - { - word[wc] = '\0'; - return source + i + 1; - } - word[wc] = source[i]; - wc++; - } - word[wc] = '\0'; - return NULL; + size_t i; + size_t len; + size_t wc=0; + + word[0] = '\0'; + len = strlen( source ); + for( i = 0; i < len && wc < maxlen; i++ ) + { + if( source[i] == ' ' ) + { + word[wc] = '\0'; + return source + i + 1; + } + word[wc] = source[i]; + wc++; + } + word[wc] = '\0'; + return NULL; } - + /******************************************************************** * * Helper function to drop all whitespaces within a string. - * + * * "word" must be large enought to hold "source", for the worst case * "word" has to be exacly the size of "source". - * + * ********************************************************************/ static void StringStrip( char *source, char *word ) { - int wc=0; - int i; - int len; + size_t wc=0; + size_t i; + size_t len; - word[0] = '\0'; - len = strlen( source ); - for( i = 0; i < len; i++ ) - { - if( source[i] == ' ' ) - continue; - if( source[i] == '\n' ) - continue; - if( source[i] == '\t' ) - continue; - word[wc] = source[i]; - wc++; - } - word[wc] = '\0'; + word[0] = '\0'; + len = strlen( source ); + for( i = 0; i < len; i++ ) + { + if( source[i] == ' ' ) + continue; + if( source[i] == '\n' ) + continue; + if( source[i] == '\t' ) + continue; + word[wc] = source[i]; + wc++; + } + word[wc] = '\0'; } /******************************************************************** * * Function parses the status flags which occure in the Q1 and Q3 * command. Sets the INFO_STATUS value ( OL, OB, ... ) - * + * ********************************************************************/ static void parseFlags( char *flags ) { @@ -143,7 +137,7 @@ static void parseFlags( char *flags ) /* this has no mapping */ #if 0 if( flags[3] == '1' ) - setinfo( INFO_ALRM_GENERAL, "1" ); + setinfo( INFO_ALRM_GENERAL, "1" ); #endif #if 0 @@ -157,195 +151,195 @@ static void parseFlags( char *flags ) status_commit(); - if( DEBUG ) - printf( "Status is %s\n", dstate_getinfo("ups.status")); + if( DEBUG ) + printf( "Status is %s\n", dstate_getinfo("ups.status")); } /******************************************************************** * * Function parses the response of the query1 ( "Q1" ) command. * Also sets various values (IPFreq ... ) - * + * ********************************************************************/ static void query1( char *buf ) { - #define WORDMAXLEN 255 - char value[WORDMAXLEN]; - char word[WORDMAXLEN]; - char *newPOS; - char *oldPOS; - int count = 0; + #define WORDMAXLEN 255 + char value[WORDMAXLEN]; + char word[WORDMAXLEN]; + char *newPOS; + char *oldPOS; + int count = 0; - if( DEBUG ) - printf( "Q1 Buffer is : %s\n" , buf + 1 ); - oldPOS = buf + 1; - newPOS = oldPOS; - - do - { - newPOS = StringSplit( oldPOS, word, WORDMAXLEN ); - StringStrip( word, value); - oldPOS = newPOS; + if( DEBUG ) + printf( "Q1 Buffer is : %s\n" , buf + 1 ); + oldPOS = buf + 1; + newPOS = oldPOS; - if( DEBUG ) - { - printf( "value=%s\n", value ); - fflush( stdout ); - } - switch( count ) - { - case 0: - /* IP Voltage */ - dstate_setinfo("input.voltage", "%s", value ); - break; - case 1: - /* IP Fault Voltage */ - break; - case 2: - /* OP Voltage */ - dstate_setinfo("output.voltage", "%s", value); - break; - case 3: - /* OP Load*/ - dstate_setinfo("ups.load", "%s", value ); - break; - case 4: - /* IP Frequency */ - dstate_setinfo("input.frequency", "%s", value); - break; - case 5: - /* Battery Cell Voltage */ - dstate_setinfo("battery.voltage", "%s", value); - break; - case 6: - /* UPS Temperature */ - dstate_setinfo("ups.temperature", "%s", value ); - break; - case 7: - /* Flags */ - parseFlags( value ); - break; - default: - /* Should never be reached */ - break; - } - count ++; - oldPOS = newPOS; - } - while( newPOS != NULL ); + do + { + newPOS = StringSplit( oldPOS, word, WORDMAXLEN ); + StringStrip( word, value); + oldPOS = newPOS; + + if( DEBUG ) + { + printf( "value=%s\n", value ); + fflush( stdout ); + } + switch( count ) + { + case 0: + /* IP Voltage */ + dstate_setinfo("input.voltage", "%s", value ); + break; + case 1: + /* IP Fault Voltage */ + break; + case 2: + /* OP Voltage */ + dstate_setinfo("output.voltage", "%s", value); + break; + case 3: + /* OP Load*/ + dstate_setinfo("ups.load", "%s", value ); + break; + case 4: + /* IP Frequency */ + dstate_setinfo("input.frequency", "%s", value); + break; + case 5: + /* Battery Cell Voltage */ + dstate_setinfo("battery.voltage", "%s", value); + break; + case 6: + /* UPS Temperature */ + dstate_setinfo("ups.temperature", "%s", value ); + break; + case 7: + /* Flags */ + parseFlags( value ); + break; + default: + /* Should never be reached */ + break; + } + count ++; + oldPOS = newPOS; + } + while( newPOS != NULL ); } /******************************************************************** * * Function parses the response of the query3 ( "Q3" ) command. * Also sets various values (IPFreq ... ) - * + * ********************************************************************/ static void query3( char *buf ) { - #define WORDMAXLEN 255 - char value[WORDMAXLEN]; - char word[WORDMAXLEN]; - char *newPOS; - char *oldPOS; - int count = 0; + #define WORDMAXLEN 255 + char value[WORDMAXLEN]; + char word[WORDMAXLEN]; + char *newPOS; + char *oldPOS; + int count = 0; - if( DEBUG ) - printf( "Q3 Buffer is : %s\n" , buf+1 ); - oldPOS = buf + 1; - newPOS = oldPOS; - - do - { - newPOS = StringSplit( oldPOS, word, WORDMAXLEN ); - StringStrip( word, value); - oldPOS = newPOS; + if( DEBUG ) + printf( "Q3 Buffer is : %s\n" , buf+1 ); + oldPOS = buf + 1; + newPOS = oldPOS; - /* Shortcut */ - if( newPOS == NULL ) - break; + do + { + newPOS = StringSplit( oldPOS, word, WORDMAXLEN ); + StringStrip( word, value); + oldPOS = newPOS; - if( DEBUG ) - { - printf( "value=%s\n", value ); - fflush( stdout ); - } - switch( count ) - { - case 0: - /* UPS ID */ - break; - case 1: - /* Input Voltage */ - dstate_setinfo("input.voltage", "%s", value ); - break; - case 2: - /* Input Fault Voltage */ - break; - case 3: - /* Output Voltage */ - dstate_setinfo("output.voltage", "%s", value); - break; - case 4: - /* Output Current */ - dstate_setinfo("output.current", "%s", value ); - break; - case 5: - /* Input Frequency */ - dstate_setinfo("input.frequency", "%s", value); - break; - case 6: - /* Battery Cell Voltage */ - dstate_setinfo("battery.voltage", "%s", value); - break; - case 7: - /* Temperature */ - dstate_setinfo("ups.temperature", "%s", value ); - break; - case 8: - /* Estimated Runtime */ - dstate_setinfo("battery.runtime", "%s", value); - break; - case 9: - /* Charge Status */ - dstate_setinfo("battery.charge", "%s", value); - break; - case 10: - /* Flags */ - parseFlags( value ); - break; - case 11: - /* Flags2 */ - break; - default: - /* This should never be reached */ - /* printf( "DEFAULT\n" ); */ - break; - } - count ++; - oldPOS = newPOS; - } - while( newPOS != NULL ); + /* Shortcut */ + if( newPOS == NULL ) + break; + + if( DEBUG ) + { + printf( "value=%s\n", value ); + fflush( stdout ); + } + switch( count ) + { + case 0: + /* UPS ID */ + break; + case 1: + /* Input Voltage */ + dstate_setinfo("input.voltage", "%s", value ); + break; + case 2: + /* Input Fault Voltage */ + break; + case 3: + /* Output Voltage */ + dstate_setinfo("output.voltage", "%s", value); + break; + case 4: + /* Output Current */ + dstate_setinfo("output.current", "%s", value ); + break; + case 5: + /* Input Frequency */ + dstate_setinfo("input.frequency", "%s", value); + break; + case 6: + /* Battery Cell Voltage */ + dstate_setinfo("battery.voltage", "%s", value); + break; + case 7: + /* Temperature */ + dstate_setinfo("ups.temperature", "%s", value ); + break; + case 8: + /* Estimated Runtime */ + dstate_setinfo("battery.runtime", "%s", value); + break; + case 9: + /* Charge Status */ + dstate_setinfo("battery.charge", "%s", value); + break; + case 10: + /* Flags */ + parseFlags( value ); + break; + case 11: + /* Flags2 */ + break; + default: + /* This should never be reached */ + /* printf( "DEFAULT\n" ); */ + break; + } + count ++; + oldPOS = newPOS; + } + while( newPOS != NULL ); } /******************************************************************** * * Function to parse the WhoAmI response of the UPS. Also sets the * values of the firmware version and the UPS identification. - * + * ********************************************************************/ static void parseWH( char *buf ) { - strncpy( name, buf + 16, 30 ); - name[30] = '\0'; - strncpy( firmware, buf + 4, 5 ); - firmware[5] = '\0'; - if( DEBUG ) - printf( "Name = %s, Firmware Version = %s\n", name, firmware ); + strncpy( name, buf + 16, 30 ); + name[30] = '\0'; + strncpy( firmware, buf + 4, 5 ); + firmware[5] = '\0'; + if( DEBUG ) + printf( "Name = %s, Firmware Version = %s\n", name, firmware ); } /******************************************************************** - * + * * Function to parse the old and possible broken WhoAmI response * and set the values for the firmware Version and the identification * of the UPS. @@ -353,97 +347,97 @@ static void parseWH( char *buf ) ********************************************************************/ static void parseOldWH( char *buf ) { - strncpy( name, buf + 4, 12 ); - name[12] = '\0'; - strncpy( firmware, buf, 4 ); - firmware[4] = '\0'; - if( DEBUG ) - printf( "Name = %s, Firmware Version = %s\n", name, firmware ); + strncpy( name, buf + 4, 12 ); + name[12] = '\0'; + strncpy( firmware, buf, 4 ); + firmware[4] = '\0'; + if( DEBUG ) + printf( "Name = %s, Firmware Version = %s\n", name, firmware ); } /******************************************************************** * * Function to fake a WhoAmI response of a UPS that returns NAK. - * + * ********************************************************************/ static void fakeWH(void) { - strcpy( name, "GenericUPS" ); - strcpy( firmware, "unkn" ); - if( DEBUG ) - printf( "Name = %s, Firmware Version = %s\n", name, firmware ); + strcpy( name, "GenericUPS" ); + strcpy( firmware, "unkn" ); + if( DEBUG ) + printf( "Name = %s, Firmware Version = %s\n", name, firmware ); } -static int ups_ident( void ) +static ssize_t ups_ident( void ) { - char buf[255]; - int ret; + char buf[255]; + ssize_t ret; - /* Check presence of Q1 */ - ret = ser_send_pace(upsfd, UPS_PACE, "%s", "Q1\x0D" ); - ret = ser_get_line(upsfd, buf, sizeof(buf), '\r', "", 3, 0); - ret = strlen( buf ); - if( ret != 46 ) - { - /* No Q1 response found */ - type = 0; - return -1; - } - else - { - if( DEBUG ) - printf( "Found Q1\n" ); - type = Q1; - } + /* Check presence of Q1 */ + ret = ser_send_pace(upsfd, UPS_PACE, "%s", "Q1\x0D" ); + ret = ser_get_line(upsfd, buf, sizeof(buf), '\r', "", 3, 0); + ret = (ssize_t)strlen( buf ); + if( ret != 46 ) + { + /* No Q1 response found */ + type = 0; + return -1; + } + else + { + if( DEBUG ) + printf( "Found Q1\n" ); + type = Q1; + } - /* Check presence of Q3 */ - ret = ser_send_pace(upsfd, UPS_PACE, "%s", "Q3\x0D" ); - ret = ser_get_line(upsfd, buf, sizeof(buf), '\r', "", 3, 0); - ret = strlen( buf ); - if( ret == 70 ) - { - if( DEBUG ) - printf( "Found Q3\n" ); - type = Q1 | Q3; - } - - /* Check presence of WH ( Who am I ) */ - ret = ser_send_pace(upsfd, UPS_PACE, "%s", "WH\x0D" ); - ret = ser_get_line(upsfd, buf, sizeof(buf), '\r', "", 3, 0); - ret = strlen( buf ); - if( ret == 112 ) - { - if( DEBUG ) - printf( "WH found\n" ); - parseWH( buf ); - } - else if( ret == 53 ) - { - if( DEBUG ) - printf( "Old (broken) WH found\n" ); - parseOldWH( buf ); - } - else if( ret == 3 && strcmp(buf, "NAK") == 0 ) - { - if( DEBUG ) - printf( "WH was NAKed\n" ); - fakeWH( ); - } - else if( ret > 0 ) - { - if( DEBUG ) - printf( "WH says <%s> with length %i\n", buf, ret ); - upslog_with_errno( LOG_INFO, - "New WH String found. Please report to maintainer\n" ); - } - return 1; -} + /* Check presence of Q3 */ + ret = ser_send_pace(upsfd, UPS_PACE, "%s", "Q3\x0D" ); + ret = ser_get_line(upsfd, buf, sizeof(buf), '\r', "", 3, 0); + ret = (ssize_t)strlen( buf ); + if( ret == 70 ) + { + if( DEBUG ) + printf( "Found Q3\n" ); + type = Q1 | Q3; + } + + /* Check presence of WH ( Who am I ) */ + ret = ser_send_pace(upsfd, UPS_PACE, "%s", "WH\x0D" ); + ret = ser_get_line(upsfd, buf, sizeof(buf), '\r', "", 3, 0); + ret = (ssize_t)strlen( buf ); + if( ret == 112 ) + { + if( DEBUG ) + printf( "WH found\n" ); + parseWH( buf ); + } + else if( ret == 53 ) + { + if( DEBUG ) + printf( "Old (broken) WH found\n" ); + parseOldWH( buf ); + } + else if( ret == 3 && strcmp(buf, "NAK") == 0 ) + { + if( DEBUG ) + printf( "WH was NAKed\n" ); + fakeWH( ); + } + else if( ret > 0 ) + { + if( DEBUG ) + printf( "WH says <%s> with length %zi\n", buf, ret ); + upslog_with_errno( LOG_INFO, + "New WH String found. Please report to maintainer\n" ); + } + return 1; +} /******************************************************************** * - * - * - * + * + * + * ********************************************************************/ void upsdrv_help( void ) { @@ -453,94 +447,92 @@ void upsdrv_help( void ) /******************************************************************** * * Function to initialize the fields of the ups driver. - * + * ********************************************************************/ void upsdrv_initinfo(void) { dstate_setinfo("ups.mfr", "MASTERGUARD"); dstate_setinfo("ups.model", "unknown"); - + /* dstate_addcmd("test.battery.stop"); dstate_addcmd("test.battery.start"); */ if( strlen( name ) > 0 ) - dstate_setinfo("ups.model", "%s", name); + dstate_setinfo("ups.model", "%s", name); if( strlen( firmware ) > 0 ) - dstate_setinfo("ups.firmware", "%s", firmware); - - dstate_setinfo("driver.version.internal", "%s", DRIVER_VERSION); + dstate_setinfo("ups.firmware", "%s", firmware); } /******************************************************************** * - * This is the main function. It gets called if the driver wants - * to update the ups status and the informations. - * + * This is the main function. It gets called if the driver wants + * to update the ups status and the information. + * ********************************************************************/ void upsdrv_updateinfo(void) { - char buf[255]; - int ret; - int lenRSP=0; + char buf[255]; + ssize_t ret; + int lenRSP=0; - if( DEBUG ) - printf( "update info\n" ); + if( DEBUG ) + printf( "update info\n" ); - /* Q3 found ? */ - if( type & Q3 ) - { - ser_send_pace(upsfd, UPS_PACE, "%s", "Q3\x0D" ); - lenRSP = 70; - } - /* Q1 found ? */ - else if( type & Q1 ) - { - ser_send_pace(upsfd, UPS_PACE, "%s", "Q1\x0D" ); - lenRSP = 46; - } - /* Should never be reached */ - else - { - fatalx(EXIT_FAILURE, "Error, no Query mode defined. Please file bug against driver."); - } + /* Q3 found ? */ + if( type & Q3 ) + { + ser_send_pace(upsfd, UPS_PACE, "%s", "Q3\x0D" ); + lenRSP = 70; + } + /* Q1 found ? */ + else if( type & Q1 ) + { + ser_send_pace(upsfd, UPS_PACE, "%s", "Q1\x0D" ); + lenRSP = 46; + } + /* Should never be reached */ + else + { + fatalx(EXIT_FAILURE, "Error, no Query mode defined. Please file bug against driver."); + } - sleep( UPSDELAY ); + sleep( UPSDELAY ); - buf[0] = '\0'; - ret = ser_get_line(upsfd, buf, sizeof(buf), '\r', "", 3, 0); - ret = strlen( buf ); + buf[0] = '\0'; + ret = ser_get_line(upsfd, buf, sizeof(buf), '\r', "", 3, 0); + ret = (ssize_t)strlen( buf ); - if( ret != lenRSP ) - { - if( DEBUG ) - printf( "buf = %s len = %i\n", buf, ret ); - upslog_with_errno( LOG_ERR, "Error in UPS response " ); - dstate_datastale(); - return; - } + if( ret != lenRSP ) + { + if( DEBUG ) + printf( "buf = %s len = %zi\n", buf, ret ); + upslog_with_errno( LOG_ERR, "Error in UPS response " ); + dstate_datastale(); + return; + } - /* Parse the response from the UPS */ - if( type & Q3 ) - { - query3( buf ); - dstate_dataok(); - return; - } - if( type & Q1 ) - { - query1( buf ); - dstate_dataok(); - return; - } + /* Parse the response from the UPS */ + if( type & Q3 ) + { + query3( buf ); + dstate_dataok(); + return; + } + if( type & Q1 ) + { + query1( buf ); + dstate_dataok(); + return; + } } /******************************************************************** * * Called if the driver wants to shutdown the UPS. * ( also used by the "-k" command line switch ) - * + * * This cuts the utility from the UPS after 20 seconds and restores * the utility one minute _after_ the utility to the UPS has restored * @@ -548,80 +540,79 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { /* ups will come up within a minute if utility is restored */ - ser_send_pace(upsfd, UPS_PACE, "%s", "S.2R0001\x0D" ); + ser_send_pace(upsfd, UPS_PACE, "%s", "S.2R0001\x0D" ); } /******************************************************************** * * Populate the command line switches. - * + * * CS: Cancel the shutdown process - * + * ********************************************************************/ void upsdrv_makevartable(void) { - addvar( VAR_FLAG, "CS", "Cancel Shutdown" ); + addvar( VAR_FLAG, "CS", "Cancel Shutdown" ); } /******************************************************************** * * This is the first function called by the UPS driver. * Detects the UPS and handles the command line args. - * + * ********************************************************************/ void upsdrv_initups(void) { - int count = 0; - int fail = 0; - int good = 0; - - /* setup serial port */ - upsfd = ser_open(device_path); - ser_set_speed(upsfd, device_path, B2400); + int count = 0; + int fail = 0; + int good = 0; - name[0] = '\0'; - firmware[0] = '\0'; + upsdebugx(0, + "Please note that this driver is deprecated and will not receive\n" + "new development. If it works for managing your devices - fine,\n" + "but if you are running it to try setting up a new device, please\n" + "consider the newer nutdrv_qx instead, which should handle all 'Qx'\n" + "protocol variants for NUT. (Please also report if your device works\n" + "with this driver, but nutdrv_qx would not actually support it with\n" + "any subdriver!)\n"); + + /* setup serial port */ + upsfd = ser_open(device_path); + ser_set_speed(upsfd, device_path, B2400); + + name[0] = '\0'; + firmware[0] = '\0'; /* probe ups type */ - do - { - count++; + do + { + count++; - if( ups_ident( ) != 1 ) - fail++; - /* at least two good identifications */ - if( (count - fail) == 2 ) - { - good = 1; - break; - } - } while( (count=0 per check above */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +/* Note for gating macros above: unsuffixed HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP + * means support of contexts both inside and outside function body, so the push + * above and pop below (outside this finction) are not used. + */ +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +/* Note that the individual warning pragmas for use inside function bodies + * are named without a _INSIDEFUNC suffix, for simplicity and legacy reasons + */ +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if (res < 7 || (unsigned long long int)res >= SIZE_MAX) +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups"); + memcpy(serial, my_answer + 7, (size_t)(res - 7)); /* serial number start from the 8th byte */ - serial[12]=0; /* terminate string */ + serial[12]='\0'; /* terminate string */ dstate_setinfo("ups.serial", "%s", serial); - + /* get the ups firmware. The major number is in the 5th byte, the minor is in the 6th */ dstate_setinfo("ups.firmware", "%u.%u", my_answer[5], my_answer[6]); - printf("Detected %s [%s] v.%s on %s\n", dstate_getinfo("ups.model"), dstate_getinfo("ups.serial"), dstate_getinfo("ups.firmware"), device_path); - + printf("Detected %s [%s] v.%s on %s\n", + dstate_getinfo("ups.model"), dstate_getinfo("ups.serial"), + dstate_getinfo("ups.firmware"), device_path); + /* Add instant commands */ dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); @@ -565,17 +615,21 @@ void upsdrv_initinfo(void) upsh.instcmd = instcmd; return; } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) ) +# pragma GCC diagnostic pop +#endif void upsdrv_updateinfo(void) { - int res, int_num; + int res; + uint16_t int_num; #ifdef EXTRADATA int day, hour, minute; #endif float float_num; - long int long_num; + uint32_t long_num; unsigned char my_answer[255]; - + /* GET Output data */ res = command_read_sequence(UPS_OUTPUT_DATA, my_answer); if (res < 0) { @@ -589,36 +643,36 @@ void upsdrv_updateinfo(void) dstate_setinfo("ups.load", "%2.1f", float_num); } else { dstate_setinfo("ups.load", "%s", "not available"); - } + } #ifdef EXTRADATA - dstate_setinfo("output.power", "%d", int_num); + dstate_setinfo("output.power", "%u", int_num); #endif /* voltage */ int_num = get_word(&my_answer[3]); - if (int_num > 0) dstate_setinfo("output.voltage", "%d", int_num); - if (int_num == -1) dstate_setinfo("output.voltage", "%s", "overrange"); - if (int_num == -2) dstate_setinfo("output.voltage", "%s", "not available"); + if ((int16_t)int_num > 0) dstate_setinfo("output.voltage", "%u", int_num); + if ((int16_t)int_num == -1) dstate_setinfo("output.voltage", "%s", "overrange"); + if ((int16_t)int_num == -2) dstate_setinfo("output.voltage", "%s", "not available"); /* current */ - float_num = get_word(&my_answer[5]); - if (float_num == -1) dstate_setinfo("output.current", "%s", "overrange"); - if (float_num == -2) dstate_setinfo("output.current", "%s", "not available"); + float_num = get_word_float(&my_answer[5]); + if (f_equal(float_num, -1.0)) dstate_setinfo("output.current", "%s", "overrange"); + if (f_equal(float_num, -2.0)) dstate_setinfo("output.current", "%s", "not available"); if (float_num > 0) { float_num = (float)(float_num/10); dstate_setinfo("output.current", "%2.2f", float_num); } #ifdef EXTRADATA /* peak current */ - float_num = get_word(&my_answer[7]); - if (float_num == -1) dstate_setinfo("output.current.peak", "%s", "overrange"); - if (float_num == -2) dstate_setinfo("output.current.peak", "%s", "not available"); + float_num = get_word_float(&my_answer[7]); + if (f_equal(float_num, -1.0)) dstate_setinfo("output.current.peak", "%s", "overrange"); + if (f_equal(float_num, -2.0)) dstate_setinfo("output.current.peak", "%s", "not available"); if (float_num > 0) { float_num = (float)(float_num/10); dstate_setinfo("output.current.peak", "%2.2f", float_num); } - + #endif } - + /* GET Input data */ res = command_read_sequence(UPS_INPUT_DATA, my_answer); if (res < 0){ @@ -628,18 +682,18 @@ void upsdrv_updateinfo(void) #ifdef EXTRADATA /* Active power */ int_num = get_word(&my_answer[1]); - if (int_num > 0) dstate_setinfo("input.power", "%d", int_num); - if (int_num == -1) dstate_setinfo("input.power", "%s", "overrange"); - if (int_num == -2) dstate_setinfo("input.power", "%s", "not available"); + if ((int16_t)int_num > 0) dstate_setinfo("input.power", "%u", int_num); + if ((int16_t)int_num == -1) dstate_setinfo("input.power", "%s", "overrange"); + if ((int16_t)int_num == -2) dstate_setinfo("input.power", "%s", "not available"); #endif /* voltage */ int_num = get_word(&my_answer[3]); - if (int_num > 0) dstate_setinfo("input.voltage", "%d", int_num); - if (int_num == -1) dstate_setinfo("input.voltage", "%s", "overrange"); - if (int_num == -2) dstate_setinfo("input.voltage", "%s", "not available"); + if ((int16_t)int_num > 0) dstate_setinfo("input.voltage", "%u", int_num); + if ((int16_t)int_num == -1) dstate_setinfo("input.voltage", "%s", "overrange"); + if ((int16_t)int_num == -2) dstate_setinfo("input.voltage", "%s", "not available"); #ifdef EXTRADATA /* current */ - float_num = get_word(&my_answer[5]); + float_num = get_word_float(&my_answer[5]); if (float_num == -1) dstate_setinfo("input.current", "%s", "overrange"); if (float_num == -2) dstate_setinfo("input.current", "%s", "not available"); if (float_num > 0) { @@ -647,7 +701,7 @@ void upsdrv_updateinfo(void) dstate_setinfo("input.current", "%2.2f", float_num); } /* peak current */ - float_num = get_word(&my_answer[7]); + float_num = get_word_float(&my_answer[7]); if (float_num == -1) dstate_setinfo("input.current.peak", "%s", "overrange"); if (float_num == -2) dstate_setinfo("input.current.peak", "%s", "not available"); if (float_num > 0) { @@ -656,8 +710,8 @@ void upsdrv_updateinfo(void) } #endif } - - + + /* GET Battery data */ res = command_read_sequence(UPS_BATTERY_DATA, my_answer); if (res < 0) { @@ -665,21 +719,21 @@ void upsdrv_updateinfo(void) dstate_datastale(); } else { /* Actual value */ - float_num = get_word(&my_answer[1]); + float_num = get_word_float(&my_answer[1]); float_num = (float)(float_num/10); dstate_setinfo("battery.voltage", "%2.2f", float_num); #ifdef EXTRADATA /* reserve threshold */ - float_num = get_word(&my_answer[3]); + float_num = get_word_float(&my_answer[3]); float_num = (float)(float_num/10); dstate_setinfo("battery.voltage.low", "%2.2f", float_num); /* exhaust threshold */ - float_num = get_word(&my_answer[5]); + float_num = get_word_float(&my_answer[5]); float_num = (float)(float_num/10); dstate_setinfo("battery.voltage.exhaust", "%2.2f", float_num); #endif } - + #ifdef EXTRADATA /* GET history data */ res = command_read_sequence(UPS_HISTORY_DATA, my_answer); @@ -695,8 +749,8 @@ void upsdrv_updateinfo(void) long_num -= (long)(hour*3600); minute = (int)(long_num / 60); long_num -= (minute*60); - dstate_setinfo("ups.total.runtime", "%d days %dh %dm %lds", day, hour, minute, long_num); - + dstate_setinfo("ups.total.runtime", "%d days %dh %dm %lus", day, hour, minute, long_num); + /* ups inverter runtime */ long_num = get_long(&my_answer[5]); day = (int)(long_num / 86400); @@ -705,22 +759,22 @@ void upsdrv_updateinfo(void) long_num -= (long)(hour*3600); minute = (int)(long_num / 60); long_num -= (minute*60); - dstate_setinfo("ups.inverter.runtime", "%d days %dh %dm %lds", day, hour, minute, long_num); + dstate_setinfo("ups.inverter.runtime", "%d days %dh %dm %lus", day, hour, minute, long_num); /* ups inverter interventions */ - dstate_setinfo("ups.inverter.interventions", "%d", get_word(&my_answer[9])); + dstate_setinfo("ups.inverter.interventions", "%u", get_word(&my_answer[9])); /* battery full discharges */ - dstate_setinfo("battery.full.discharges", "%d", get_word(&my_answer[11])); + dstate_setinfo("battery.full.discharges", "%u", get_word(&my_answer[11])); /* ups bypass / stabilizer interventions */ int_num = get_word(&my_answer[13]); - if (int_num == -2) dstate_setinfo("ups.bypass.interventions", "%s", "not avaliable"); - if (int_num >= 0) dstate_setinfo("ups.bypass.interventions", "%d", int_num); + if ((int16_t)int_num == -2) dstate_setinfo("ups.bypass.interventions", "%s", "not avaliable"); + if ((int16_t)int_num >= 0) dstate_setinfo("ups.bypass.interventions", "%u", int_num); /* ups overheatings */ int_num = get_word(&my_answer[15]); - if (int_num == -2) dstate_setinfo("ups.overheatings", "%s", "not avalilable"); - if (int_num >= 0) dstate_setinfo("ups.overheatings", "%d", int_num); + if ((int16_t)int_num == -2) dstate_setinfo("ups.overheatings", "%s", "not avalilable"); + if ((int16_t)int_num >= 0) dstate_setinfo("ups.overheatings", "%u", int_num); } #endif - + /* GET times on battery */ res = command_read_sequence(UPS_GET_TIMES_ON_BATTERY, my_answer); if (res < 0) { @@ -729,8 +783,8 @@ void upsdrv_updateinfo(void) } else { autorestart = my_answer[5]; } - - + + /* GET schedule */ res = command_read_sequence(UPS_GET_SCHEDULING, my_answer); if (res < 0) { @@ -739,21 +793,21 @@ void upsdrv_updateinfo(void) } else { /* time remaining to shutdown */ long_num = get_long(&my_answer[1]); - if (long_num == -1) { - dstate_setinfo("ups.delay.shutdown", "%d", 120); + if ((int32_t)long_num == -1) { + dstate_setinfo("ups.delay.shutdown", "%d", 120); } else { - dstate_setinfo("ups.delay.shutdown", "%ld", long_num); + dstate_setinfo("ups.delay.shutdown", "%lu", (unsigned long)long_num); } /* time remaining to restart */ long_num = get_long(&my_answer[5]); - if (long_num == -1) { - dstate_setinfo("ups.delay.start", "%d", 0); + if ((int32_t)long_num == -1) { + dstate_setinfo("ups.delay.start", "%d", 0); } else { - dstate_setinfo("ups.delay.start", "%ld", long_num); - } + dstate_setinfo("ups.delay.start", "%lu", (unsigned long)long_num); + } } - - + + /* GET ups status */ res = command_read_sequence(UPS_STATUS, my_answer); if (res < 0) { @@ -766,7 +820,7 @@ void upsdrv_updateinfo(void) dstate_setinfo("ups.temperature", "%d", my_answer[3]); } else { dstate_setinfo("ups.temperature", "%s", "not available"); - } + } /* Status */ status_init(); switch (my_answer[1]) { /* byte 1 = STATUS */ @@ -786,7 +840,7 @@ void upsdrv_updateinfo(void) default: printf("status unknown \n"); break; - } + } switch (my_answer[2]) { /* byte 2 = FAULTS */ case 0x00: /* all right */ break; @@ -806,7 +860,7 @@ void upsdrv_updateinfo(void) printf("status unknown \n"); break; } - status_commit(); + status_commit(); dstate_dataok(); } return; @@ -815,30 +869,29 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { unsigned char command[10], answer[10]; - - + /* Ensure that the ups is configured for automatically - restart after a complete battery discharge + restart after a complete battery discharge and when the power comes back after a shutdown */ if (! autorestart) { command[0]=UPS_SET_TIMES_ON_BATTERY; - command[1]=0x00; /* max time on */ + command[1]=0x00; /* max time on */ command[2]=0x00; /* battery */ - + command[3]=0x00; /* max time after */ command[4]=0x00; /* battery reserve */ - + command[5]=0x01; /* autorestart after battery depleted enabled */ command_write_sequence(command, 6, answer); } - + /* shedule a shutdown in 120 seconds */ - command[0]=UPS_SET_SCHEDULING; + command[0]=UPS_SET_SCHEDULING; command[1]=0x96; /* remaining */ command[2]=0x00; /* time */ command[3]=0x00; /* to */ command[4]=0x00; /* shutdown 150 secs */ - + /* restart time has been set to 1 instead of 0 for avoiding a bug in some ups firmware */ command[5]=0x01; /* programmed */ @@ -860,7 +913,7 @@ static int instcmd(const char *cmdname, const char *extra) { unsigned char command[10], answer[10]; int res; - + if (!strcasecmp(cmdname, "beeper.off")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, @@ -879,7 +932,7 @@ static int instcmd(const char *cmdname, const char *extra) /* Same stuff as upsdrv_shutdown() */ if (! autorestart) { command[0]=UPS_SET_TIMES_ON_BATTERY; - command[1]=0x00; /* max time on */ + command[1]=0x00; /* max time on */ command[2]=0x00; /* battery */ command[3]=0x00; /* max time after */ command[4]=0x00; /* battery reserve */ @@ -887,12 +940,12 @@ static int instcmd(const char *cmdname, const char *extra) command_write_sequence(command, 6, answer); } /* shedule a shutdown in 30 seconds */ - command[0]=UPS_SET_SCHEDULING; + command[0]=UPS_SET_SCHEDULING; command[1]=0x1e; /* remaining */ command[2]=0x00; /* time */ command[3]=0x00; /* to */ command[4]=0x00; /* shutdown 30 secs */ - + command[5]=0x01; /* programmed */ command[6]=0x00; /* time */ command[7]=0x00; /* to */ @@ -900,15 +953,15 @@ static int instcmd(const char *cmdname, const char *extra) command_write_sequence(command, 9, answer); return STAT_INSTCMD_HANDLED; } - + if (!strcasecmp(cmdname, "shutdown.stayoff")) { /* shedule a shutdown in 30 seconds with no restart (-1) */ - command[0]=UPS_SET_SCHEDULING; + command[0]=UPS_SET_SCHEDULING; command[1]=0x1e; /* remaining */ command[2]=0x00; /* time */ command[3]=0x00; /* to */ command[4]=0x00; /* shutdown 150 secs */ - + command[5]=0xff; /* programmed */ command[6]=0xff; /* time */ command[7]=0xff; /* to */ @@ -916,15 +969,15 @@ static int instcmd(const char *cmdname, const char *extra) command_write_sequence(command, 9, answer); return STAT_INSTCMD_HANDLED; } - + if (!strcasecmp(cmdname, "shutdown.stop")) { /* set shutdown and restart time to -1 (no shutdown, no restart) */ - command[0]=UPS_SET_SCHEDULING; + command[0]=UPS_SET_SCHEDULING; command[1]=0xff; /* remaining */ command[2]=0xff; /* time */ command[3]=0xff; /* to */ command[4]=0xff; /* shutdown -1 (no shutdown) */ - + command[5]=0xff; /* programmed */ command[6]=0xff; /* time */ command[7]=0xff; /* to */ @@ -935,30 +988,30 @@ static int instcmd(const char *cmdname, const char *extra) if (!strcasecmp(cmdname, "test.failure.start")) { /* force ups on battery power */ - command[0]=UPS_SET_BATTERY_TEST; - command[1]=0x01; + command[0]=UPS_SET_BATTERY_TEST; + command[1]=0x01; /* 0 = perform battery test 1 = force UPS on battery power 2 = restore standard mode (mains power) */ command_write_sequence(command, 2, answer); return STAT_INSTCMD_HANDLED; } - + if (!strcasecmp(cmdname, "test.failure.stop")) { /* restore standard mode (mains power) */ command[0]=UPS_SET_BATTERY_TEST; - command[1]=0x02; + command[1]=0x02; /* 0 = perform battery test 1 = force UPS on battery power 2 = restore standard mode (mains power) */ command_write_sequence(command, 2, answer); return STAT_INSTCMD_HANDLED; } - + if (!strcasecmp(cmdname, "test.battery.start")) { /* launch battery test */ - command[0]=UPS_SET_BATTERY_TEST; - command[1]=0x00; + command[0]=UPS_SET_BATTERY_TEST; + command[1]=0x00; /* 0 = perform battery test 1 = force UPS on battery power 2 = restore standard mode (mains power) */ @@ -969,22 +1022,22 @@ static int instcmd(const char *cmdname, const char *extra) case 0x00: /* all right */ dstate_setinfo("ups.test.result", "OK"); break; - case 0x01: + case 0x01: dstate_setinfo("ups.test.result", "Battery charge: 20%%"); break; - case 0x02: + case 0x02: dstate_setinfo("ups.test.result", "Battery charge: 40%%"); break; - case 0x03: + case 0x03: dstate_setinfo("ups.test.result", "Battery charge: 60%%"); break; - case 0x04: + case 0x04: dstate_setinfo("ups.test.result", "Battery charge: 80%%"); break; - case 0x05: + case 0x05: dstate_setinfo("ups.test.result", "Battery charge: 100%%"); break; - case 0xfe: + case 0xfe: dstate_setinfo("ups.test.result", "Bad battery pack: replace"); break; default: @@ -996,22 +1049,11 @@ static int instcmd(const char *cmdname, const char *extra) upslogx(LOG_NOTICE, "test battery byte 1 = %x", answer[1]); return STAT_INSTCMD_HANDLED; } - + if (!strcasecmp(cmdname, "beeper.enable")) { /* set buzzer to not muted */ - command[0]=UPS_SET_BUZZER_MUTE; - command[1]=0x00; - /* 0 = not muted - 1 = muted - 2 = read current status */ - command_write_sequence(command, 2, answer); - return STAT_INSTCMD_HANDLED; - } - - if (!strcasecmp(cmdname, "beeper.mute")) { - /* set buzzer to muted */ - command[0]=UPS_SET_BUZZER_MUTE; - command[1]=0x01; + command[0]=UPS_SET_BUZZER_MUTE; + command[1]=0x00; /* 0 = not muted 1 = muted 2 = read current status */ @@ -1019,7 +1061,18 @@ static int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + if (!strcasecmp(cmdname, "beeper.mute")) { + /* set buzzer to muted */ + command[0]=UPS_SET_BUZZER_MUTE; + command[1]=0x01; + /* 0 = not muted + 1 = muted + 2 = read current status */ + command_write_sequence(command, 2, answer); + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -1041,12 +1094,12 @@ void upsdrv_makevartable(void) void upsdrv_initups(void) { - upsfd = ser_open(device_path); - ser_set_speed(upsfd, device_path, B2400); + upsfd = ser_open(device_path); + ser_set_speed(upsfd, device_path, B2400); send_zeros(); } void upsdrv_cleanup(void) { - ser_close(upsfd, device_path); + ser_close(upsfd, device_path); } diff --git a/drivers/mge-hid.c b/drivers/mge-hid.c index 7c07703..55d56cf 100644 --- a/drivers/mge-hid.c +++ b/drivers/mge-hid.c @@ -1,7 +1,8 @@ -/* mge-hid.c - data to monitor MGE UPS SYSTEMS HID (USB and serial) devices +/* mge-hid.c - data to monitor Eaton / MGE HID (USB and serial) devices * - * Copyright (C) 2003 - 2009 - * Arnaud Quette + * Copyright (C) + * 2003 - 2015 Arnaud Quette + * 2015 - 2016 Eaton / Arnaud Quette * * Sponsored by MGE UPS SYSTEMS * @@ -22,11 +23,22 @@ * */ +/* TODO list: + * - better processing of FW info: + * * some models (HP R5000) include firmware.aux (00.01.0021;00.01.00) + * * other (9130) need more processing (0128 => 1.28) + * ... + * - better handling of input.transfer.* (need dstate_addrange) + * - outlet management logic (Ie, for outlet.X.load.{on,off}.delay + * => use outlet.X.delay.{start,stop} + */ + #include "main.h" /* for getval() */ #include "usbhid-ups.h" #include "mge-hid.h" +#include "nut_float.h" -#define MGE_HID_VERSION "MGE HID 1.27" +#define MGE_HID_VERSION "MGE HID 1.46" /* (prev. MGE Office Protection Systems, prev. MGE UPS SYSTEMS) */ /* Eaton */ @@ -38,6 +50,20 @@ /* Powerware */ #define POWERWARE_VENDORID 0x0592 +/* Hewlett Packard */ +#define HP_VENDORID 0x03f0 + +/* AEG */ +#define AEG_VENDORID 0x2b2d + +/* Note that normally this VID is handled by Liebert/Phoenixtec HID mapping, + * here it is just for for AEG PROTECT NAS devices: */ +/* Phoenixtec Power Co., Ltd */ +#define PHOENIXTEC 0x06da + +/* IBM */ +#define IBM_VENDORID 0x04b3 + #ifndef SHUT_MODE #include "usb-common.h" @@ -53,8 +79,23 @@ static usb_device_id_t mge_usb_device_table[] = { /* PW 9140 */ { USB_DEVICE(POWERWARE_VENDORID, 0x0004), NULL }, + /* R/T3000 */ + { USB_DEVICE(HP_VENDORID, 0x1fe5), NULL }, + /* R/T3000 */ + { USB_DEVICE(HP_VENDORID, 0x1fe6), NULL }, + /* various models */ + { USB_DEVICE(HP_VENDORID, 0x1fe7), NULL }, + { USB_DEVICE(HP_VENDORID, 0x1fe8), NULL }, + + /* PROTECT B / NAS */ + { USB_DEVICE(AEG_VENDORID, 0xffff), NULL }, + { USB_DEVICE(PHOENIXTEC, 0xffff), NULL }, + + /* 6000 VA LCD 4U Rack UPS; 5396-1Kx */ + { USB_DEVICE(IBM_VENDORID, 0x0001), NULL }, + /* Terminating entry */ - { -1, -1, NULL } + { 0, 0, NULL } }; #endif @@ -77,11 +118,12 @@ typedef enum { MGE_PULSAR_M = 0x400, /* MGE Pulsar M series */ MGE_PULSAR_M_2200, MGE_PULSAR_M_3000, - MGE_PULSAR_M_3000_XL + MGE_PULSAR_M_3000_XL, + EATON_5P = 0x500 /* Eaton 5P / 5PX series */ } models_type_t; /* Default to line-interactive or online (ie, not offline). - * This is then overriden for offline, through mge_model_names */ + * This is then overridden for offline, through mge_model_names */ static models_type_t mge_type = MGE_DEFAULT; /* Countries definition, for region specific settings and features */ @@ -99,14 +141,232 @@ static int country_code = COUNTRY_UNKNOWN; static char mge_scratch_buf[20]; +/* ABM - Advanced Battery Monitoring + *********************************** + * Synthesis table + * HID data | Charger in ABM mode | Charger in Constant mode + * UPS.BatterySystem.Charger.ABMEnable | 1 | 0 + * UPS.PowerSummary.PresentStatus.ACPresent | On utility | On battery | On utility | On battery + * Charger ABM mode | Charging | Floating | Resting | Discharging | Disabled | Disabled + * UPS.BatterySystem.Charger.Mode | 1 | 3 | 4 | 2 | 6 | 6 + * UPS.PowerSummary.PresentStatus.Charging | 1 | 1 | 1 | 0 | 1 | 0 + * UPS.PowerSummary.PresentStatus.Discharging | 0 |  0 |  0 | 1 | 0 | 1 + * + * Notes (from David G. Miller) to understand ABM status: + * When supporting ABM, when a UPS powers up or returns from battery, or + * ends the ABM rest mode, it enters charge mode. + * Some UPSs run a different charger reference voltage during charge mode + * but all the newer models should not be doing that, but basically once + * the battery voltage reaches the charger reference level (should be 2.3 + * volts/cell), the charger is considered in float mode. Some UPSs will not + * annunciate float mode until the charger power starts falling from the maximum + * level indicating the battery is truly at the float voltage or in float mode. + * The %charge level is based on battery voltage and the charge mode timer + * (should be 48 hours) and some UPSs add in a value that's related to charger + * power output. So you can have UPS that enters float mode with anywhere + * from 80% or greater battery capacity. + * float mode is not important from the software's perspective, it's there to + * help determine if the charger is advancing correctly. + * So in float mode, the charger is charging the battery, so by definition you + * can assert the CHRG flag in NUT when in “float” mode or “charge” mode. + * When in “rest” mode the charger is not delivering anything to the battery, + * but it will when the ABM cycle(28 days) ends, or a battery discharge occurs + * and utility returns. This is when the ABM status should be “resting”. + * If a battery failure is detected that disables the charger, it should be + * reporting “off” in the ABM charger status. + * Of course when delivering load power from the battery, the ABM status is + * discharging. + */ + +#define ABM_UNKNOWN -1 +#define ABM_DISABLED 0 +#define ABM_ENABLED 1 + +/* Internal flag to process battery status (CHRG/DISCHRG) and ABM */ +static int advanced_battery_monitoring = ABM_UNKNOWN; + +/* Used to store internally if ABM is enabled or not */ +static const char *eaton_abm_enabled_fun(double value) +{ + advanced_battery_monitoring = value; + + upsdebugx(2, "ABM is %s", (advanced_battery_monitoring==1)?"enabled":"disabled"); + + /* Return NULL, not to get the value published! */ + return NULL; +} + +static info_lkp_t eaton_abm_enabled_info[] = { + { 0, "dummy", eaton_abm_enabled_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* Note 1: This point will need more clarification! */ +# if 0 +/* Used to store internally if ABM is enabled or not (for legacy units) */ +static const char *eaton_abm_enabled_legacy_fun(double value) +{ + advanced_battery_monitoring = value; + + upsdebugx(2, "ABM is %s (legacy data)", (advanced_battery_monitoring==1)?"enabled":"disabled"); + + /* Return NULL, not to get the value published! */ + return NULL; +} + +static info_lkp_t eaton_abm_enabled_legacy_info[] = { + { 0, "dummy", eaton_abm_enabled_legacy_fun, NULL }, + { 0, NULL, NULL, NULL } +}; +#endif /* if 0 */ + +/* Used to process ABM flags, for battery.charger.status */ +static const char *eaton_abm_status_fun(double value) +{ + /* Don't process if ABM is disabled */ + if (advanced_battery_monitoring == ABM_DISABLED) { + /* Clear any previously published data, in case + * the user has switched off ABM */ + dstate_delinfo("battery.charger.status"); + return NULL; + } + + upsdebugx(2, "ABM numeric status: %i", (int)value); + + switch ((long)value) + { + case 1: + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "charging"); + break; + case 2: + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "discharging"); + break; + case 3: + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "floating"); + break; + case 4: + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "resting"); + break; + case 6: /* ABM Charger Disabled */ + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "off"); + break; + case 5: /* Undefined - ABM is not activated */ + default: + /* Return NULL, not to get the value published! */ + return NULL; + } + + upsdebugx(2, "ABM string status: %s", mge_scratch_buf); + + return mge_scratch_buf; +} + +static info_lkp_t eaton_abm_status_info[] = { + { 1, "dummy", eaton_abm_status_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* Used to process ABM flags, for ups.status (CHRG/DISCHRG/RB) */ +static const char *eaton_abm_chrg_dischrg_fun(double value) +{ + /* Don't process if ABM is disabled */ + if (advanced_battery_monitoring == ABM_DISABLED) + return NULL; + + switch ((long)value) + { + case 1: /* charging status */ + case 3: /* floating status */ + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "chrg"); + break; + case 2: + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "dischrg"); + break; + case 6: /* ABM Charger Disabled */ + case 4: /* resting, nothing to publish! (?) */ + case 5: /* Undefined - ABM is not activated */ + default: + /* Return NULL, not to get the value published! */ + return NULL; + } + + upsdebugx(2, "ABM CHRG/DISCHRG legacy string status (ups.status): %s", mge_scratch_buf); + + return mge_scratch_buf; +} + +static info_lkp_t eaton_abm_chrg_dischrg_info[] = { + { 1, "dummy", eaton_abm_chrg_dischrg_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* ABM also implies that standard CHRG/DISCHRG are processed according + * to weither ABM is enabled or not... + * If ABM is disabled, we publish these legacy status + * Otherwise, we don't publish on ups.status, but only battery.charger.status */ +/* FIXME: we may prefer to publish the CHRG/DISCHRG status + * on battery.charger.status?! */ +static const char *eaton_abm_check_dischrg_fun(double value) +{ + if (advanced_battery_monitoring == ABM_DISABLED) + { + if (d_equal(value, 1)) { + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "dischrg"); + } + else { + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "!dischrg"); + } + } + else { + /* Else, ABM is enabled, we should return NULL, + * not to get the value published! + * However, clear flags that would persist in case of prior + * publication in ABM-disabled mode */ + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "!dischrg"); + } + return mge_scratch_buf; +} + +static info_lkp_t eaton_discharging_info[] = { + { 1, "dummy", eaton_abm_check_dischrg_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static const char *eaton_abm_check_chrg_fun(double value) +{ + if (advanced_battery_monitoring == ABM_DISABLED) + { + if (d_equal(value, 1)) { + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "chrg"); + } + else { + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "!chrg"); + } + } + else { + /* Else, ABM is enabled, we should return NULL, + * not to get the value published! + * However, clear flags that would persist in case of prior + * publication in ABM-disabled mode */ + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", "!chrg"); + } + return mge_scratch_buf; +} + +static info_lkp_t eaton_charging_info[] = { + { 1, "dummy", eaton_abm_check_chrg_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + /* The HID path 'UPS.PowerSummary.Time' reports Unix time (ie the number of * seconds since 1970-01-01 00:00:00. This has to be split between ups.date and * ups.time */ static const char *mge_date_conversion_fun(double value) { time_t sec = value; + struct tm tmbuf; - if (strftime(mge_scratch_buf, sizeof(mge_scratch_buf), "%Y/%m/%d", localtime(&sec)) == 10) { + if (strftime(mge_scratch_buf, sizeof(mge_scratch_buf), "%Y/%m/%d", localtime_r(&sec, &tmbuf)) == 10) { return mge_scratch_buf; } @@ -117,8 +377,9 @@ static const char *mge_date_conversion_fun(double value) static const char *mge_time_conversion_fun(double value) { time_t sec = value; + struct tm tmbuf; - if (strftime(mge_scratch_buf, sizeof(mge_scratch_buf), "%H:%M:%S", localtime(&sec)) == 8) { + if (strftime(mge_scratch_buf, sizeof(mge_scratch_buf), "%H:%M:%S", localtime_r(&sec, &tmbuf)) == 8) { return mge_scratch_buf; } @@ -160,19 +421,23 @@ static double mge_time_conversion_nuf(const char *value) } static info_lkp_t mge_date_conversion[] = { - { 0, NULL, mge_date_conversion_fun, mge_date_conversion_nuf } + { 0, NULL, mge_date_conversion_fun, mge_date_conversion_nuf }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_time_conversion[] = { - { 0, NULL, mge_time_conversion_fun, mge_time_conversion_nuf } + { 0, NULL, mge_time_conversion_fun, mge_time_conversion_nuf }, + { 0, NULL, NULL, NULL } }; #else static info_lkp_t mge_date_conversion[] = { - { 0, NULL, mge_date_conversion_fun, NULL } + { 0, NULL, mge_date_conversion_fun, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_time_conversion[] = { - { 0, NULL, mge_time_conversion_fun, NULL } + { 0, NULL, mge_time_conversion_fun, NULL }, + { 0, NULL, NULL, NULL } }; #endif /* HAVE_STRPTIME */ @@ -190,6 +455,7 @@ static const char *mge_battery_voltage_nominal_fun(double value) break; case MGE_PULSAR_M: + case EATON_5P: break; default: @@ -201,7 +467,8 @@ static const char *mge_battery_voltage_nominal_fun(double value) } static info_lkp_t mge_battery_voltage_nominal[] = { - { 0, NULL, mge_battery_voltage_nominal_fun } + { 0, NULL, mge_battery_voltage_nominal_fun, NULL }, + { 0, NULL, NULL, NULL } }; /* The HID path 'UPS.PowerSummary.Voltage' only reports @@ -213,6 +480,7 @@ static const char *mge_battery_voltage_fun(double value) { case MGE_EVOLUTION: case MGE_PULSAR_M: + case EATON_5P: break; default: @@ -224,7 +492,8 @@ static const char *mge_battery_voltage_fun(double value) } static info_lkp_t mge_battery_voltage[] = { - { 0, NULL, mge_battery_voltage_fun } + { 0, NULL, mge_battery_voltage_fun, NULL }, + { 0, NULL, NULL, NULL } }; static const char *mge_powerfactor_conversion_fun(double value) @@ -234,7 +503,8 @@ static const char *mge_powerfactor_conversion_fun(double value) } static info_lkp_t mge_powerfactor_conversion[] = { - { 0, NULL, mge_powerfactor_conversion_fun } + { 0, NULL, mge_powerfactor_conversion_fun, NULL }, + { 0, NULL, NULL, NULL } }; static const char *mge_battery_capacity_fun(double value) @@ -244,59 +514,66 @@ static const char *mge_battery_capacity_fun(double value) } static info_lkp_t mge_battery_capacity[] = { - { 0, NULL, mge_battery_capacity_fun } + { 0, NULL, mge_battery_capacity_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t eaton_enable_disable_info[] = { + { 0, "disabled", NULL, NULL }, + { 1, "enabled", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_upstype_conversion[] = { - { 1, "offline / line interactive", NULL }, - { 2, "online", NULL }, - { 3, "online - unitary/parallel", NULL }, - { 4, "online - parallel with hot standy", NULL }, - { 5, "online - hot standby redundancy", NULL }, - { 0, NULL, NULL } + { 1, "offline / line interactive", NULL, NULL }, + { 2, "online", NULL, NULL }, + { 3, "online - unitary/parallel", NULL, NULL }, + { 4, "online - parallel with hot standy", NULL, NULL }, + { 5, "online - hot standby redundancy", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_sensitivity_info[] = { - { 0, "normal", NULL }, - { 1, "high", NULL }, - { 2, "low", NULL }, - { 0, NULL, NULL } + { 0, "normal", NULL, NULL }, + { 1, "high", NULL, NULL }, + { 2, "low", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_emergency_stop[] = { - { 1, "Emergency stop!", NULL }, - { 0, NULL, NULL } + { 1, "Emergency stop!", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_wiring_fault[] = { - { 1, "Wiring fault!", NULL }, - { 0, NULL, NULL } + { 1, "Wiring fault!", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_config_failure[] = { - { 1, "Fatal EEPROM fault!", NULL }, - { 0, NULL, NULL } + { 1, "Fatal EEPROM fault!", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_inverter_volthi[] = { - { 1, "Inverter AC voltage too high!", NULL }, - { 0, NULL, NULL } + { 1, "Inverter AC voltage too high!", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_inverter_voltlo[] = { - { 1, "Inverter AC voltage too low!", NULL }, - { 0, NULL, NULL } + { 1, "Inverter AC voltage too low!", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_short_circuit[] = { - { 1, "Output short circuit!", NULL }, - { 0, NULL, NULL } + { 1, "Output short circuit!", NULL, NULL }, + { 0, NULL, NULL, NULL } }; -info_lkp_t mge_onbatt_info[] = { - { 1, "!online", NULL }, - { 0, "online", NULL }, - { 0, NULL, NULL } +static info_lkp_t mge_onbatt_info[] = { + { 1, "!online", NULL, NULL }, + { 0, "online", NULL, NULL }, + { 0, NULL, NULL, NULL } }; /* allow limiting to ups.model = Protection Station, Ellipse Eco @@ -311,6 +588,7 @@ static const char *eaton_check_pegasus_fun(double value) /* Only consider non European models */ if (country_code != COUNTRY_EUROPE) break; + return NULL; default: return NULL; } @@ -320,10 +598,59 @@ static const char *eaton_check_pegasus_fun(double value) } static info_lkp_t pegasus_threshold_info[] = { - { 10, "10", eaton_check_pegasus_fun }, - { 25, "25", eaton_check_pegasus_fun }, - { 60, "60", eaton_check_pegasus_fun }, - { 0, NULL, NULL } + { 10, "10", eaton_check_pegasus_fun, NULL }, + { 25, "25", eaton_check_pegasus_fun, NULL }, + { 60, "60", eaton_check_pegasus_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* allow limiting standard yes/no info (here, to enable ECO mode) to + * ups.model = Protection Station, Ellipse Eco and 3S (US 750 and AUS 700 only!) + * this allows to enable special flags used in hid_info_t entries (Ie RW) */ +static const char *pegasus_yes_no_info_fun(double value) +{ + switch (mge_type & 0xFF00) /* Ignore model byte */ + { + case MGE_PEGASUS: + break; + case MGE_3S: + /* Only consider non European models */ + if (country_code != COUNTRY_EUROPE) + break; + return NULL; + default: + return NULL; + } + + return (d_equal(value, 0)) ? "no" : "yes"; +} + +/* Conversion back of yes/no info */ +static double pegasus_yes_no_info_nuf(const char *value) +{ + switch (mge_type & 0xFF00) /* Ignore model byte */ + { + case MGE_PEGASUS: + break; + case MGE_3S: + /* Only consider non European models */ + if (country_code != COUNTRY_EUROPE) + break; + return 0; + default: + return 0; + } + + if (!strncmp(value, "yes", 3)) + return 1; + else + return 0; +} + +static info_lkp_t pegasus_yes_no_info[] = { + { 0, "no", pegasus_yes_no_info_fun, pegasus_yes_no_info_nuf }, + { 1, "yes", pegasus_yes_no_info_fun, pegasus_yes_no_info_nuf }, + { 0, NULL, NULL, NULL } }; /* Determine country using UPS.PowerSummary.Country. @@ -338,8 +665,42 @@ static const char *eaton_check_country_fun(double value) } static info_lkp_t eaton_check_country_info[] = { - { 0, "dummy", eaton_check_country_fun }, - { 0, NULL, NULL } + { 0, "dummy", eaton_check_country_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* When UPS.PowerConverter.Output.ActivePower is not present, + * compute a realpower approximation using available data */ +static const char *eaton_compute_realpower_fun(double value) +{ + NUT_UNUSED_VARIABLE(value); + const char *str_ups_load = dstate_getinfo("ups.load"); + const char *str_power_nominal = dstate_getinfo("ups.power.nominal"); + const char *str_powerfactor = dstate_getinfo("output.powerfactor"); + float powerfactor = 0.80; + int power_nominal = 0; + int ups_load = 0; + double realpower = 0; + if (str_power_nominal && str_ups_load) { + /* Extract needed values */ + ups_load = atoi(str_ups_load); + power_nominal = atoi(str_power_nominal); + if (str_powerfactor) + powerfactor = atoi(str_powerfactor); + /* Compute the value */ + realpower = round(ups_load * 0.01 * power_nominal * powerfactor); + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.0f", realpower); + upsdebugx(1, "eaton_compute_realpower_fun(%s)", mge_scratch_buf); + return mge_scratch_buf; + } + /* else can't process */ + /* Return NULL, not to get the value published! */ + return NULL; +} + +static info_lkp_t eaton_compute_realpower_info[] = { + { 0, "dummy", eaton_compute_realpower_fun, NULL }, + { 0, NULL, NULL, NULL } }; /* Limit nominal output voltage according to HV or LV models */ @@ -386,6 +747,7 @@ static const char *nominal_output_voltage_fun(double value) case 240: if ((mge_type & 0xFF00) >= MGE_DEFAULT) break; + return NULL; default: return NULL; } @@ -408,10 +770,15 @@ static const char *nominal_output_voltage_fun(double value) * support both HV values */ if (country_code == COUNTRY_EUROPE_208) break; + /* explicit fallthrough: */ + goto fallthrough_value; + case 220: case 230: case 240: + fallthrough_value: break; + default: return NULL; } @@ -429,26 +796,42 @@ static info_lkp_t nominal_output_voltage_info[] = { /* line-interactive, starting with Evolution, support both HV values */ /* HV models */ /* 208V */ - { 200, "200", nominal_output_voltage_fun }, - { 208, "208", nominal_output_voltage_fun }, + { 200, "200", nominal_output_voltage_fun, NULL }, + { 208, "208", nominal_output_voltage_fun, NULL }, /* HV models */ /* 230V */ - { 220, "220", nominal_output_voltage_fun }, - { 230, "230", nominal_output_voltage_fun }, - { 240, "240", nominal_output_voltage_fun }, + { 220, "220", nominal_output_voltage_fun, NULL }, + { 230, "230", nominal_output_voltage_fun, NULL }, + { 240, "240", nominal_output_voltage_fun, NULL }, /* LV models */ - { 100, "100", nominal_output_voltage_fun }, - { 110, "110", nominal_output_voltage_fun }, - { 120, "120", nominal_output_voltage_fun }, - { 127, "127", nominal_output_voltage_fun }, - { 0, NULL, NULL } + { 100, "100", nominal_output_voltage_fun, NULL }, + { 110, "110", nominal_output_voltage_fun, NULL }, + { 120, "120", nominal_output_voltage_fun, NULL }, + { 127, "127", nominal_output_voltage_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* Limit reporting "online / !online" to when "!off" */ +static const char *eaton_converter_online_fun(double value) +{ + unsigned ups_status = ups_status_get(); + + if (ups_status & STATUS(OFF)) + return NULL; + else + return (d_equal(value, 0)) ? "!online" : "online"; +} + +static info_lkp_t eaton_converter_online_info[] = { + { 0, "dummy", eaton_converter_online_fun, NULL }, + { 0, NULL, NULL, NULL } }; /* --------------------------------------------------------------- */ /* Vendor-specific usage table */ /* --------------------------------------------------------------- */ -/* MGE UPS SYSTEMS usage table */ +/* Eaton / MGE HID usage table */ static usage_lkp_t mge_usage_lkp[] = { { "Undefined", 0xffff0000 }, { "STS", 0xffff0001 }, @@ -474,7 +857,7 @@ static usage_lkp_t mge_usage_lkp[] = { { "EventID", 0xffff001f }, { "CircuitBreaker", 0xffff0020 }, { "TransferForbidden", 0xffff0021 }, - { "OverallAlarm", 0xffff0022 }, + { "OverallAlarm", 0xffff0022 }, /* renamed to Alarm in Eaton SW! */ { "Dephasing", 0xffff0023 }, { "BypassBreaker", 0xffff0024 }, { "PowerModule", 0xffff0025 }, @@ -486,8 +869,9 @@ static usage_lkp_t mge_usage_lkp[] = { { "NotificationStatus", 0xffff002b }, { "ProtectionLost", 0xffff002c }, { "ConfigurationFailure", 0xffff002d }, + { "CompatibilityFailure", 0xffff002e }, /* 0xffff002e-0xffff003f => Reserved */ - { "SwitchType", 0xffff0040 }, + { "SwitchType", 0xffff0040 }, /* renamed to Type in Eaton SW! */ { "ConverterType", 0xffff0041 }, { "FrequencyConverterMode", 0xffff0042 }, { "AutomaticRestart", 0xffff0043 }, @@ -556,8 +940,12 @@ static usage_lkp_t mge_usage_lkp[] = { { "HighHumidity", 0xffff0082 }, { "LowTemperature", 0xffff0083 }, { "HighTemperature", 0xffff0084 }, - /* 0xffff0085-0xffff008f (minus 0xffff0086) => Reserved */ + { "ECOControl", 0xffff0085 }, { "Efficiency", 0xffff0086 }, + { "ABMEnable", 0xffff0087 }, + { "NegativeCurrent", 0xffff0088 }, + { "AutomaticStart", 0xffff0089 }, + /* 0xffff008a-0xffff008f => Reserved */ { "Count", 0xffff0090 }, { "Timer", 0xffff0091 }, { "Interval", 0xffff0092 }, @@ -569,16 +957,26 @@ static usage_lkp_t mge_usage_lkp[] = { { "Code", 0xffff0098 }, { "DataValid", 0xffff0099 }, { "ToggleTimer", 0xffff009a }, - /* 0xffff009b-0xffff009f => Reserved */ - { "PDU", 0xffff00a0 }, + { "BypassTransferDelay", 0xffff009b }, + { "HysteresysVoltageTransfer", 0xffff009c }, + { "SlewRate", 0xffff009d }, + /* 0xffff009e-0xffff009f => Reserved */ + { "PDU", 0xffff00a0 }, { "Breaker", 0xffff00a1 }, { "BreakerID", 0xffff00a2 }, - { "OverVoltage", 0xffff00a3 }, + { "OverVoltage", 0xffff00a3 }, { "Tripped", 0xffff00a4 }, { "OverEnergy", 0xffff00a5 }, - { "OverHumidity", 0xffff00a6 }, - { "LCDControl", 0xffff00a6 }, - /* 0xffff00a8-0xffff00df => Reserved */ + { "OverHumidity", 0xffff00a6 }, + { "ConfigurationReset", 0xffff00a7 }, /* renamed from LCDControl in Eaton SW! */ + { "Level", 0xffff00a8 }, + { "PDUType", 0xffff00a9 }, + { "ReactivePower", 0xffff00aa }, + { "Pole", 0xffff00ab }, + { "PoleID", 0xffff00ac }, + { "Reset", 0xffff00ad }, + { "WatchdogReset", 0xffff00ae }, + /* 0xffff00af-0xffff00df => Reserved */ { "COPIBridge", 0xffff00e0 }, /* 0xffff00e1-0xffff00ef => Reserved */ { "iModel", 0xffff00f0 }, @@ -586,7 +984,8 @@ static usage_lkp_t mge_usage_lkp[] = { { "iTechnicalLevel", 0xffff00f2 }, { "iPartNumber", 0xffff00f3 }, { "iReferenceNumber", 0xffff00f4 }, - /* 0xffff00f5-0xffff00ff => Reserved */ + { "iGang", 0xffff00f5 }, + /* 0xffff00f6-0xffff00ff => Reserved */ /* end of table */ { NULL, 0 } @@ -684,6 +1083,12 @@ static models_name_t mge_model_names [] = { "Evolution", "S 2500", MGE_EVOLUTION_S_2500, NULL }, { "Evolution", "S 3000", MGE_EVOLUTION_S_3000, NULL }, + /* Eaton 5P */ + { "Eaton 5P", "650", EATON_5P, "5P 650" }, + { "Eaton 5P", "850", EATON_5P, "5P 850" }, + { "Eaton 5P", "1150", EATON_5P, "5P 1150" }, + { "Eaton 5P", "1550", EATON_5P, "5P 1550" }, + /* Pulsar M models */ { "PULSAR M", "2200", MGE_PULSAR_M_2200, NULL }, { "PULSAR M", "3000", MGE_PULSAR_M_3000, NULL }, @@ -749,7 +1154,7 @@ static models_name_t mge_model_names [] = { "GALAXY", "3000_30", MGE_DEFAULT, "Galaxy 3000 30 kVA" }, /* end of structure. */ - { NULL } + { NULL, NULL, 0, NULL } }; @@ -781,6 +1186,21 @@ static hid_info_t mge_hid2nut[] = { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%s", HU_FLAG_STATIC, mge_battery_voltage_nominal }, { "battery.protection", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.BatterySystem.Battery.DeepDischargeProtection", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, { "battery.energysave", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].EnergySaving", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, + { "battery.energysave.load", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].ConfigPercentLoad", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + /* Current implementation */ + { "battery.energysave.delay", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].EnergySaving.ShutdownTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + /* Newer implementation */ + { "battery.energysave.delay", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].ShutdownTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "battery.energysave.realpower", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].ConfigActivePower", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + /* ABM (Advanced Battery Monitoring) processing + * Must be processed before the BOOL status */ + /* Not published, just to store in internal var. advanced_battery_monitoring */ + { "battery.charger.status", 0, 0, "UPS.BatterySystem.Charger.ABMEnable", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_enabled_info }, + /* Same as the one above, but for legacy units */ + /* Refer to Note 1 (This point will need more clarification!) + { "battery.charger.status", 0, 0, "UPS.BatterySystem.Charger.PresentStatus.Used", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_enabled_legacy_info }, */ + /* This data is the actual ABM status information */ + { "battery.charger.status", 0, 0, "UPS.BatterySystem.Charger.Mode", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_status_info }, /* UPS page */ { "ups.efficiency", 0, 0, "UPS.PowerConverter.Output.Efficiency", NULL, "%.0f", 0, NULL }, @@ -794,6 +1214,9 @@ static hid_info_t mge_hid2nut[] = { "ups.timer.reboot", 0, 0, "UPS.PowerSummary.DelayBeforeReboot", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.test.result", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "%s", 0, test_read_info }, { "ups.test.interval", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.BatterySystem.Battery.TestPeriod", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + /* Duplicate data for some units (such as 3S) that use a different path + * Only the first valid one will be used */ + { "ups.beeper.status", 0 ,0, "UPS.BatterySystem.Battery.AudibleAlarmControl", NULL, "%s", HU_FLAG_SEMI_STATIC, beeper_info }, { "ups.beeper.status", 0 ,0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "%s", HU_FLAG_SEMI_STATIC, beeper_info }, { "ups.temperature", 0, 0, "UPS.PowerSummary.Temperature", NULL, "%s", 0, kelvin_celsius_conversion }, { "ups.power", 0, 0, "UPS.PowerConverter.Output.ApparentPower", NULL, "%.0f", 0, NULL }, @@ -802,6 +1225,9 @@ static hid_info_t mge_hid2nut[] = { "ups.L3.power", 0, 0, "UPS.PowerConverter.Output.Phase.[3].ApparentPower", NULL, "%.0f", 0, NULL }, { "ups.power.nominal", 0, 0, "UPS.Flow.[4].ConfigApparentPower", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "ups.realpower", 0, 0, "UPS.PowerConverter.Output.ActivePower", NULL, "%.0f", 0, NULL }, + /* When not available, process an approximation from other data, + * but map to apparent power to be called */ + { "ups.realpower", 0, 0, "UPS.Flow.[4].ConfigApparentPower", NULL, "-1", 0, eaton_compute_realpower_info }, { "ups.L1.realpower", 0, 0, "UPS.PowerConverter.Output.Phase.[1].ActivePower", NULL, "%.0f", 0, NULL }, { "ups.L2.realpower", 0, 0, "UPS.PowerConverter.Output.Phase.[2].ActivePower", NULL, "%.0f", 0, NULL }, { "ups.L3.realpower", 0, 0, "UPS.PowerConverter.Output.Phase.[3].ActivePower", NULL, "%.0f", 0, NULL }, @@ -809,6 +1235,7 @@ static hid_info_t mge_hid2nut[] = { "ups.start.auto", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[1].AutomaticRestart", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, { "ups.start.battery", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Input.[3].StartOnBattery", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, { "ups.start.reboot", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.ForcedReboot", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, + { "ups.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.PresentStatus.Switchable", NULL, "%s", HU_FLAG_SEMI_STATIC | HU_FLAG_ENUM, eaton_enable_disable_info }, #ifdef HAVE_STRPTIME { "ups.date", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.Time", NULL, "%s", 0, mge_date_conversion }, { "ups.time", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.Time", NULL, "%s", 0, mge_time_conversion }, @@ -821,9 +1248,11 @@ static hid_info_t mge_hid2nut[] = /* Special case: boolean values that are mapped to ups.status and ups.alarm */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[3].PresentStatus.Used", NULL, NULL, 0, mge_onbatt_info }, - { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.Used", NULL, NULL, 0, online_info }, - { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, - { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, + /* These 2 ones are used when ABM is disabled */ + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, eaton_discharging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, eaton_charging_info }, + /* And this one when ABM is enabled (same as battery.charger.status) */ + { "BOOL", 0, 0, "UPS.BatterySystem.Charger.Mode", NULL, "%.0f", HU_FLAG_QUICK_POLL, eaton_abm_chrg_dischrg_info }, /* FIXME: on Dell, the above requires an "AND" with "UPS.BatterySystem.Charger.Mode = 1" */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, /* Output overload, Level 1 (FIXME: add the level?) */ @@ -840,8 +1269,9 @@ static hid_info_t mge_hid2nut[] = { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.VoltageOutOfRange", NULL, NULL, 0, vrange_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.FrequencyOutOfRange", NULL, NULL, 0, frange_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Good", NULL, NULL, 0, off_info }, - { "BOOL", 0, 0, "UPS.BatterySystem.Charger.PresentStatus.Used", NULL, NULL, 0, off_info }, - /* FIXME: on Dell, the above requires an "AND" with "UPS.BatterySystem.Charger.Mode = 4 (ABM Resting)" */ + /* NOTE: UPS.PowerConverter.Input.[1].PresentStatus.Used" must only be considered when not "OFF", + * and must hence be after "UPS.PowerSummary.PresentStatus.Good" */ + { "BOOL", 0, 0, "UPS.PowerConverter.Input.[1].PresentStatus.Used", NULL, NULL, 0, eaton_converter_online_info }, { "BOOL", 0, 0, "UPS.PowerConverter.Input.[2].PresentStatus.Used", NULL, NULL, 0, bypass_auto_info }, /* Automatic bypass */ { "BOOL", 0, 0, "UPS.PowerConverter.Input.[4].PresentStatus.Used", NULL, NULL, 0, bypass_manual_info }, /* Manual bypass */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FanFailure", NULL, NULL, 0, fanfail_info }, @@ -941,7 +1371,7 @@ static hid_info_t mge_hid2nut[] = { "outlet.power", 0, 0, "UPS.OutletSystem.Outlet.[1].ApparentPower", NULL, "%.0f", 0, NULL }, { "outlet.realpower", 0, 0, "UPS.OutletSystem.Outlet.[1].ActivePower", NULL, "%.0f", 0, NULL }, { "outlet.current", 0, 0, "UPS.OutletSystem.Outlet.[1].Current", NULL, "%.2f", 0, NULL }, - { "outlet.powerfactor", 0, 0, "UPS.OutletSystem.Outlet.[1].PowerFactor", NULL, "%.2f", 0, NULL }, // "%s", 0, mge_powerfactor_conversion }, + { "outlet.powerfactor", 0, 0, "UPS.OutletSystem.Outlet.[1].PowerFactor", NULL, "%.2f", 0, NULL }, /* "%s", 0, mge_powerfactor_conversion }, */ /* First outlet */ { "outlet.1.id", 0, 0, "UPS.OutletSystem.Outlet.[2].OutletID", NULL, "%.0f", HU_FLAG_STATIC, NULL }, @@ -950,18 +1380,22 @@ static hid_info_t mge_hid2nut[] = { "outlet.1.status", 0, 0, "UPS.OutletSystem.Outlet.[2].PresentStatus.SwitchOn/Off", NULL, "%s", 0, on_off_info }, /* For low end models, with 1 non backup'ed outlet */ { "outlet.1.status", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, "%s", 0, on_off_info }, + /* FIXME: change to outlet.1.battery.charge.low, as in mge-xml.c?! */ { "outlet.1.autoswitch.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.OutletSystem.Outlet.[2].RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "outlet.1.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[2].ShutdownTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "outlet.1.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[2].StartupTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "outlet.1.power", 0, 0, "UPS.OutletSystem.Outlet.[2].ApparentPower", NULL, "%.0f", 0, NULL }, { "outlet.1.realpower", 0, 0, "UPS.OutletSystem.Outlet.[2].ActivePower", NULL, "%.0f", 0, NULL }, { "outlet.1.current", 0, 0, "UPS.OutletSystem.Outlet.[2].Current", NULL, "%.2f", 0, NULL }, - { "outlet.1.powerfactor", 0, 0, "UPS.OutletSystem.Outlet.[2].PowerFactor", NULL, "%.2f", 0, NULL }, // "%s", 0, mge_powerfactor_conversion }, + { "outlet.1.powerfactor", 0, 0, "UPS.OutletSystem.Outlet.[2].PowerFactor", NULL, "%.2f", 0, NULL }, /* "%s", 0, mge_powerfactor_conversion }, */ /* Second outlet */ { "outlet.2.id", 0, 0, "UPS.OutletSystem.Outlet.[3].OutletID", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "outlet.2.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, "UPS.OutletSystem.Outlet.[3].OutletID", NULL, "PowerShare Outlet 2", HU_FLAG_ABSENT, NULL }, - /* needed for Pegasus to enable master/slave mode */ - { "outlet.2.switchable", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.OutletSystem.Outlet.[3].PresentStatus.Switchable", NULL, "%s", HU_FLAG_SEMI_STATIC, yes_no_info }, + /* needed for Pegasus to enable master/slave mode: + * FIXME: rename to something more suitable (outlet.?) */ + { "outlet.2.switchable", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.OutletSystem.Outlet.[3].PresentStatus.Switchable", NULL, "%s", HU_FLAG_SEMI_STATIC, pegasus_yes_no_info }, + /* Generic version (RO) for other models */ + { "outlet.2.switchable", 0, 0, "UPS.OutletSystem.Outlet.[3].PresentStatus.Switchable", NULL, "%s", 0, yes_no_info }, { "outlet.2.status", 0, 0, "UPS.OutletSystem.Outlet.[3].PresentStatus.SwitchOn/Off", NULL, "%s", 0, on_off_info }, { "outlet.2.autoswitch.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 3, "UPS.OutletSystem.Outlet.[3].RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, { "outlet.2.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[3].ShutdownTimer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, @@ -969,7 +1403,7 @@ static hid_info_t mge_hid2nut[] = { "outlet.2.power", 0, 0, "UPS.OutletSystem.Outlet.[3].ApparentPower", NULL, "%.0f", 0, NULL }, { "outlet.2.realpower", 0, 0, "UPS.OutletSystem.Outlet.[3].ActivePower", NULL, "%.0f", 0, NULL }, { "outlet.2.current", 0, 0, "UPS.OutletSystem.Outlet.[3].Current", NULL, "%.2f", 0, NULL }, - { "outlet.2.powerfactor", 0, 0, "UPS.OutletSystem.Outlet.[3].PowerFactor", NULL, "%.2f", 0, NULL }, // "%s", 0, mge_powerfactor_conversion }, + { "outlet.2.powerfactor", 0, 0, "UPS.OutletSystem.Outlet.[3].PowerFactor", NULL, "%.2f", 0, NULL }, /* "%s", 0, mge_powerfactor_conversion }, */ /* instant commands. */ /* splited into subset while waiting for extradata support @@ -984,8 +1418,13 @@ static hid_info_t mge_hid2nut[] = { "shutdown.reboot", 0, 0, "UPS.PowerSummary.DelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL}, { "beeper.off", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, { "beeper.on", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, + /* Duplicate commands for some units (such as 3S) that use a different path + * Only the first valid one will be used */ + { "beeper.mute", 0, 0, "UPS.BatterySystem.Battery.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, { "beeper.mute", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, + { "beeper.disable", 0, 0, "UPS.BatterySystem.Battery.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, { "beeper.disable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, + { "beeper.enable", 0, 0, "UPS.BatterySystem.Battery.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, /* Command for the outlet collection */ @@ -995,7 +1434,7 @@ static hid_info_t mge_hid2nut[] = { "outlet.2.load.on", 0, 0, "UPS.OutletSystem.Outlet.[3].DelayBeforeStartup", NULL, "0", HU_TYPE_CMD, NULL }, /* end of structure. */ - { NULL } + { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; /* @@ -1011,7 +1450,13 @@ static char *get_model_name(const char *iProduct, const char *iModel) /* Search for device type and formatting rules */ for (model = mge_model_names; model->iProduct; model++) { - upsdebugx(2, "comparing with: %s", model->name); + if(model->name) { + upsdebugx(2, "comparing with: %s", model->name); + } + else { + upsdebugx(2, "comparing with: %s %s", model->iProduct, + model->iModel); + } if (strcmp(iProduct, model->iProduct)) { continue; @@ -1031,7 +1476,31 @@ static char *get_model_name(const char *iProduct, const char *iModel) * model name by concatenation of iProduct and iModel */ char buf[SMALLBUF]; - snprintf(buf, sizeof(buf), "%s %s", iProduct, iModel); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic ignored "-Wformat-truncation" +#endif + /* NOTE: We intentionally limit the amount of bytes reported */ + int len = snprintf(buf, sizeof(buf), "%s %s", iProduct, iModel); + + if (len < 0) { + upsdebugx(1, "%s: got an error while extracting iProduct+iModel value", __func__); + } + + /* NOTE: SMALLBUF here comes from mge_format_model() + * buffer definitions below + */ + if ((intmax_t)len > (intmax_t)sizeof(buf) + || (intmax_t)(strnlen(iProduct, SMALLBUF) + strnlen(iModel, SMALLBUF) + 1 + 1) + > (intmax_t)sizeof(buf) + ) { + upsdebugx(1, "%s: extracted iProduct+iModel value was truncated", __func__); + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic pop +#endif return strdup(buf); } @@ -1079,28 +1548,75 @@ static const char *mge_format_serial(HIDDevice_t *hd) { static int mge_claim(HIDDevice_t *hd) { #ifndef SHUT_MODE - int status = is_usb_device_supported(mge_usb_device_table, hd->VendorID, - hd->ProductID); + int status = is_usb_device_supported(mge_usb_device_table, hd); switch (status) { - case POSSIBLY_SUPPORTED: - /* by default, reject, unless the productid option is given */ - if (getval("productid")) { - return 1; - } - possibly_supported("Eaton / MGE", hd); - return 0; + case POSSIBLY_SUPPORTED: - case SUPPORTED: - return 1; + switch (hd->VendorID) + { + case HP_VENDORID: + case DELL_VENDORID: + /* by default, reject, unless the productid option is given */ + if (getval("productid")) { + return 1; + } - case NOT_SUPPORTED: - default: - return 0; + /* + * this vendor makes lots of USB devices that are + * not a UPS, so don't use possibly_supported here + */ + return 0; + + case PHOENIXTEC: + /* The vendorid 0x06da is primarily handled by + * liebert-hid, except for (maybe) AEG PROTECT NAS + * branded devices */ + if (hd->Vendor && strstr(hd->Vendor, "AEG")) { + return 1; + } + if (hd->Product && strstr(hd->Product, "AEG")) { + return 1; + } + + /* Let liebert-hid grab this */ + return 0; + + default: /* Valid for Eaton */ + /* by default, reject, unless the productid option is given */ + if (getval("productid")) { + return 1; + } + possibly_supported("Eaton / MGE", hd); + return 0; + } + + case SUPPORTED: + + switch (hd->VendorID) + { + case PHOENIXTEC: /* see comments above */ + if (hd->Vendor && strstr(hd->Vendor, "AEG")) { + return 1; + } + if (hd->Product && strstr(hd->Product, "AEG")) { + return 1; + } + + /* Let liebert-hid grab this */ + return 0; + } + + return 1; + + case NOT_SUPPORTED: + default: + return 0; } #else - return 1; + NUT_UNUSED_VARIABLE(hd); + return 1; #endif } @@ -1112,4 +1628,5 @@ subdriver_t mge_subdriver = { mge_format_model, mge_format_mfr, mge_format_serial, + fix_report_desc, }; diff --git a/drivers/mge-mib.c b/drivers/mge-mib.c index 8c8c59f..d0a844d 100644 --- a/drivers/mge-mib.c +++ b/drivers/mge-mib.c @@ -1,11 +1,12 @@ /* mge-mib.c - data to monitor MGE UPS SYSTEMS SNMP devices with NUT * - * Copyright (C) 2002-2003 - * Arnaud Quette - * J.W. Hoogervorst + * Copyright (C) + * 2002-2012 Arnaud Quette + * 2002-2003 J.W. Hoogervorst * * Sponsored by MGE UPS SYSTEMS - * and MGE Office Protection Systems + * MGE Office Protection Systems + * Eaton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +27,7 @@ #include "mge-mib.h" -#define MGE_MIB_VERSION "0.4" +#define MGE_MIB_VERSION "0.55" /* TODO: * - MGE PDU MIB and sysOID (".1.3.6.1.4.1.705.2") */ @@ -37,128 +38,252 @@ #define MGE_OID_MODEL_NAME MGE_BASE_OID ".1.1.0" static info_lkp_t mge_lowbatt_info[] = { - { 1, "LB" }, - { 2, "" }, - { 0, "NULL" } + { 1, "LB", NULL, NULL }, + { 2, "", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_onbatt_info[] = { - { 1, "OB" }, - { 2, "OL" }, - { 0, "NULL" } + { 1, "OB", NULL, NULL }, + { 2, "OL", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_bypass_info[] = { - { 1, "BYPASS" }, - { 2, "" }, - { 0, "NULL" } + { 1, "BYPASS", NULL, NULL }, + { 2, "", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_boost_info[] = { - { 1, "BOOST" }, - { 2, "" }, - { 0, "NULL" } + { 1, "BOOST", NULL, NULL }, + { 2, "", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_trim_info[] = { - { 1, "TRIM" }, - { 2, "" }, - { 0, "NULL" } + { 1, "TRIM", NULL, NULL }, + { 2, "", NULL, NULL }, + { 0, NULL, NULL, NULL } }; static info_lkp_t mge_overload_info[] = { - { 1, "OVER" }, - { 2, "" }, - { 0, "NULL" } + { 1, "OVER", NULL, NULL }, + { 2, "", NULL, NULL }, + { 0, NULL, NULL, NULL } }; -#define MGE_NOTHING_VALUE 1 -#define MGE_START_VALUE 2 -#define MGE_STOP_VALUE 3 + +static info_lkp_t mge_replacebatt_info[] = { + { 1, "RB", NULL, NULL }, + { 2, "", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t mge_output_util_off_info[] = { + { 1, "OFF", NULL, NULL }, + { 2, "", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t mge_transfer_reason_info[] = { + { 1, "", NULL, NULL }, + { 2, "input voltage out of range", NULL, NULL }, + { 3, "input frequency out of range", NULL, NULL }, + { 4, "utility off", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t mge_test_result_info[] = { + { 1, "done and passed", NULL, NULL }, + { 2, "done and warning", NULL, NULL }, + { 3, "done and error", NULL, NULL }, + { 4, "aborted", NULL, NULL }, + { 5, "in progress", NULL, NULL }, + { 6, "no test initiated", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t mge_beeper_status_info[] = { + { 1, "disabled", NULL, NULL }, + { 2, "enabled", NULL, NULL }, + { 3, "muted", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t mge_yes_no_info[] = { + { 1, "yes", NULL, NULL }, + { 2, "no", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* FIXME: the below may introduce status redundancy, that needs to be + * addressed by the driver, as for usbhid-ups! */ +static info_lkp_t mge_power_source_info[] = { + { 1, "" /* other */, NULL, NULL }, + { 2, "OFF" /* none */, NULL, NULL }, +#if 0 + { 3, "OL" /* normal */, NULL, NULL }, +#endif + { 4, "BYPASS" /* bypass */, NULL, NULL }, + { 5, "OB" /* battery */, NULL, NULL }, + { 6, "BOOST" /* booster */, NULL, NULL }, + { 7, "TRIM" /* reducer */, NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t mge_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "closed", NULL, NULL }, + { 2, "opened", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* Parameters default values */ +#define DEFAULT_ONDELAY "30" /* Delay between return of utility power */ + /* and powering up of load, in seconds */ + /* CAUTION: ondelay > offdelay */ +#define DEFAULT_OFFDELAY "20" /* Delay before power off, in seconds */ + +#define MGE_NOTHING_VALUE "1" +#define MGE_START_VALUE "2" +#define MGE_STOP_VALUE "3" /* TODO: PowerShare (per plug .1, .2, .3) and deals with delays */ /* Snmp2NUT lookup table */ static snmp_info_t mge_mib[] = { + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* UPS page */ - { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "MGE UPS SYSTEMS", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, - { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, MGE_BASE_OID ".1.1.0", "Generic SNMP UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, MGE_BASE_OID ".1.7.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, MGE_BASE_OID ".12.12.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, MGE_BASE_OID ".5.14.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_lowbatt_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, MGE_BASE_OID ".5.16.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_lowbatt_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, MGE_BASE_OID ".7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_onbatt_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, MGE_BASE_OID ".7.4.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_bypass_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, MGE_BASE_OID ".7.8.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_boost_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, MGE_BASE_OID ".7.10.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_overload_info }, - { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, MGE_BASE_OID ".7.12.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_trim_info }, - { "ups.load", 0, 1, MGE_BASE_OID ".7.2.1.4.1", "", SU_OUTPUT_1, NULL }, - { "ups.L1.load", 0, 1, MGE_BASE_OID ".7.2.1.4.1", "", SU_OUTPUT_3, NULL }, - { "ups.L2.load", 0, 1, MGE_BASE_OID ".7.2.1.4.2", "", SU_OUTPUT_3, NULL }, - { "ups.L3.load", 0, 1, MGE_BASE_OID ".7.2.1.4.3", "", SU_OUTPUT_3, NULL }, -/* { "ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 3, MGE_OID_GRACEDELAY, "", SU_FLAG_OK, NULL }, */ + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Eaton", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.1.1.0", "Generic SNMP UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.1.7.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.1.4.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.12.12.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.1", "", SU_OUTPUT_1, NULL }, + { "ups.beeper.status", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.2.1.33.1.9.8.0", "", 0, mge_beeper_status_info }, + { "ups.L1.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.1", "", SU_OUTPUT_3, NULL }, + { "ups.L2.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.2", "", SU_OUTPUT_3, NULL }, + { "ups.L3.load", 0, 1, ".1.3.6.1.4.1.705.1.7.2.1.4.3", "", SU_OUTPUT_3, NULL }, + { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.33.1.7.3.0", "", 0, mge_test_result_info }, + { "ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW, 6, "1.3.6.1.2.1.33.1.8.2.0", DEFAULT_OFFDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.delay.start", ST_FLAG_STRING | ST_FLAG_RW, 6, "1.3.6.1.2.1.33.1.8.3.0", DEFAULT_ONDELAY, SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.timer.shutdown", 0, 1, "1.3.6.1.2.1.33.1.8.2.0", "", SU_FLAG_OK, NULL }, + { "ups.timer.start", 0, 1, "1.3.6.1.2.1.33.1.8.3.0", "", SU_FLAG_OK, NULL }, + { "ups.timer.reboot", 0, 1, "1.3.6.1.2.1.33.1.8.4.0", "", SU_FLAG_OK, NULL }, + { "ups.start.auto", ST_FLAG_RW, 1, "1.3.6.1.2.1.33.1.8.5.0", "", SU_FLAG_OK, mge_yes_no_info }, + /* status data */ + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.5.11.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_replacebatt_info }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.5.14.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_lowbatt_info }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.5.16.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_lowbatt_info }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_onbatt_info }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.4.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_bypass_info }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.7.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_output_util_off_info }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.8.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_boost_info }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.10.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_overload_info }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.12.0", "", SU_FLAG_OK | SU_STATUS_BATT, mge_trim_info }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.2.1.33.1.4.1.0", "", SU_STATUS_PWR | SU_FLAG_OK, mge_power_source_info }, + + /* FIXME: Alarms + * - upsmgBatteryChargerFault (.1.3.6.1.4.1.705.1.5.15.0), yes (1), no (2) + * => Battery charger fail! + * - upsmgBatteryFaultBattery (.1.3.6.1.4.1.705.1.5.9.0), yes (1), no (2) + * => "Battery fault!" or? + * - upsmgOutputOverTemp (.1.3.6.1.4.1.705.1.7.11.0), yes (1), no (2) + * => "Temperature too high!" + * - upsmgOutputInverterOff (.1.3.6.1.4.1.705.1.7.9.0), yes (1), no (2) + * => "??" + * - upsmgInputBadStatus (.1.3.6.1.4.1.705.1.6.3.0), yes (1), no (2) + * => "bad volt or bad freq" + */ /* Input page */ - { "input.phases", 0, 1.0, MGE_BASE_OID ".6.1.0", "", SU_FLAG_SETINT, NULL, &input_phases }, - { "input.voltage", 0, 0.1, MGE_BASE_OID ".6.2.1.2.1", "", SU_INPUT_1, NULL }, - { "input.L1-N.voltage", 0, 0.1, MGE_BASE_OID ".6.2.1.2.1", "", SU_INPUT_3, NULL }, - { "input.L2-N.voltage", 0, 0.1, MGE_BASE_OID ".6.2.1.2.2", "", SU_INPUT_3, NULL }, - { "input.L3-N.voltage", 0, 0.1, MGE_BASE_OID ".6.2.1.2.3", "", SU_INPUT_3, NULL }, - { "input.frequency", 0, 0.1, MGE_BASE_OID ".6.2.1.3.1", "", SU_INPUT_1, NULL }, - { "input.L1.frequency", 0, 0.1, MGE_BASE_OID ".6.2.1.3.1", "", SU_INPUT_3, NULL }, - { "input.L2.frequency", 0, 0.1, MGE_BASE_OID ".6.2.1.3.2", "", SU_INPUT_3, NULL }, - { "input.L3.frequency", 0, 0.1, MGE_BASE_OID ".6.2.1.3.3", "", SU_INPUT_3, NULL }, - { "input.voltage.minimum", 0, 0.1, MGE_BASE_OID ".6.2.1.4.1", "", SU_INPUT_1, NULL }, - { "input.L1-N.voltage.minimum", 0, 0.1, MGE_BASE_OID ".6.2.1.4.1", "", SU_INPUT_3, NULL }, - { "input.L2-N.voltage.minimum", 0, 0.1, MGE_BASE_OID ".6.2.1.4.2", "", SU_INPUT_3, NULL }, - { "input.L3-N.voltage.minimum", 0, 0.1, MGE_BASE_OID ".6.2.1.4.3", "", SU_INPUT_3, NULL }, - { "input.voltage.maximum", 0, 0.1, MGE_BASE_OID ".6.2.1.5.1", "", SU_INPUT_1, NULL }, - { "input.L1-N.voltage.maximum", 0, 0.1, MGE_BASE_OID ".6.2.1.5.1", "", SU_INPUT_3, NULL }, - { "input.L2-N.voltage.maximum", 0, 0.1, MGE_BASE_OID ".6.2.1.5.2", "", SU_INPUT_3, NULL }, - { "input.L3-N.voltage.maximum", 0, 0.1, MGE_BASE_OID ".6.2.1.5.3", "", SU_INPUT_3, NULL }, - { "input.current", 0, 0.1, MGE_BASE_OID ".6.2.1.6.1", "", SU_INPUT_1, NULL }, - { "input.L1.current", 0, 0.1, MGE_BASE_OID ".6.2.1.6.1", "", SU_INPUT_3, NULL }, - { "input.L2.current", 0, 0.1, MGE_BASE_OID ".6.2.1.6.2", "", SU_INPUT_3, NULL }, - { "input.L3.current", 0, 0.1, MGE_BASE_OID ".6.2.1.6.3", "", SU_INPUT_3, NULL }, + { "input.phases", 0, 1.0, ".1.3.6.1.4.1.705.1.6.1.0", "", 0, NULL }, + { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, + { "input.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_3, NULL }, + { "input.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.2", "", SU_INPUT_3, NULL }, + { "input.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.3", "", SU_INPUT_3, NULL }, + { "input.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.1", "", SU_INPUT_1, NULL }, + { "input.L1.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.1", "", SU_INPUT_3, NULL }, + { "input.L2.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.2", "", SU_INPUT_3, NULL }, + { "input.L3.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.3.3", "", SU_INPUT_3, NULL }, + { "input.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.1", "", SU_INPUT_1, NULL }, + { "input.L1-N.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.1", "", SU_INPUT_3, NULL }, + { "input.L2-N.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.2", "", SU_INPUT_3, NULL }, + { "input.L3-N.voltage.minimum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.4.3", "", SU_INPUT_3, NULL }, + { "input.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.1", "", SU_INPUT_1, NULL }, + { "input.L1-N.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.1", "", SU_INPUT_3, NULL }, + { "input.L2-N.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.2", "", SU_INPUT_3, NULL }, + { "input.L3-N.voltage.maximum", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.5.3", "", SU_INPUT_3, NULL }, + { "input.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.1", "", SU_INPUT_1, NULL }, + { "input.L1.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.1", "", SU_INPUT_3, NULL }, + { "input.L2.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.2", "", SU_INPUT_3, NULL }, + { "input.L3.current", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.6.3", "", SU_INPUT_3, NULL }, + { "input.transfer.reason", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.6.4.0", "", SU_FLAG_OK, mge_transfer_reason_info }, /* Output page */ - { "output.phases", 0, 1.0, MGE_BASE_OID ".7.1.0", "", SU_FLAG_SETINT, NULL, &output_phases }, - { "output.voltage", 0, 0.1, MGE_BASE_OID ".7.2.1.2.1", "", SU_OUTPUT_1, NULL }, - { "output.L1-N.voltage", 0, 0.1, MGE_BASE_OID ".7.2.1.2.1", "", SU_OUTPUT_3, NULL }, - { "output.L2-N.voltage", 0, 0.1, MGE_BASE_OID ".7.2.1.2.2", "", SU_OUTPUT_3, NULL }, - { "output.L3-N.voltage", 0, 0.1, MGE_BASE_OID ".7.2.1.2.3", "", SU_OUTPUT_3, NULL }, - { "output.frequency", 0, 0.1, MGE_BASE_OID ".7.2.1.3.1", "", SU_OUTPUT_1, NULL }, - { "output.L1.frequency", 0, 0.1, MGE_BASE_OID ".7.2.1.3.1", "", SU_OUTPUT_3, NULL }, - { "output.L2.frequency", 0, 0.1, MGE_BASE_OID ".7.2.1.3.2", "", SU_OUTPUT_3, NULL }, - { "output.L3.frequency", 0, 0.1, MGE_BASE_OID ".7.2.1.3.3", "", SU_OUTPUT_3, NULL }, - { "output.current", 0, 0.1, MGE_BASE_OID ".7.2.1.5.1", "", SU_OUTPUT_1, NULL }, - { "output.L1.current", 0, 0.1, MGE_BASE_OID ".7.2.1.5.1", "", SU_OUTPUT_3, NULL }, - { "output.L2.current", 0, 0.1, MGE_BASE_OID ".7.2.1.5.2", "", SU_OUTPUT_3, NULL }, - { "output.L3.current", 0, 0.1, MGE_BASE_OID ".7.2.1.5.3", "", SU_OUTPUT_3, NULL }, + { "output.phases", 0, 1.0, ".1.3.6.1.4.1.705.1.7.1.0", "", 0, NULL }, + { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.1", "", SU_OUTPUT_1, NULL }, + { "output.L1-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.1", "", SU_OUTPUT_3, NULL }, + { "output.L2-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.2", "", SU_OUTPUT_3, NULL }, + { "output.L3-N.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.2.3", "", SU_OUTPUT_3, NULL }, + { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.1", "", SU_OUTPUT_1, NULL }, + { "output.L1.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.1", "", SU_OUTPUT_3, NULL }, + { "output.L2.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.2", "", SU_OUTPUT_3, NULL }, + { "output.L3.frequency", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.3.3", "", SU_OUTPUT_3, NULL }, + { "output.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.1", "", SU_OUTPUT_1, NULL }, + { "output.L1.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.1", "", SU_OUTPUT_3, NULL }, + { "output.L2.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.2", "", SU_OUTPUT_3, NULL }, + { "output.L3.current", 0, 0.1, ".1.3.6.1.4.1.705.1.7.2.1.5.3", "", SU_OUTPUT_3, NULL }, /* Battery page */ - { "battery.charge", 0, 1, MGE_BASE_OID ".5.2.0", "", SU_FLAG_OK, NULL }, - { "battery.runtime", 0, 1, MGE_BASE_OID ".5.1.0", "", SU_FLAG_OK, NULL }, - { "battery.charge.low", ST_FLAG_STRING | ST_FLAG_RW, 2, MGE_BASE_OID ".4.8.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, - { "battery.voltage", 0, 0.1, MGE_BASE_OID ".5.5.0", "", SU_FLAG_OK, NULL }, + { "battery.charge", 0, 1, ".1.3.6.1.4.1.705.1.5.2.0", "", SU_FLAG_OK, NULL }, + { "battery.runtime", 0, 1, ".1.3.6.1.4.1.705.1.5.1.0", "", SU_FLAG_OK, NULL }, + { "battery.runtime.low", 0, 1, ".1.3.6.1.4.1.705.1.4.7.0", "", SU_FLAG_OK, NULL }, + { "battery.charge.low", ST_FLAG_STRING | ST_FLAG_RW, 2, ".1.3.6.1.4.1.705.1.4.8.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, + { "battery.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.5.5.0", "", SU_FLAG_OK, NULL }, /* Ambient page: Environment Sensor (ref 66 846) */ - { "ambient.temperature", 0, 0.1, MGE_BASE_OID ".8.1.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, - { "ambient.humidity", 0, 0.1, MGE_BASE_OID ".8.2.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, + { "ambient.temperature", 0, 0.1, ".1.3.6.1.4.1.705.1.8.1.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, + { "ambient.humidity", 0, 0.1, ".1.3.6.1.4.1.705.1.8.2.0", "", SU_TYPE_INT | SU_FLAG_OK, NULL }, + /* upsmgEnvironmentInput1State.1 */ + { "ambient.contacts.1.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.8.7.1.9.1", "", SU_TYPE_INT | SU_FLAG_OK, mge_ambient_drycontacts_info }, + /* upsmgEnvironmentInput1State.1 */ + { "ambient.contacts.2.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.8.7.1.10.1", "", SU_TYPE_INT | SU_FLAG_OK, mge_ambient_drycontacts_info }, /* Outlet page */ { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "Main Outlet", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, /* instant commands. */ - { "test.battery.start", 0, MGE_START_VALUE, MGE_BASE_OID ".10.4.0", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, -/* { "load.off", 0, MGE_START_VALUE, MGE_BASE_OID ".9.1.1.6.1", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, */ -/* { "load.on", 0, MGE_START_VALUE, MGE_BASE_OID ".9.1.1.3.1", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, */ -/* { "shutdown.return", 0, MGE_START_VALUE, MGE_BASE_OID ".9.1.1.9.1", "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, */ + { "test.battery.start", 0, 1, ".1.3.6.1.4.1.705.1.10.4.0", MGE_START_VALUE, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* Also use IETF OIDs + * { "test.battery.stop", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.2", SU_TYPE_CMD, NULL }, + * { "test.battery.start", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.3", SU_TYPE_CMD, NULL }, + * { "test.battery.start.quick", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.4", SU_TYPE_CMD, NULL }, + * { "test.battery.start.deep", 0, 0, "1.3.6.1.2.1.33.1.7.1.0", "1.3.6.1.2.1.33.1.7.7.5", SU_TYPE_CMD, NULL }, + */ + + /* { "load.off", 0, 1, ".1.3.6.1.4.1.705.1.9.1.1.6.1", MGE_START_VALUE, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + * { "load.on", 0, 1, ".1.3.6.1.4.1.705.1.9.1.1.3.1", MGE_START_VALUE, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + * { "shutdown.return", 0, 1, ".1.3.6.1.4.1.705.1.9.1.1.9.1", MGE_START_VALUE, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + */ + /* IETF MIB fallback */ + { "beeper.disable", 0, 1, "1.3.6.1.2.1.33.1.9.8.0", "1", SU_TYPE_CMD, NULL }, + { "beeper.enable", 0, 1, "1.3.6.1.2.1.33.1.9.8.0", "2", SU_TYPE_CMD, NULL }, + { "beeper.mute", 0, 1, "1.3.6.1.2.1.33.1.9.8.0", "3", SU_TYPE_CMD, NULL }, + /*{ "load.off", 0, 1, "1.3.6.1.2.1.33.1.8.2.0", DEFAULT_OFFDELAY, SU_TYPE_CMD, NULL }, + { "load.on", 0, 1, "1.3.6.1.2.1.33.1.8.3.0", DEFAULT_ONDELAY, SU_TYPE_CMD, NULL },*/ + { "load.off.delay", 0, 1, "1.3.6.1.2.1.33.1.8.2.0", NULL, SU_TYPE_CMD, NULL }, + { "load.on.delay", 0, 1, "1.3.6.1.2.1.33.1.8.3.0", NULL, SU_TYPE_CMD, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } }; -mib2nut_info_t mge = { "mge", MGE_MIB_VERSION, "", MGE_OID_MODEL_NAME, mge_mib, MGE_SYSOID }; +mib2nut_info_t mge = { "mge", MGE_MIB_VERSION, NULL, MGE_OID_MODEL_NAME, mge_mib, MGE_SYSOID, NULL }; diff --git a/drivers/mge-shut.c b/drivers/mge-shut.c deleted file mode 100644 index 1f9e519..0000000 --- a/drivers/mge-shut.c +++ /dev/null @@ -1,1452 +0,0 @@ -/* mge-shut.c - monitor MGE UPS for NUT with SHUT protocol - * - * Copyright (C) 2002 - 2008 - * Arnaud Quette - * - * Sponsored by MGE UPS SYSTEMS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include - -#include "config.h" -#include "main.h" -#include "serial.h" -#include "timehead.h" -#include "mge-shut.h" -#include "hidparser.h" -#include "hidtypes.h" -#include "common.h" /* for upsdebugx() etc */ - -/* --------------------------------------------------------------- */ -/* Define "technical" constants */ -/* --------------------------------------------------------------- */ - -#define DRIVER_NAME "Eaton / SHUT driver" -#define DRIVER_VERSION "0.69" - -/* driver description structure */ -upsdrv_info_t upsdrv_info = { - DRIVER_NAME, - DRIVER_VERSION, - "Arnaud Quette ", - DRV_STABLE, - { NULL } -}; - -#define MAX_TRY 4 - -/* global variables */ -int commstatus = 0; -int lowbatt = -1; -int ondelay = DEFAULT_ONDELAY; -int offdelay = DEFAULT_OFFDELAY; -int notification = DEFAULT_NOTIFICATION; - -#define SD_RETURN 0 -#define SD_STAYOFF 1 - -int sdtype = SD_RETURN; - -#define BYTESWAP(in) (((in & 0xFF) << 8) + ((in & 0xFF00) >> 8)) - -/* realign packet data according to Endianess */ -static void align_request(hid_packet_t *sd) -{ -#if WORDS_BIGENDIAN - /* Sparc/Mips/... are big endian, USB/SHUT little endian */ - (*sd).wValue = BYTESWAP((*sd).wValue); - (*sd).wIndex = BYTESWAP((*sd).wIndex); - (*sd).wLength = BYTESWAP((*sd).wLength); -#endif -} - -/* --------------------------------------------------------------- */ -/* Global structures */ -/* --------------------------------------------------------------- */ - -hid_desc_data_t hid_descriptor; -device_desc_data_t device_descriptor; -static long hValue; -static HIDDesc_t *pDesc = NULL; /* parsed Report Descriptor */ -u_char raw_buf[4096]; - -/* --------------------------------------------------------------- */ -/* Function prototypes */ -/* --------------------------------------------------------------- */ - -float expo(int a, int b); -extern long FormatValue(long Value, u_char Size); -static const char *hu_find_infoval(info_lkp_t *hid2info, long value); - -/* --------------------------------------------------------------- */ -/* UPS Driver Functions */ -/* --------------------------------------------------------------- */ - -void upsdrv_initinfo (void) -{ - mge_info_item_t *item; - - upsdebugx(2, "entering initinfo()\n"); - - /* Get complete Model information */ - shut_identify_ups (); - - printf("Detected %s [%s] on %s\n", dstate_getinfo("ups.model"), - dstate_getinfo("ups.serial"), device_path); - - /* Device capabilities enumeration ----------------------------- */ - for ( item = mge_info ; item->type != NULL ; item++ ) { - - /* Check if we are asked to stop (reactivity++) */ - if (exit_flag != 0) - return; - - /* avoid redundancy when multiple defines (RO/RW) */ - if (dstate_getinfo(item->type) != NULL) - continue; - - /* Special case for handling server side variables */ - if (item->shut_flags & SHUT_FLAG_ABSENT) { - /* Check if exists (if necessary) before creation */ - if (item->item_path != NULL) - { - if (hid_get_value(item->item_path) != 1 ) - continue; - } - else - { - /* Simply set the default value */ - dstate_setinfo(item->type, "%s", item->dfl); - dstate_setflags(item->type, item->flags); - continue; - } - - dstate_setinfo(item->type, "%s", item->dfl); - dstate_setflags(item->type, item->flags); - - /* Set max length for strings, if needed */ - if (item->flags & ST_FLAG_STRING) - dstate_setaux(item->type, item->length); - - /* disable reading now - item->shut_flags &= ~SHUT_FLAG_OK;*/ - } else { - if (hid_get_value(item->item_path) != 0 ) { - - item->shut_flags &= SHUT_FLAG_OK; - dstate_setinfo(item->type, item->fmt, hValue); - dstate_setflags(item->type, item->flags); - /* Set max length for strings */ - if (item->flags & ST_FLAG_STRING) - dstate_setaux(item->type, item->length); - } - else { - item->shut_flags &= ~SHUT_FLAG_OK; - } - } - } - - /* commands ----------------------------------------------- */ - dstate_addcmd("load.off"); - dstate_addcmd("load.on"); - dstate_addcmd("shutdown.return"); - dstate_addcmd("shutdown.stayoff"); - dstate_addcmd("test.battery.start"); - dstate_addcmd("test.battery.stop"); - - /* install handlers */ - upsh.setvar = hid_set_value; /* setvar; */ - upsh.instcmd = instcmd; - - /* check if low battery level has been given to set it */ - if (lowbatt != -1) { - hid_set_value("battery.charge.low", getval ("lowbatt")); - } -} - -/* --------------------------------------------------------------- */ - -void upsdrv_updateinfo (void) -{ - mge_info_item_t *item; - const char *nutvalue; - - upsdebugx(2, "entering upsdrv_updateinfo()"); - - if (commstatus == 0) { - if (shut_ups_start () != 0) { - upsdebugx(2, "No communication with UPS, retrying"); - dstate_datastale(); - return; - } else { - upsdebugx(2, "Communication with UPS established"); - } - } - - shut_ups_status(); - - /* Device data walk ----------------------------- */ - for ( item = mge_info ; item->type != NULL; item++ ) { - - /* Check if we are asked to stop (reactivity++) */ - if (exit_flag != 0) - return; - - if (item->shut_flags & SHUT_FLAG_ABSENT) - continue; - - if (item->shut_flags & SHUT_FLAG_OK) { - - if(hid_get_value(item->item_path) != 0 ) { - upsdebugx(3, "%s: hValue = %ld", item->item_path, hValue); - /* upsdebugx(3, "%s: hValue = %ld (%ld)", - item->item_path, hValue, hData.LogMax); */ - - /* need lookup'ed translation */ - if (item->hid2info != NULL) - { - nutvalue = hu_find_infoval(item->hid2info, (long)hValue); - if (nutvalue != NULL) - dstate_setinfo(item->type, "%s", nutvalue); - else - dstate_setinfo(item->type, item->fmt, hValue); - } - else - dstate_setinfo(item->type, item->fmt, hValue); - - dstate_dataok(); - } else { - if (shut_ups_start () != 0) - dstate_datastale(); - } - } - } -} - -/* --------------------------------------------------------------- */ - -void upsdrv_shutdown (void) -{ - char val[5]; - - if (sdtype == SD_RETURN) { - /* set DelayBeforeStartup */ - snprintf(val, sizeof(val), "%d", ondelay); - hid_set_value("ups.timer.start", val); - } - - /* set DelayBeforeShutdown */ - snprintf(val, sizeof(val), "%d", offdelay); - hid_set_value("ups.timer.shutdown", val); -} - -/* --------------------------------------------------------------- */ - -void upsdrv_help (void) -{ - upsdebugx(2, "entering upsdrv_help"); -} - -/* --------------------------------------------------------------- */ - -/* list flags and values that you want to receive via -x */ -void upsdrv_makevartable (void) -{ - char msg[MAX_STRING]; - - upsdebugx (2, "entering upsdrv_makevartable()"); - - snprintf(msg, sizeof(msg), "Set low battery level, in %% (default=%d).", - DEFAULT_LOWBATT); - addvar (VAR_VALUE, "lowbatt", msg); - - snprintf(msg, sizeof(msg), "Set shutdown delay, in seconds (default=%d).", - DEFAULT_OFFDELAY); - addvar (VAR_VALUE, "offdelay", msg); - - snprintf(msg, sizeof(msg), "Set startup delay, in ten seconds units (default=%d).", - DEFAULT_ONDELAY); - addvar (VAR_VALUE, "ondelay", msg); - - snprintf(msg, sizeof(msg), "Set notification type, 1 = no, 2 = light, 3 = yes (default=%d).", - DEFAULT_NOTIFICATION); - addvar (VAR_VALUE, "notification", msg); -} - -/* --------------------------------------------------------------- */ - -void upsdrv_initups (void) -{ - upsdebugx(2, "entering upsdrv_initups()"); - - /* initialize serial port */ - upsfd = ser_open(device_path); - ser_set_speed(upsfd, device_path, B2400); - setline (1); - - /* get battery lowlevel */ - if (getval ("lowbatt")) - lowbatt = atoi (getval ("lowbatt")); - - /* on delay */ - if (getval ("ondelay")) - ondelay = atoi (getval ("ondelay")); - - /* shutdown delay */ - if (getval ("offdelay")) - offdelay = atoi (getval ("offdelay")); - - /* notification type */ - if (getval ("notification")) - notification = atoi (getval ("notification")); - - /* initialise communication */ - if (shut_ups_start () != 0) - fatalx(EXIT_FAILURE, "No communication with UPS"); - else - upsdebugx(2, "Communication with UPS established"); - - /* initialise HID communication */ - if(hid_init_device() != 0) - fatalx(EXIT_FAILURE, "Can't initialise HID device"); -} - -/* --------------------------------------------------------------- */ - -void upsdrv_cleanup(void) -{ - ser_close(upsfd, device_path); -} - -/* --------------------------------------------------------------- */ - -int instcmd(const char *cmdname, const char *extra) -{ - /* Shutdown UPS and return when power is restored */ - if (!strcasecmp(cmdname, "shutdown.return")) { - sdtype = SD_RETURN; - upsdrv_shutdown(); - return STAT_INSTCMD_HANDLED; - } - - /* Shutdown UPS and stay off when power is restored */ - if (!strcasecmp(cmdname, "shutdown.stayoff")) { - sdtype = SD_STAYOFF; - upsdrv_shutdown(); - return STAT_INSTCMD_HANDLED; - } - - /* Power off the load immediatly */ - if (!strcasecmp(cmdname, "load.off")) { - /* set DelayBeforeShutdown to 0 */ - hid_set_value("ups.timer.shutdown", "0"); - return STAT_INSTCMD_HANDLED; - } - - /* Power on the load immediatly */ - if (!strcasecmp(cmdname, "load.on")) { - /* set DelayBeforeStartup to 0 */ - hid_set_value("ups.timer.start", "0"); - return STAT_INSTCMD_HANDLED; - } - - /* Start battery test */ - if (!strcasecmp(cmdname, "test.battery.start")) { - /* set Test to 1 (Quick test) */ - hid_set_value("ups.test.result", "1"); - return STAT_INSTCMD_HANDLED; - } - - /* Stop battery test */ - if (!strcasecmp(cmdname, "test.battery.stop")) { - /* set Test to 3 (Abort test) */ - hid_set_value("ups.test.result", "3"); - return STAT_INSTCMD_HANDLED; - } - - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); - return STAT_INSTCMD_UNKNOWN; -} - - -/***************************************************************************** - * shut_ups_start () - * - * initiate communication with the UPS - * - * return 0 on success, -1 on failure - * - *****************************************************************************/ -int shut_ups_start () -{ - u_char c = SHUT_SYNC, r[1]; - int try; - - upsdebugx (2, "entering shut_ups_start()\n"); - r[0] = '\0'; - - switch (notification) { - case OFF_NOTIFICATION: - c = SHUT_SYNC_OFF; - break; - case LIGHT_NOTIFICATION: - c = SHUT_SYNC_LIGHT; - break; - default: - case COMPLETE_NOTIFICATION: - c = SHUT_SYNC; - break; - } - - /* Sync with the UPS using Complete, Off or light notification */ - for (try = 0; try < MAX_TRY; try++) { - if ((shut_token_send(c)) == -1) { - upsdebugx (3, "Communication error while writing to port"); - return -1; - } - serial_read (1000, &r[0]); - if (r[0] == c) { - commstatus = 1; - upsdebugx (3, "Syncing and notification setting done"); - return 0; - } - } - commstatus = 0; - return -1; -} - -/********************************************************************** - * shut_identify_ups () - * - * Get SHUT device complete name - * - * return 0 on success, -1 on failure - * - *********************************************************************/ -int shut_identify_ups () -{ - char string[MAX_STRING]; - char model[MAX_STRING]; - const char *finalname = NULL; - int retcode, tries=MAX_TRY; - - if (commstatus == 0) - return -1; - - upsdebugx (2, "entering shut_identify_ups(0x%04x, 0x%04x)\n", - device_descriptor.dev_desc.iManufacturer, - device_descriptor.dev_desc.iProduct); - - /* Get strings iModel and iProduct */ - while (tries > 0) - { - if (shut_get_string(device_descriptor.dev_desc.iProduct, string, 0x25) > 0) - { - strcpy(model, string); - - if(hid_get_value("UPS.PowerSummary.iModel") != 0 ) - { - if((shut_get_string(hValue, string, 0x25)) > 0) - { - finalname = get_model_name(model, string); - upsdebugx (2, "iModel = %s", string); - tries = 0; - } - } - else - { - /* Try with "UPS.Flow.[4].ConfigApparentPower" */ - if(hid_get_value("UPS.Flow.[4].ConfigApparentPower") != 0 ) - { - snprintf(string, sizeof(string), "%i", (int)hValue); - finalname = get_model_name(model, string); - } - else - finalname = get_model_name(model, NULL); - - tries = 0; - } - - dstate_setinfo("ups.model", "%s", finalname); - } - else - tries--; - } - - /* Get strings iSerialNumber */ - if (((retcode = shut_get_string(device_descriptor.dev_desc.iSerialNumber, string, 0x25)) > 0) - && strcmp(string, "") && string[0] != '\t') { - - dstate_setinfo("ups.serial", "%s", string); - } - else - dstate_setinfo("ups.serial", "unknown"); - - /* all went fine */ - return 1; -} - -/********************************************************************** - * shut_wait_ack() - * - * wait for an ACK packet - * - * returns 0 on success, -1 on error, -2 on NACK, -3 on NOTIFICATION - * - *********************************************************************/ -int shut_wait_ack (void) -{ - u_char c[1]; - - c[0] = '\0'; - - serial_read (DEFAULT_TIMEOUT, &c[0]); - if (c[0] == SHUT_OK) { - upsdebugx (2, "shut_wait_ack(): ACK received"); - return 0; - } - else if (c[0] == SHUT_NOK) { - upsdebugx (2, "shut_wait_ack(): NACK received"); - return -2; - } - else if ((c[0] & SHUT_PKT_LAST) == SHUT_TYPE_NOTIFY) { - upsdebugx (2, "shut_wait_ack(): NOTIFY received"); - return -3; - } - upsdebugx (2, "shut_wait_ack(): Nothing received"); - return -1; -} - -/********************************************************************** - * char_read (char *bytes, int size, int read_timeout) - * - * reads size bytes from the serial port - * - * bytes - buffer to store the data - * size - size of the data to get - * read_timeout - serial timeout (in milliseconds) - * - * return -1 on error, -2 on timeout, nb_bytes_readen on success - * - *********************************************************************/ -static int char_read (char *bytes, int size, int read_timeout) -{ - struct timeval serial_timeout; - fd_set readfs; - int readen = 0; - int rc = 0; - - FD_ZERO (&readfs); - FD_SET (upsfd, &readfs); - - serial_timeout.tv_usec = (read_timeout % 1000) * 1000; - serial_timeout.tv_sec = (read_timeout / 1000); - - rc = select (upsfd + 1, &readfs, NULL, NULL, &serial_timeout); - if (0 == rc) - return -2; /* timeout */ - - if (FD_ISSET (upsfd, &readfs)) { - int now = read (upsfd, bytes, size - readen); - - if (now < 0) { - return -1; - } - else { - bytes += now; - readen += now; - } - } - else { - return -1; - } - return readen; -} - -/********************************************************************** - * serial_read (int read_timeout) - * - * return data one byte at a time - * - * read_timeout - serial timeout (in milliseconds) - * - * returns 0 on success, -1 on error, -2 on timeout - * - **********************************************************************/ -int serial_read (int read_timeout, u_char *readbuf) -{ - static u_char cache[512]; - static u_char *cachep = cache; - static u_char *cachee = cache; - int recv; - *readbuf = '\0'; - - /* if still data in cache, get it */ - if (cachep < cachee) { - *readbuf = *cachep++; - return 0; - /* return (int) *cachep++; */ - } - recv = char_read ((char *)cache, 1, read_timeout); - - if ((recv == -1) || (recv == -2)) - return recv; - - cachep = cache; - cachee = cache + recv; - cachep = cache; - cachee = cache + recv; - - if (recv) { - upsdebugx(5,"received: %02x", *cachep); - *readbuf = *cachep++; - return 0; - } - return -1; -} - -/********************************************************************** - * serial_send (char *buf, int len) - * - * write the content of buf to the serial port - * - * buf - data to send - * len - lenght of data to send - * - * returns number of bytes written on success, -1 on error - * - **********************************************************************/ -int serial_send (u_char *buf, int len) -{ - tcflush (upsfd, TCIFLUSH); - upsdebug_hex (3, "sent", (u_char *)buf, len); - return write (upsfd, buf, len); -} - -/* - * Serial HID UPS Transfer (SHUT) functions - *********************************************************************/ -/* Get and parse UPS status */ -void shut_ups_status(void) -{ - int try = 0, retcode = 0; - - /* clear status buffer before begining */ - status_init(); - - /* Ensure to have at least basic status */ - while (try < MAX_TRY) { - if((retcode = hid_get_value("UPS.PowerSummary.PresentStatus.ACPresent")) != 0 ) { - try = MAX_TRY; - if(hValue == 1){ - status_set("OL"); - } else { - status_set("OB"); - } - } else { /* retry to get data */ - try++; - } - } - - if(hid_get_value("UPS.PowerSummary.PresentStatus.Discharging") != 0 ) { - if(hValue == 1) - status_set("DISCHRG"); - } - - if(hid_get_value("UPS.PowerSummary.PresentStatus.Charging") != 0 ) { - if(hValue == 1) - status_set("CHRG"); - } - - if(hid_get_value("UPS.PowerSummary.PresentStatus.ShutdownImminent") != 0 ) { - if(hValue == 1) - status_set("LB"); - } - - if(hid_get_value("UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit") != 0 ) { - if(hValue == 1) - status_set("LB"); - } - - if(hid_get_value("UPS.PowerSummary.PresentStatus.Overload") != 0 ) { - if(hValue == 1) - status_set("OVER"); - } - - if(hid_get_value("UPS.PowerSummary.PresentStatus.NeedReplacement") != 0 ) { - if(hValue == 1) - status_set("RB"); - } - - if(hid_get_value("UPS.PowerSummary.PresentStatus.Good") != 0 ) { - if(hValue == 0) - status_set("OFF"); - } - - /* FIXME: extend ups.status for BYPASS: */ - /* Manual bypass */ - if(hid_get_value("UPS.PowerConverter.Input.[4].PresentStatus.Used") != 0 ) { - if(hValue == 1) - status_set("BYPASS"); - } - /* Automatic bypass */ - if(hid_get_value("UPS.PowerConverter.Input.[2].PresentStatus.Used") != 0 ) { - if(hValue == 1) - status_set("BYPASS"); - } - - status_commit(); -} - -/* Calculate the SHUT checksum for the packet "buf" */ -u_char shut_checksum(const u_char *buf, int bufsize) -{ - int i; - u_char chk=0; - - for(i=0; i0 && Retry>0) - { - Size=(datalen>=8) ? 8 : datalen; - - /* Packets need only to be sent once - * NACK handling should take care of the rest */ - if (Retry == 1) { - - /* Forge SHUT Frame */ - SHUTRequest.bType = SHUT_TYPE_REQUEST + token; - SHUTRequest.bLength = (Size<<4) + Size; - SHUTRequest.data = *hdata; - /* memcpy(&SHUTRequest.data.raw_pkt, hdata->raw_pkt, Size); */ - - sdata.shut_pkt = SHUTRequest; - sdata.raw_pkt[(Size+3) - 1] = shut_checksum(sdata.shut_pkt.data.raw_pkt, Size); - - upsdebugx (4, "shut_checksum = %2x", sdata.raw_pkt[(Size+3)-1]); - - serial_send (sdata.raw_pkt, Size+3); - } - i = shut_wait_ack (); - if (i == 0) { - datalen-=Size; - Retry=5; - - upsdebugx (4, "received ACK"); - break; - } else if ((i == -1) || (i == -3)) { - /* retry a finite number of times if something wrong happened while - * sending like a notification or a NACK */ - if (Retry >= MAX_TRY) { - upsdebugx(2, "Max tries reached while waiting for ACK, still getting errors"); - return i; - } else { - upsdebugx(4, "Retry = %i", Retry); - /* Send a NACK to get a resend from the UPS */ - shut_token_send(SHUT_NOK); - Retry++; - } - } - } - return (datalen==0); -} - -int shut_packet_recv (u_char *Buf, int datalen) -{ - u_char Start[2]; - u_char Frame[8]; - u_char Chk[1]; - u_short Size=8; - u_short Pos=0; - u_char Retry=0; - int recv; - - upsdebugx (4, "entering shut_packet_recv (%i)", datalen); - - while(datalen>0 && Retry<3) - { - if(serial_read (DEFAULT_TIMEOUT, &Start[0]) >= 0) - { - if(Start[0]==SHUT_SYNC) - { - upsdebugx (4, "received SYNC token"); - memcpy(Buf, Start, 1); - return 1; - } - else - { - /* if(((Start[1] = serial_read (DEFAULT_TIMEOUT)) >= 0) && */ - if((serial_read (DEFAULT_TIMEOUT, &Start[1]) >= 0) && - ((Start[1]>>4)==(Start[1]&0x0F))) - { - upsdebug_hex(3, "Receive", Start, 2); - Size=Start[1]&0x0F; - for(recv=0;recv OK", Chk[0]); - memcpy(Buf, Frame, Size); - datalen-=Size; - Buf+=Size; - Pos+=Size; - Retry=0; - - shut_token_send(SHUT_OK); - - if(Start[0]&SHUT_PKT_LAST) { - if ((Start[0]&SHUT_PKT_LAST) == SHUT_TYPE_NOTIFY) { - /* TODO: process notification (dropped for now) */ - upsdebugx (4, "=> notification"); - datalen+=Pos; - Pos=0; - } - else - return Pos; - } - else - upsdebugx (4, "need more data (%i)!", datalen); - } - else - { - upsdebugx (4, "shut_checksum: %02x => NOK", Chk[0]); - shut_token_send(SHUT_NOK); - Retry++; - } - } - else - return 0; - } - } - else - Retry++; - } /* while */ - return 0; -} - -/* - * Human Interface Device (HID) functions - *********************************************************************/ - -/********************************************************************** - * shut_get_descriptor(int desctype, u_char *pkt) - * - * get descriptor specified by DescType and return it in Buf - * - * desctype - from shutdataType - * pkt - where to store the report received - * - * return 0 on success, -1 on failure, -2 on NACK - * - *********************************************************************/ -int shut_get_descriptor(int desctype, u_char *pkt, int reportlen) -{ - hid_packet_t HIDRequest; - hid_data_t data; - int retcode; - - upsdebugx (2, "entering shut_get_descriptor(n %02x, %i)", - desctype, reportlen); - - HIDRequest.bmRequestType = REQUEST_TYPE_USB+(desctype>=HID_DESCRIPTOR?1:0); - HIDRequest.bRequest = 0x06; - HIDRequest.wValue = (desctype<<8); - HIDRequest.wIndex = 0x0000; - HIDRequest.wLength = reportlen; - - align_request(&HIDRequest); - - data.hid_pkt = HIDRequest; - -/* if((retcode = shut_packet_send (&data, sizeof(data), SHUT_PKT_LAST)) > 0) */ - if((retcode = shut_packet_send (&data, 8, SHUT_PKT_LAST)) > 0) - { - if((retcode = shut_packet_recv (pkt, reportlen)) > 0) - { - upsdebug_hex(3, "shut_get_descriptor", pkt, retcode); - return retcode; - } - else - return retcode; - } - return retcode; -} - -/********************************************************************** - * shut_get_string(int index, u_char *pkt, int reportlen) - * - * get descriptor specified by DescType and return it in Buf - * - * index - from shutdataType - * string - where to store the string received - * strlen - length of string - * - * return string size on success, -1 on failure, -2 on NACK - * - *********************************************************************/ -int shut_get_string(int strindex, char *string, int stringlen) -{ - hid_packet_t HIDRequest; - hid_data_t data; - int retcode; - u_char buf[MAX_STRING]; - - upsdebugx (2, "entering shut_get_string(%02x)", strindex); - - HIDRequest.bmRequestType = REQUEST_TYPE_USB; - HIDRequest.bRequest = 0x06; - HIDRequest.wValue = strindex+(STRING_DESCRIPTOR<<8); - HIDRequest.wIndex = 0x0000; - HIDRequest.wLength = (stringlen<<8); /* (reportlen&0xFF)&(reportlen>>8); */ - - align_request(&HIDRequest); - - data.hid_pkt = HIDRequest; - - if((retcode = shut_packet_send (&data, 8, SHUT_PKT_LAST)) >0) - { - upsdebug_hex(3, "shut_get_string", data.raw_pkt, 8); - if((retcode = shut_packet_recv (buf, stringlen)) > 0) - { - upsdebug_hex(3, "shut_get_string", buf, retcode); - make_string(buf, retcode, string); - upsdebugx(2, "string: %s", string); - return strlen(string); - } - else - return retcode; - } - return 0; -} - -/********************************************************************** - * shut_get_report(int id, u_char *pkt, int reportlen) - * - * get report specified by id and return it in pkt - * - * id - from shutdataType - * pkt - where to store the string received - * strlen - length of string - * - * return report size on success, -1 on failure, -2 on NACK - * - *********************************************************************/ -int shut_get_report(int id, u_char *pkt, int reportlen) -{ - hid_packet_t HIDRequest; - hid_data_t data; - int retcode; - - upsdebugx (2, "entering shut_get_report(id: %02x, len: %02x)", id, reportlen); - - HIDRequest.bmRequestType = REQUEST_TYPE_GET_REPORT; - HIDRequest.bRequest = 0x01; - HIDRequest.wValue = id+(HID_REPORT_TYPE_FEATURE<<8); - HIDRequest.wIndex = 0x0000; - HIDRequest.wLength = reportlen; - - align_request(&HIDRequest); - - data.hid_pkt = HIDRequest; - -/* if((retcode = shut_packet_send (&data, sizeof(data), SHUT_PKT_LAST)) > 0) */ - if((retcode = shut_packet_send (&data, 8, SHUT_PKT_LAST)) > 0) - { - if((retcode = shut_packet_recv (pkt, reportlen)) > 0) - { - upsdebug_hex(3, "shut_get_report", pkt, retcode); - return retcode; - } - else - return retcode; - } - return retcode; -} - -/********************************************************************** - * shut_set_report(int id, u_char *pkt, int reportlen) - * - * set report specified by id using pkt as value - * - * id - from shutdataType - * pkt - what to put in report - * strlen - length of report - * - * return string size on success, -1 on failure, -2 on NACK - * - *********************************************************************/ -int shut_set_report(int id, u_char *pkt, int reportlen) -{ - hid_packet_t HIDRequest; - hid_data_t data; - int retcode; - - upsdebugx (2, "entering shut_set_report(id: %02x, len: %02x)", id, reportlen); - - HIDRequest.bmRequestType = REQUEST_TYPE_SET_REPORT; - HIDRequest.bRequest = 0x09; - HIDRequest.wValue = id+(HID_REPORT_TYPE_FEATURE<<8); - HIDRequest.wIndex = 0x0000; - HIDRequest.wLength = reportlen; - - align_request(&HIDRequest); - - data.hid_pkt = HIDRequest; - - /* first packet to instruct a set command */ - if((retcode = shut_packet_send (&data, sizeof(data), 0x0)) > 0) - { - /* second packet to give the actual data */ - memcpy(&data.raw_pkt, pkt, reportlen); - upsdebug_hex(3, "Set2", pkt, reportlen); - - retcode = shut_packet_send (&data, reportlen, SHUT_PKT_LAST); - } - - return retcode; -} - -/********************************************************************** - * hid_init_device() - * - * Get Device/HID/Report descriptors from device and initialise - * HID Parser for further actions - * - * return 0 on success, -1 on failure - * - *********************************************************************/ -int hid_init_device() -{ - int retcode; - - /* Get HID descriptor */ - if((retcode = shut_get_descriptor(HID_DESCRIPTOR, hid_descriptor.raw_desc, 0x09)) > 0) - { - upsdebug_hex(3, "shut_get_descriptor(hid)", hid_descriptor.raw_desc, retcode); - - /* WORKAROUND: need to be fixed */ - hid_descriptor.hid_desc.wDescriptorLength = hid_descriptor.raw_desc[7] + - (hid_descriptor.raw_desc[8]<<8); - - upsdebugx(3, "HID Descriptor: \nbLength: \t\t0x%02x\nbDescriptorType: \t0x%02x\n", - hid_descriptor.hid_desc.bLength, - hid_descriptor.hid_desc.bDescriptorType); - - upsdebugx(3, "bcdHID: \t\t0x%04x\nbCountryCode: \t\t0x%02x\nbNumDescriptors: \t0x%02x\n", - hid_descriptor.hid_desc.bcdHID, - hid_descriptor.hid_desc.bCountryCode, - hid_descriptor.hid_desc.bNumDescriptors); - - upsdebugx(3, "bReportDescriptorType: \t0x%02x\nwDescriptorLength: \t0x%04x", - hid_descriptor.hid_desc.bReportDescriptorType, - hid_descriptor.hid_desc.wDescriptorLength); - - /* Get Device descriptor */ - if((retcode = shut_get_descriptor(DEVICE_DESCRIPTOR, device_descriptor.raw_desc, 0x12)) > 0) - { - upsdebug_hex(3, "shut_get_descriptor(device)", device_descriptor.raw_desc, retcode); - - upsdebugx(2, "Device Descriptor: \nbLength: \t\t0x%02x\nbDescriptorType:\ - \t0x%02x\nbcdUSB: \t\t0x%04x\nbDeviceClass: \t\t0x%02x\nbDeviceSubClass:\ - \t0x%02x\nbDeviceProtocol: \t0x%02x\nbMaxPacketSize0:\ - \t0x%02x\nidVendor: \t\t0x%04x\nidProduct: \t\t0x%04x\nbcdDevice:\ - \t\t0x%04x\niManufacturer: \t\t0x%02x\niProduct:\ - \t\t0x%02x\niSerialNumber: \t\t0x%02x\nbNumConfigurations: \t0x%02x\n", - device_descriptor.dev_desc.bLength, - device_descriptor.dev_desc.bDescriptorType, - device_descriptor.dev_desc.bcdUSB, - device_descriptor.dev_desc.bDeviceClass, - device_descriptor.dev_desc.bDeviceSubClass, - device_descriptor.dev_desc.bDeviceProtocol, - device_descriptor.dev_desc.bMaxPacketSize0, - device_descriptor.dev_desc.idVendor, - device_descriptor.dev_desc.idProduct, - device_descriptor.dev_desc.bcdDevice, - device_descriptor.dev_desc.iManufacturer, - device_descriptor.dev_desc.iProduct, - device_descriptor.dev_desc.iSerialNumber, - device_descriptor.dev_desc.bNumConfigurations); - - /* Get Report descriptor */ - if((retcode = shut_get_descriptor(REPORT_DESCRIPTOR, raw_buf, - hid_descriptor.hid_desc.wDescriptorLength)) > 0) { - - upsdebug_hex(3, "shut_get_descriptor(report)", raw_buf, retcode); - - /* Parse Report Descriptor */ - Free_ReportDesc(pDesc); - pDesc = Parse_ReportDesc(raw_buf, retcode); - if (!pDesc) { - fatalx(EXIT_FAILURE, "Failed to parse report descriptor: %s", strerror(errno)); - } - } - else - fatalx(EXIT_FAILURE, "Unable to get Report Descriptor"); - } - else - fatalx(EXIT_FAILURE, "Unable to get Device Descriptor"); - } - else - fatalx(EXIT_FAILURE, "Unable to get HID Descriptor"); - - return 0; -} - -/* translate HID string path to numeric path and return path depth */ -ushort lookup_path(const char *HIDpath, HIDData_t *data) -{ - ushort i = 0, cond = 1; - int cur_usage; - char buf[MAX_STRING]; - char *start, *end; - - strncpy(buf, HIDpath, strlen(HIDpath)); - buf[strlen(HIDpath)] = '\0'; - start = end = buf; - - upsdebugx(3, "entering lookup_path(%s)", buf); - - while (cond) { - - if ((end = strchr(start, '.')) == NULL) { - cond = 0; - } - else - *end = '\0'; - - upsdebugx(4, "parsing %s", start); - - /* lookup code */ - if ((cur_usage = hid_lookup_usage(start)) == -1) { - upsdebugx(4, "%s wasn't found", start); - return 0; - } - else { - data->Path.Node[i] = cur_usage; - i++; - } - - if(cond) - start = end +1 ; - } - data->Path.Size = i; - return i; -} - -/* Lookup this usage name to find its code (page + index) */ -int hid_lookup_usage(char *name) -{ - int i; - - upsdebugx(4, "Looking up %s", name); - - if (name[0] == '[') /* manage indexed collection */ - return (0x00FF0000 + atoi(&name[1])); - else { - for (i = 0; (usage_lkp[i].usage_code != 0x0); i++) - { - if (!strcmp(usage_lkp[i].usage_name, name)) - { - upsdebugx(4, "hid_lookup_usage: found %04x", - usage_lkp[i].usage_code); - return usage_lkp[i].usage_code; - } - } - } - return -1; -} - -/* Get an item value from a HID path */ -int hid_get_value(const char *item_path) -{ - int i, retcode; - HIDData_t hData; - - upsdebugx(3, "entering hid_get_value(%s)", item_path); - - /* Prepare path of HID object */ - hData.Type = ITEM_FEATURE; - hData.ReportID = 0; - - if((retcode = lookup_path(item_path, &hData)) > 0) { - upsdebugx(3, "Path depth = %i\n", retcode); - - for (i = 0; i 0) { - GetValue((const u_char *) raw_buf, &hData, &hValue); - upsdebug_hex(3, "Object's report", raw_buf, 10); - upsdebugx(3, "Value = %ld", hValue); - return 1; - } - else - shut_ups_start(); - } - else { - upsdebugx(3, "Can't find object"); - return 0; - } - } - else { - upsdebugx(3, "Can't lookup object's path"); - return 0; - } - - return 0; -} - - /* - * Internal functions - ****************************************************************************/ - -/* - * Filter and reformat HID strings (suppress space - * between each letter) - * Note: string format in HID String Descriptor is - * Byte1: Size of descriptor(=>string) - * Byte2: String descriptor type (always 0x03) - * Byte3 to byteN: UNICODE string (in US: xx 00 for a letter) - */ -void make_string(u_char *buf, int datalen, char *string) -{ - int i, /* Skip size and type */ - j=0; - - upsdebugx(4, "String descriptor: size = 0x%02x, type = 0x%02x", buf[0], buf[1]); - - /* TODO: add clean support for UNICODE */ - for(i=2;i0) - return (float) a * expo(a,b-1); - if (b<0) - return (float)((float)(1/(float)a) * (float) expo(a,b+1)); - - /* not reached */ - return -1; -} - -/* Format model names */ -const char *get_model_name(char *iProduct, char *iModel) -{ - models_name_t *model = NULL; - - upsdebugx(2, "get_model_name(%s, %s)\n", iProduct, iModel); - - /* Search for formatting rules */ - for ( model = models_names ; model->iProduct != NULL ; model++ ) - { - upsdebugx(2, "comparing with: %s", model->finalname); - if ( (!strncmp(iProduct, model->iProduct, strlen(model->iProduct))) - && (!strncmp(iModel, model->iModel, strlen(model->iModel))) ) - { - upsdebugx(2, "Found %s\n", model->finalname); - break; - } - } - /* FIXME: if we end up with model->iProduct == NULL - * then process name in a generic way (not yet supported models!) - */ - return model->finalname; -} - -/* set r/w INFO_ element to a value. */ -int hid_set_value(const char *varname, const char *val) -{ - int retcode, i, replen; - mge_info_item_t *shut_info_p; - HIDData_t hData; - - upsdebugx(2, "============== entering hid_set_value(%s, %s) ==============", varname, val); - - /* 1) retrieve and check netvar & item_path */ - shut_info_p = shut_find_info(varname); - - if (shut_info_p == NULL || shut_info_p->type == NULL || - !(shut_info_p->flags & SHUT_FLAG_OK)) - { - upsdebugx(2, "hid_ups_set: info element unavailable %s", varname); - return STAT_SET_UNKNOWN; - } - - /* Checking item writability and HID Path */ - if (!shut_info_p->flags & ST_FLAG_RW) { - upsdebugx(2, "hid_ups_set: not writable %s", varname); - return STAT_SET_UNKNOWN; - } - - /* handle server side variable */ - if (shut_info_p->shut_flags & SHUT_FLAG_ABSENT) { - upsdebugx(2, "hid_ups_set: setting server side variable %s", varname); - dstate_setinfo(shut_info_p->type, "%s", val); - return STAT_SET_HANDLED; - } else { - /* SHUT_FLAG_ABSENT is the only case of HID Path == NULL */ - if (shut_info_p->item_path == NULL) { - upsdebugx(2, "hid_ups_set: ID Path is NULL for %s", varname); - return STAT_SET_UNKNOWN; - } - } - - /* Prepare path of HID object */ - hData.Type = ITEM_FEATURE; - hData.ReportID = 0; - - if((retcode = lookup_path(shut_info_p->item_path, &hData)) > 0) { - upsdebugx(3, "Path depth = %i\n", retcode); - - for (i = 0; i disabled for now - if (shut_get_report(hData.ReportID, raw_buf, MAX_REPORT_SIZE) > 0) { - GetValue((const u_char *) raw_buf, &hData, &hValue); - upsdebugx(3, "Value = %d", hValue); - - if (hValue != atol(val)) - upsdebugx(3, "FAILED"); - else - upsdebugx(3, "SUCCEED"); - } else - upsdebugx(3, "FAILED"); - */ - return STAT_SET_HANDLED; - } - else - upsdebugx(3, "Object is constant"); - } - else - upsdebugx(3, "Can't find object"); - } - else - upsdebugx(3, "Can't lookup object's path"); - - return STAT_SET_UNKNOWN; -} - -/* find info element definition in my info array. */ -mge_info_item_t *shut_find_info(const char *varname) -{ - mge_info_item_t *shut_info_p; - - for (shut_info_p = &mge_info[0]; shut_info_p->type != NULL; shut_info_p++) - if (!strcasecmp(shut_info_p->type, varname)) - return shut_info_p; - - fatalx(EXIT_FAILURE, "shut_find_info: unknown info type: %s", varname); - return NULL; -} - -/* find the NUT value matching that HID Item value */ -static const char *hu_find_infoval(info_lkp_t *hid2info, long value) -{ - info_lkp_t *info_lkp; - - upsdebugx(3, "hu_find_infoval: searching for value = %ld\n", value); - - for (info_lkp = hid2info; (info_lkp != NULL) && - (strcmp(info_lkp->nut_value, "NULL")); info_lkp++) { - - if (info_lkp->hid_value == value) { - upsdebugx(3, "hu_find_infoval: found %s (value: %ld)\n", - info_lkp->nut_value, value); - - return info_lkp->nut_value; - } - } - upsdebugx(3, "hu_find_infoval: no matching INFO_* value for this HID value (%ld)\n", value); - return NULL; -} diff --git a/drivers/mge-shut.h b/drivers/mge-shut.h deleted file mode 100644 index 850be4d..0000000 --- a/drivers/mge-shut.h +++ /dev/null @@ -1,523 +0,0 @@ -/* mge-shut.h - monitor MGE UPS for NUT with SHUT protocol - * - * Copyright (C) 2002 - 2005 - * Arnaud Quette & - * Philippe Marzouk - * - * Sponsored by MGE UPS SYSTEMS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include "hidparser.h" -#include "hidtypes.h" - -#define DEFAULT_TIMEOUT 3000 -#define MAX_STRING 64 - -#define DEFAULT_LOWBATT 30 /* low battery level, in % */ -#define DEFAULT_ONDELAY 3 /* delay between return of utility power - and powering up of load, in 10 seconds units */ -#define DEFAULT_OFFDELAY 20 /* delay befor power off, in seconds */ -#define OFF_NOTIFICATION 1 /* notification off */ - -#define LIGHT_NOTIFICATION 2 /* light notification */ -#define COMPLETE_NOTIFICATION 3 /* complete notification for UPSs which do - * not support disabling it like some early - * Ellipse models */ -#define DEFAULT_NOTIFICATION COMPLETE_NOTIFICATION - -/* HID definitions */ - -#define HID_REPORT_TYPE_INPUT 0x01 -#define HID_REPORT_TYPE_OUTPUT 0x02 -#define HID_REPORT_TYPE_FEATURE 0x03 - -#define REQUEST_TYPE_USB 0x80 -#define REQUEST_TYPE_HID 0x81 -#define REQUEST_TYPE_GET_REPORT 0xa1 -#define REQUEST_TYPE_SET_REPORT 0x21 - -#define DEVICE_DESCRIPTOR 0x0001 -#define CONFIG_DESCRIPTOR 0x0002 -#define STRING_DESCRIPTOR 0x0003 -#define INTERFACE_DESCRIPTOR 0x0004 -#define ENDPOINT_DESCRIPTOR 0x0005 -#define HID_DESCRIPTOR 0x0021 -#define REPORT_DESCRIPTOR 0x0022 - -#define MAX_REPORT_SIZE 0x1800 - -/* SHUT definitions - From Simplified SHUT spec */ - -#define SHUT_TYPE_REQUEST 0x01 -#define SHUT_TYPE_RESPONSE 0x04 -#define SHUT_TYPE_NOTIFY 0x05 -#define SHUT_OK 0x06 -#define SHUT_NOK 0x15 -#define SHUT_SYNC 0x16 /* complete notifications - not yet managed - but needed for some early Ellipse models */ -#define SHUT_SYNC_LIGHT 0x17 /* partial notifications */ -#define SHUT_SYNC_OFF 0x18 /* disable notifications - only do polling */ -#define SHUT_PKT_LAST 0x80 - -/* From SHUT specifications */ - -typedef struct hid_packet { - unsigned char bmRequestType; - unsigned char bRequest; - unsigned short wValue; - unsigned short wIndex; - unsigned short wLength; -/* unsigned char padding[8]; for use with shut_set_report */ -} hid_packet_t; - -typedef union hid_data_u { - hid_packet_t hid_pkt; - unsigned char raw_pkt[8]; /* max report lengh, was 8 */ -} hid_data_t; - -typedef struct shut_packet { - unsigned char bType; - unsigned char bLength; - hid_data_t data; - unsigned char bChecksum; -} shut_packet_t; - -typedef union shut_data_u { - shut_packet_t shut_pkt; - unsigned char raw_pkt[11]; -} shut_data_t; - -/* From USB/HID specifications */ - -typedef struct hid_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short bcdHID; - unsigned char bCountryCode; - unsigned char bNumDescriptors; - unsigned char bReportDescriptorType; - unsigned short wDescriptorLength; -} hid_descriptor_t; - -typedef union hid_desc_data_u { - hid_descriptor_t hid_desc; - unsigned char raw_desc[9]; /* max report lengh, aws 9 */ -} hid_desc_data_t; - -typedef struct device_descriptor { - unsigned char bLength; - unsigned char bDescriptorType; - unsigned short bcdUSB; - unsigned char bDeviceClass; - unsigned char bDeviceSubClass; - unsigned char bDeviceProtocol; - unsigned char bMaxPacketSize0; - unsigned short idVendor; - unsigned short idProduct; - unsigned short bcdDevice; - unsigned char iManufacturer; - unsigned char iProduct; - unsigned char iSerialNumber; - unsigned char bNumConfigurations; -} device_descriptor_t; - -typedef union device_desc_data_u { - device_descriptor_t dev_desc; - unsigned char raw_desc[18]; -} device_desc_data_t; - -/* --------------------------------------------------------------- */ -/* Explicit Booleans */ -/* --------------------------------------------------------------- */ - -#define SHUT_FLAG_OK (1 << 0) /* show element to upsd. */ -#define SHUT_FLAG_STATIC (1 << 1) /* retrieve info only once. */ -#define SHUT_FLAG_ABSENT (1 << 2) /* data is absent in the device, - use default value. */ -#define SHUT_FLAG_STALE (1 << 3) /* data stale, don't try too often. */ -#define SHUT_FLAG_DELAY (1 << 4) /* delay type value: formated differently. */ - -/* --------------------------------------------------------------- */ -/* Model Name formating entries */ -/* --------------------------------------------------------------- */ -typedef struct { - const char *iProduct; - const char *iModel; - const char *finalname; -} models_name_t; - -models_name_t models_names [] = - { - /* Ellipse models */ - { "ELLIPSE", "300", "ellipse 300" }, - { "ELLIPSE", "500", "ellipse 500" }, - { "ELLIPSE", "650", "ellipse 650" }, - { "ELLIPSE", "800", "ellipse 800" }, - { "ELLIPSE", "1200", "ellipse 1200" }, - /* Ellipse Premium models */ - { "ellipse", "PR500", "ellipse premium 500" }, - { "ellipse", "PR650", "ellipse premium 650" }, - { "ellipse", "PR800", "ellipse premium 800" }, - { "ellipse", "PR1200", "ellipse premium 1200" }, - /* Ellipse "Pro" */ - { "ELLIPSE", "600", "Ellipse 600" }, - { "ELLIPSE", "750", "Ellipse 750" }, - { "ELLIPSE", "1000", "Ellipse 1000" }, - { "ELLIPSE", "1500", "Ellipse 1500" }, - /* Ellipse "MAX" */ - { "Ellipse MAX", "600", "Ellipse MAX 600" }, - { "Ellipse MAX", "850", "Ellipse MAX 850" }, - { "Ellipse MAX", "1100", "Ellipse MAX 1100" }, - { "Ellipse MAX", "1500", "Ellipse MAX 1500" }, - /* Protection Center */ - { "PROTECTIONCENTER", "420", "Protection Center 420" }, - { "PROTECTIONCENTER", "500", "Protection Center 500" }, - { "PROTECTIONCENTER", "675", "Protection Center 675" }, - /* Pulsar Evolution models */ - { "Evolution", "500", "Pulsar Evolution 500" }, - { "Evolution", "800", "Pulsar Evolution 800" }, - { "Evolution", "1100", "Pulsar Evolution 1100" }, - { "Evolution", "1500", "Pulsar Evolution 1500" }, - { "Evolution", "2200", "Pulsar Evolution 2200" }, - { "Evolution", "3000", "Pulsar Evolution 3000" }, - { "Evolution", "3000XL", "Pulsar Evolution 3000 XL" }, - /* Newer Evolution models */ - { "Evolution", "650", "Evolution 650" }, - { "Evolution", "850", "Evolution 850" }, - { "Evolution", "1150", "Evolution 1150" }, - { "Evolution", "S 1250", "Evolution S 1250" }, - { "Evolution", "1550", "Evolution 1550" }, - { "Evolution", "S 1750", "Evolution S 1750" }, - { "Evolution", "2000", "Evolution 2000" }, - { "Evolution", "S 2500", "Evolution S 2500" }, - { "Evolution", "S 3000", "Evolution S 3000" }, - /* Pulsar M models */ - { "PULSAR M", "2200", "Pulsar M 2200" }, - { "PULSAR M", "3000", "Pulsar M 3000" }, - { "PULSAR M", "3000 XL", "Pulsar M 3000 XL" }, - /* Eaton'ified names */ - { "EX", "2200", "EX 2200" }, - { "EX", "3000", "EX 3000" }, - { "EX", "3000 XL", "EX 3000 XL" }, - /* Pulsar models */ - { "Pulsar", "700", "Pulsar 700" }, - { "Pulsar", "1000", "Pulsar 1000" }, - { "Pulsar", "1500", "Pulsar 1500" }, - { "Pulsar", "1000 RT2U", "Pulsar 1000 RT2U" }, - { "Pulsar", "1500 RT2U", "Pulsar 1500 RT2U" }, - /* Eaton'ified names */ - { "EX", "700", "EX 700" }, - { "EX", "1000", "EX 1000" }, - { "EX", "1500", "EX 1500" }, - { "EX", "1000 RT2U", "EX 1000 RT2U" }, - { "EX", "1500 RT2U", "EX 1500 RT2U" }, - /* Pulsar MX models */ - { "PULSAR", "MX4000", "Pulsar MX 4000 RT" }, - { "PULSAR", "MX5000", "Pulsar MX 5000 RT" }, - /* NOVA models */ - { "NOVA AVR", "600", "NOVA 600 AVR" }, - { "NOVA AVR", "625", "Nova AVR 625" }, - { "NOVA AVR", "1100", "NOVA 1100 AVR" }, - { "NOVA AVR", "1250", "Nova AVR 1250" }, - /* EXtreme C (EMEA) */ - { "EXtreme", "700C", "Pulsar EXtreme 700C" }, - { "EXtreme", "1000C", "Pulsar EXtreme 1000C" }, - { "EXtreme", "1500C", "Pulsar EXtreme 1500C" }, - { "EXtreme", "1500CCLA", "Pulsar EXtreme 1500C CLA" }, - { "EXtreme", "2200C", "Pulsar EXtreme 2200C" }, - { "EXtreme", "3200C", "Pulsar EXtreme 3200C" }, - /* EXtreme C (USA, aka "EX RT") */ - { "EX", "700RT", "Pulsar EX 700 RT" }, - { "EX", "1000RT", "Pulsar EX 1000 RT" }, - { "EX", "1500RT", "Pulsar EX 1500 RT" }, - { "EX", "2200RT", "Pulsar EX 2200 RT" }, - { "EX", "3200RT", "Pulsar EX 3200 RT" }, - /* Comet EX RT three phased */ - { "EX", "5RT31", "EX 5 RT 3:1" }, - { "EX", "7RT31", "EX 7 RT 3:1" }, - { "EX", "11RT31", "EX 11 RT 3:1" }, - /* Comet EX RT single phased */ - { "EX", "5RT", "EX 5 RT" }, - { "EX", "7RT", "EX 7 RT" }, - { "EX", "11RT", "EX 11 RT" }, - /* Galaxy 3000 */ - { "GALAXY", "3000_10", "Galaxy 3000 10 kVA" }, - { "GALAXY", "3000_15", "Galaxy 3000 15 kVA" }, - { "GALAXY", "3000_20", "Galaxy 3000 20 kVA" }, - { "GALAXY", "3000_30", "Galaxy 3000 30 kVA" }, - - /* FIXME: To be completed (Comet, Galaxy, Esprit, ...) */ - - /* end of structure. */ - { NULL, NULL, "Generic SHUT model" } -}; -/* for lookup between HID values and NUT values*/ -typedef struct { - long hid_value; /* HID value */ - const char *nut_value; /* NUT value */ -} info_lkp_t; - -/* Actual value lookup tables => should be fine for all Mfrs (TODO: validate it!) */ -/* --------------------------------------------------------------- */ -/* Lookup values between NUT and HID */ -/* --------------------------------------------------------------- */ -info_lkp_t onbatt_info[] = { - { 0, "OB" }, - { 1, "OL" }, - { 0, "NULL" } -}; -info_lkp_t discharging_info[] = { - { 1, "DISCHRG" }, - { 0, "NULL" } -}; -info_lkp_t charging_info[] = { - { 1, "CHRG" }, - { 0, "NULL" } -}; -info_lkp_t lowbatt_info[] = { - { 1, "LB" }, - { 0, "NULL" } -}; -info_lkp_t overbatt_info[] = { - { 1, "OVER" }, - { 0, "NULL" } -}; -info_lkp_t replacebatt_info[] = { - { 1, "RB" }, - { 0, "NULL" } -}; -info_lkp_t shutdownimm_info[] = { - { 1, "LB" }, - { 0, "NULL" } -}; -info_lkp_t trim_info[] = { - { 1, "TRIM" }, - { 0, "NULL" } -}; -info_lkp_t boost_info[] = { - { 1, "BOOST" }, - { 0, "NULL" } -}; -/* TODO: add BYPASS, OFF, CAL */ - -info_lkp_t test_write_info[] = { - { 0, "No test" }, - { 1, "Quick test" }, - { 2, "Deep test" }, - { 3, "Abort test" }, - { 0, "NULL" } -}; -info_lkp_t test_read_info[] = { - { 1, "Done and passed" }, - { 2, "Done and warning" }, - { 3, "Done and error" }, - { 4, "Aborted" }, - { 5, "In progress" }, - { 6, "No test initiated" }, - { 0, "NULL" } -}; - -/* --------------------------------------------------------------- */ -/* Query Commands and their Mapping to INFO_ Variables */ -/* --------------------------------------------------------------- */ - -/* Structure defining how to query UPS for a variable and write - information to INFO structure. -*/ -typedef struct { - const char *type; /* INFO_* element */ - int flags; /* INFO-element flags to set in addinfo */ - int length; /* INFO-element length of strings */ - const char *item_path; /* HID object (fully qualified string path) */ - const char *fmt; /* printf format string for INFO entry */ - const char *dfl; /* default value */ - unsigned long shut_flags; /* specific SHUT flags */ - info_lkp_t *hid2info; /* lookup table between HID and NUT values */ -} mge_info_item_t; - -/* Array containing information to translate between UTalk and NUT info - * NOTE: - * - Array is terminated by element with type NULL. - * - Essential INFO items (ups.{mfr, model, firmware, status} are - * handled separately. - * - Array is NOT const, since "shut_flags" can be changed. - */ - -/* FIXME: should be shared with mgehid.h */ -static mge_info_item_t mge_info[] = { - /* Battery page */ - { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "battery.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerSummary.RemainingCapacityLimitSetting", - "%ld", NULL, SHUT_FLAG_OK, NULL }, /* RW, to be caught first if exists... */ - { "battery.charge.low", ST_FLAG_STRING, 5, "UPS.PowerSummary.RemainingCapacityLimit", - "%ld", NULL, SHUT_FLAG_OK, NULL }, /* ... or Read only */ - { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", "%.0d", NULL, SHUT_FLAG_OK, NULL }, - /* UPS page */ - { "ups.mfr", ST_FLAG_STRING, 20, NULL, "%s", "MGE UPS SYSTEMS", SHUT_FLAG_ABSENT | SHUT_FLAG_OK, NULL }, - { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "ups.timer.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerSummary.DelayBeforeShutdown", - "%ld", NULL, SHUT_FLAG_OK | SHUT_FLAG_DELAY, NULL }, - { "ups.timer.reboot", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerSummary.DelayBeforeReboot", - "%ld", NULL, SHUT_FLAG_OK | SHUT_FLAG_DELAY, NULL }, - { "ups.timer.start", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerSummary.DelayBeforeStartup", - "%ld", NULL, SHUT_FLAG_OK | SHUT_FLAG_DELAY, NULL }, - /* FIXME: miss ups.power */ - { "ups.power.nominal", ST_FLAG_STRING, 5, "UPS.Flow.[4].ConfigApparentPower", - "%i", NULL, SHUT_FLAG_OK, NULL }, - { "ups.test.interval", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.BatterySystem.Battery.TestPeriod", - "%i", NULL, SHUT_FLAG_OK, NULL }, - { "ups.test.result", ST_FLAG_STRING, 5, "UPS.BatterySystem.Battery.Test", - "%i", NULL, SHUT_FLAG_OK, &test_read_info[0] }, - - /* Output page */ - { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "output.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "output.current", 0, 0, "UPS.PowerSummary.Output.Current", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", "%i", NULL, SHUT_FLAG_OK, NULL }, - - /* Outlet page (using MGE UPS SYSTEMS - PowerShare technology) */ - /* TODO: add an iterative semantic [%x] to factorise outlets */ - { "outlet.id", 0, 0, "UPS.OutletSystem.Outlet.[1].OutletID", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, "UPS.OutletSystem.Outlet.[1].OutletID", - "%s", "Main Outlet", SHUT_FLAG_ABSENT | SHUT_FLAG_OK, NULL }, - { "outlet.switchable", 0, 0, "UPS.OutletSystem.Outlet.[1].PresentStatus.Switchable", - "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.1.id", 0, 0, "UPS.OutletSystem.Outlet.[2].OutletID", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.1.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, "UPS.OutletSystem.Outlet.[2].OutletID", - "%s", "PowerShare Outlet 1", SHUT_FLAG_ABSENT | SHUT_FLAG_OK, NULL }, - { "outlet.1.switchable", 0, 0, "UPS.OutletSystem.Outlet.[2].PresentStatus.Switchable", - "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.1.switch", ST_FLAG_RW | ST_FLAG_STRING, 2, "UPS.OutletSystem.Outlet.[2].PresentStatus.SwitchOn/Off", - "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.1.autoswitch.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 3, - "UPS.OutletSystem.Outlet.[2].RemainingCapacityLimit", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.1.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 5, - "UPS.OutletSystem.Outlet.[2].DelayBeforeShutdown", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.1.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.OutletSystem.Outlet.[2].DelayBeforeStartup", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.2.id", 0, 0, "UPS.OutletSystem.Outlet.[3].OutletID", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.2.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, "UPS.OutletSystem.Outlet.[3].OutletID", - "%s", "PowerShare Outlet 2", SHUT_FLAG_ABSENT | SHUT_FLAG_OK, NULL }, - { "outlet.2.switchable", 0, 0, "UPS.OutletSystem.Outlet.[3].PresentStatus.Switchable", - "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.2.switch", ST_FLAG_RW | ST_FLAG_STRING, 2, "UPS.OutletSystem.Outlet.[3].PresentStatus.SwitchOn/Off", - "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.2.autoswitch.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 3, - "UPS.OutletSystem.Outlet.[3].RemainingCapacityLimit", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.2.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 5, - "UPS.OutletSystem.Outlet.[3].DelayBeforeShutdown", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "outlet.2.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 5, - "UPS.OutletSystem.Outlet.[3].DelayBeforeStartup", "%i", NULL, SHUT_FLAG_OK, NULL }, - - /* Input page */ - { "input.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Voltage", "%i", NULL, SHUT_FLAG_OK, NULL }, - { "input.frequency", 0, 0, "UPS.PowerConverter.Input.[1].Frequency", "%i", NULL, SHUT_FLAG_OK, NULL }, - - /* terminating element */ - { NULL, 0, 0, "\0", "\0", NULL, 0, NULL } -}; - -/* temporary usage code lookup */ -typedef struct { - const char *usage_name; - const uint32_t usage_code; -} usage_lkp_t; - -/* FIXME: share this data structure with libhid.c */ -static usage_lkp_t usage_lkp[] = { - /* Power Device Page */ - { "PresentStatus", 0x00840002 }, - { "UPS", 0x00840004 }, - { "BatterySystem", 0x00840010 }, - { "Battery", 0x00840012 }, - { "BatteryID", 0x00840013 }, - { "PowerConverter", 0x00840016 }, - { "OutletSystem", 0x00840018 }, - { "Input", 0x0084001a }, - { "Output", 0x0084001c }, - { "Outlet", 0x00840020 }, - { "OutletID", 0x00840021 }, - { "PowerSummary", 0x00840024 }, - { "Voltage", 0x00840030 }, - { "Current", 0x00840031 }, - { "Frequency", 0x00840032 }, - { "PercentLoad", 0x00840035 }, - { "ConfigVoltage", 0x00840040 }, - { "ConfigCurrent", 0x00840041 }, - { "ConfigFrequency", 0x00840042 }, - { "ConfigApparentPower", 0x00840043 }, - { "LowVoltageTransfer", 0x00840053 }, - { "HighVoltageTransfer", 0x00840054 }, - { "DelayBeforeReboot", 0x00840055 }, - { "DelayBeforeStartup", 0x00840056 }, - { "DelayBeforeShutdown", 0x00840057 }, - { "Test", 0x00840058 }, - { "Good", 0x00840061 }, - { "Overload", 0x00840065 }, /* sic */ - { "SwitchOn/Off", 0x0084006b }, - { "Switchable", 0x0084006c }, - { "Used", 0x0084006d }, - { "Flow", 0x0084001e }, - /* Battery System Page */ - { "RemainingCapacityLimit", 0x00850029 }, - { "BelowRemainingCapacityLimit", 0x00850042 }, - { "RemainingCapacity", 0x00850066 }, - { "RunTimeToEmpty", 0x00850068 }, - { "ACPresent", 0x008500d0 }, - { "Charging", 0x00850044 }, - { "Discharging", 0x00850045 }, - { "NeedReplacement", 0x0085004b }, - /* MGE UPS SYSTEMS Page */ - { "iModel", 0xffff00f0 }, - { "RemainingCapacityLimitSetting", 0xffff004d }, - { "TestPeriod", 0xffff0001 }, - - { "\0", 0x0 } -}; - -/* SHUT / HID functions Prototypes */ - -int shut_ups_start(void); -u_char shut_checksum(const u_char *buf, int bufsize); -int shut_token_send(u_char token); -int shut_packet_send (hid_data_t *hdata, int datalen, u_char token); -int shut_packet_recv (u_char *Buf, int datalen); - -int shut_get_descriptor(int desctype, u_char *pkt, int reportlen); -int shut_get_string(int strindex, char *string, int stringlen); -int shut_get_report(int id, u_char *pkt, int reportlen); -int shut_set_report(int id, u_char *pkt, int reportlen); -int shut_identify_ups (void); -int shut_wait_ack (void); -void shut_ups_status(void); - -int hid_init_device(void); -const char *get_model_name(char *iProduct, char *iModel); -int hid_lookup_usage(char *name); -int hid_get_value(const char *item_path); -int hid_set_value(const char *varname, const char *val); -u_short lookup_path(const char *HIDpath, HIDData_t *data); - -int instcmd(const char *cmdname, const char *extra); -void setline(int set); -int serial_read (int read_timeout, u_char *readbuf); -int serial_send(u_char *buf, int len); - -void make_string(u_char *buf, int datalen, char *string); - - -mge_info_item_t *shut_find_info(const char *varname); diff --git a/drivers/mge-utalk.c b/drivers/mge-utalk.c index 616e2ad..86d272f 100644 --- a/drivers/mge-utalk.c +++ b/drivers/mge-utalk.c @@ -3,7 +3,7 @@ * Copyright (C) 2002 - 2005 * Arnaud Quette * Hans Ekkehard Plesser - * Martin Loyer + * Martin Loyer * Patrick Agrain * Nicholas Reilly * Dave Abbott @@ -30,41 +30,44 @@ /* * IMPLEMENTATION DETAILS - * + * * Not all UTalk models provide all possible information, settings and commands. * mge-utalk checks on startup which variables and commands are available from * the UPS, and re-reads these regularly. Thus, startup is a bit slow, but this * should not matter much. - * + * * mge-utalk.h defines a struct array that tells the driver how to read * variables from the UPS and publish them as NUT data. - * + * * "ups.status" variable is not included in this array, since it contains * information that requires several calls to the UPS and more advanced analysis * of the reponses. The function get_ups_status does this job. - * + * * Note that MGE enumerates the status "bits" from right to left, * i.e., if buf[] contains the reponse to command "Ss" (read system status), * then buf[0] contains "bit" Ss.1.7 (General alarm), while buf[7] contains - * "bit" Ss.1.0 (Load unprotected). - * + * "bit" Ss.1.0 (Load unprotected). + * * enable_ups_comm() is called before each attempt to read/write data * from/to the UPS to re synchronise the communication. */ +#include "config.h" /* must be the first header */ + #include #include #include "timehead.h" #include "main.h" #include "serial.h" #include "mge-utalk.h" +#include "nut_stdint.h" /* --------------------------------------------------------------- */ /* Define "technical" constants */ /* --------------------------------------------------------------- */ #define DRIVER_NAME "MGE UPS SYSTEMS/U-Talk driver" -#define DRIVER_VERSION "0.92" +#define DRIVER_VERSION "0.94" /* driver description structure */ @@ -73,7 +76,7 @@ upsdrv_info_t upsdrv_info = { DRIVER_VERSION, "Arnaud Quette \n" \ "Hans Ekkehard Plesser \n" \ - "Martin Loyer \n" \ + "Martin Loyer \n" \ "Patrick Agrain \n" \ "Nicholas Reilly \n" \ "Dave Abbott \n" \ @@ -96,12 +99,12 @@ upsdrv_info_t upsdrv_info = { #define MGE_REPLY_IGNCHAR "\r\n" #define MAXTRIES 10 /* max number of connect tries */ -#define BUFFLEN 256 +#define BUFFLEN 256 #define SD_RETURN 0 #define SD_STAYOFF 1 -int sdtype = SD_RETURN; +static int sdtype = SD_RETURN; static time_t lastpoll; /* Timestamp the last polling */ /* --------------------------------------------------------------- */ @@ -124,12 +127,12 @@ static int instcmd(const char *cmdname, const char *extra); static int setvar(const char *varname, const char *val); static void enable_ups_comm(void); static void disable_ups_comm(void); -static void extract_info(const char *buf, const mge_info_item_t *mge, - char *infostr, int infolen); +static void extract_info(const char *buf, const mge_info_item_t *mge, + char *infostr, size_t infolen); static const char *info_variable_cmd(const char *type); static bool_t info_variable_ok(const char *type); static int get_ups_status(void); -static int mge_command(char *reply, int replylen, const char *fmt, ...); +static ssize_t mge_command(char *reply, size_t replylen, const char *fmt, ...); /* --------------------------------------------------------------- */ /* UPS Driver Functions */ @@ -139,17 +142,17 @@ void upsdrv_makevartable(void) { char temp[BUFFLEN]; - snprintf(temp, sizeof(temp), - "Low battery level, in %% (default = %3d)", + snprintf(temp, sizeof(temp), + "Low battery level, in %% (default = %3d)", DEFAULT_LOWBATT); addvar (VAR_VALUE, "LowBatt", temp); - snprintf(temp, sizeof(temp), + snprintf(temp, sizeof(temp), "Delay before startup, in minutes (default = %3d)", DEFAULT_ONDELAY); addvar (VAR_VALUE, "OnDelay", temp); - snprintf(temp, sizeof(temp), + snprintf(temp, sizeof(temp), "Delay before shutdown, in seconds (default = %3d)", DEFAULT_OFFDELAY); addvar (VAR_VALUE, "OffDelay", temp); @@ -163,14 +166,14 @@ void upsdrv_initups(void) { char buf[BUFFLEN]; int RTS = TIOCM_RTS; - + upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); /* read command line/conf variable that affect comm. */ if (testvar ("oldmac")) RTS = ~TIOCM_RTS; - + /* Init serial line */ ioctl(upsfd, TIOCMBIC, &RTS); enable_ups_comm(); @@ -224,19 +227,18 @@ void upsdrv_initinfo(void) int table; int tries; int status_ok = 0; - int bytes_rcvd; + ssize_t bytes_rcvd; int si_data1 = 0; int si_data2 = 0; mge_info_item_t *item; models_name_t *model_info; mge_model_info_t *legacy_model; char infostr[32]; - int chars_rcvd; + ssize_t chars_rcvd; /* manufacturer -------------------------------------------- */ dstate_setinfo("ups.mfr", "MGE UPS SYSTEMS"); - dstate_setinfo("driver.version.internal", "%s", DRIVER_VERSION); - + /* loop until we have at status */ tries = 0; do { @@ -287,10 +289,9 @@ void upsdrv_initinfo(void) *p = '\0'; si_data1 = atoi(buf); v = p+1; + p = strchr(v, ' '); } - p = strchr(v, ' '); - if ( p != NULL ) { *p = '\0'; si_data2 = atoi(v); @@ -322,13 +323,13 @@ void upsdrv_initinfo(void) } } - if ( firmware && strcmp(firmware, "")) + if (firmware && firmware[0] != '\0') dstate_setinfo("ups.firmware", "%s", firmware); else dstate_setinfo("ups.firmware", "unknown"); - + /* multiplier table */ - /* */ + /* */ bytes_rcvd = mge_command(buf, sizeof(buf), "Ai"); if (bytes_rcvd > 0 && buf[0] != '?') { @@ -343,7 +344,7 @@ void upsdrv_initinfo(void) /* status --- try only system status, to get the really important * information (OL, OB, LB); all else is added later by updateinfo */ status_ok = get_ups_status(); - + } while ( (!status_ok) && (tries++ < MAXTRIES) && (exit_flag != 1) ); if ( tries == MAXTRIES && !status_ok ) @@ -361,7 +362,7 @@ void upsdrv_initinfo(void) /* send request, read answer */ chars_rcvd = mge_command(buf, sizeof(buf), item->cmd); - + if ( chars_rcvd < 1 || buf[0] == '?' ) { item->ok = FALSE; upsdebugx(1, "initinfo: %s unavailable", item->type); @@ -406,7 +407,7 @@ void upsdrv_updateinfo(void) char buf[BUFFLEN]; char infostr[32]; int status_ok; - int bytes_rcvd; + ssize_t bytes_rcvd; mge_info_item_t *item; /* make sure that communication is enabled */ @@ -427,7 +428,7 @@ void upsdrv_updateinfo(void) } /* Don't overload old units (at startup) */ - if ( (unsigned int)time(NULL) <= (unsigned int)(lastpoll + poll_interval) ) + if ( time(NULL) <= (lastpoll + poll_interval) ) return; /* update all other ok variables */ @@ -439,7 +440,7 @@ void upsdrv_updateinfo(void) if ( item->ok ) { /* send request, read answer */ bytes_rcvd = mge_command(buf, sizeof(buf), item->cmd); - + if ( bytes_rcvd > 0 && buf[0] != '?' ) { extract_info(buf, item, infostr, sizeof(infostr)); dstate_setinfo(item->type, "%s", infostr); @@ -470,10 +471,10 @@ void upsdrv_shutdown(void) if (sdtype == SD_RETURN) { /* enable automatic restart */ mge_command(buf, sizeof(buf), "Sx 5"); - + upslogx(LOG_INFO, "UPS response to Automatic Restart was %s", buf); } - + /* Only call the effective shutoff if restart is ok */ /* or if we need only a stayoff... */ if (!strcmp(buf, "OK") || (sdtype == SD_STAYOFF)) { @@ -508,7 +509,7 @@ int instcmd(const char *cmdname, const char *extra) { mge_command(temp, sizeof(temp), "Bx 1"); upsdebugx(2, "UPS response to %s was %s", cmdname, temp); - + if(strcmp(temp, "OK")) return STAT_INSTCMD_UNKNOWN; else @@ -520,7 +521,7 @@ int instcmd(const char *cmdname, const char *extra) { mge_command(temp, sizeof(temp), "Sx 129"); upsdebugx(2, "UPS response to %s was %s", cmdname, temp); - + if(strcmp(temp, "OK")) return STAT_INSTCMD_UNKNOWN; else @@ -533,13 +534,13 @@ int instcmd(const char *cmdname, const char *extra) sdtype = SD_STAYOFF; upsdrv_shutdown(); } - + if (!strcasecmp(cmdname, "shutdown.return")) { sdtype = SD_RETURN; upsdrv_shutdown(); } - + /* Power Off [all] plugs */ if (!strcasecmp(cmdname, "load.off")) { @@ -552,10 +553,10 @@ int instcmd(const char *cmdname, const char *extra) else { mge_command(temp, sizeof(temp), "Wx 0"); - upsdebugx(2, "UPS response to %s was %s", cmdname, temp); + upsdebugx(2, "UPS response to %s was %s", cmdname, temp); if(strcmp(temp, "OK")) return STAT_INSTCMD_UNKNOWN; - else + else return STAT_INSTCMD_HANDLED; } } @@ -572,16 +573,16 @@ int instcmd(const char *cmdname, const char *extra) else { mge_command(temp, sizeof(temp), "Wx 1"); - upsdebugx(2, "UPS response to %s was %s", cmdname, temp); + upsdebugx(2, "UPS response to %s was %s", cmdname, temp); if(strcmp(temp, "OK")) return STAT_INSTCMD_UNKNOWN; - else + else return STAT_INSTCMD_HANDLED; } } /* Switch on/off Maintenance Bypass */ - if ((!strcasecmp(cmdname, "bypass.start")) + if ((!strcasecmp(cmdname, "bypass.start")) || (!strcasecmp(cmdname, "bypass.stop"))) { /* TODO: add control on bypass value */ @@ -598,17 +599,17 @@ int instcmd(const char *cmdname, const char *extra) /* Enable Maintenance Bypass */ mge_command(temp, sizeof(temp), "Px 3"); } - + upsdebugx(2, "UPS response to %s was %s", cmdname, temp); - + if(strcmp(temp, "OK")) return STAT_INSTCMD_UNKNOWN; else return STAT_INSTCMD_HANDLED; } } - - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -619,9 +620,9 @@ int setvar(const char *varname, const char *val) { char temp[BUFFLEN]; char cmd[15]; - + /* TODO : add some controls */ - + if(info_variable_ok(varname)) { /* format command */ @@ -633,7 +634,7 @@ int setvar(const char *varname, const char *val) upslogx(LOG_INFO, "setvar: UPS response to Set %s to %s was %s", varname, val, temp); } else upsdebugx(1, "setvar: Variable %s not supported by UPS", varname); - + return STAT_SET_UNKNOWN; } @@ -653,16 +654,17 @@ static void disable_ups_comm(void) static void enable_ups_comm(void) { char buf[8]; - + + /* send Z twice --- speeds up re-connect */ + mge_command(NULL, 0, "Z"); + mge_command(NULL, 0, "Z"); /* only enable communication if needed! */ if ( mge_command(buf, 8, "Si") <= 0) { - mge_command(NULL, 0, "Z"); /* send Z twice --- speeds up re-connect */ - mge_command(NULL, 0, "Z"); mge_command(NULL, 0, "Ax 1"); usleep(MGE_CONNECT_DELAY); } - + ser_flush_in(upsfd, "?\r\n", nut_debug_level); } @@ -671,16 +673,25 @@ static void enable_ups_comm(void) /* extract information from buffer in: buf : reply from UPS item : INFO item queried - out: infostr: to be placed in INFO_ variable + out: infostr: to be placed in INFO_ variable NOTE: buf="?" must be handled before calling extract_info buf is changed inspite of const !!!!! */ -static void extract_info(const char *buf, const mge_info_item_t *item, - char *infostr, int infolen) +static void extract_info(const char *buf, const mge_info_item_t *item, + char *infostr, size_t infolen) { /* initialize info string */ infostr[0] = '\0'; +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif /* write into infostr with proper formatting */ if ( strpbrk(item->fmt, "feEgG") ) { /* float */ snprintf(infostr, infolen, item->fmt, @@ -691,6 +702,9 @@ static void extract_info(const char *buf, const mge_info_item_t *item, } else { snprintf(infostr, infolen, item->fmt, buf); } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif } @@ -700,9 +714,9 @@ static void extract_info(const char *buf, const mge_info_item_t *item, /* get system status, at least: OB, OL, LB calls set_status appropriately tries MAXTRIES times - returns non-nil if successful + returns non-nil if successful - NOTE: MGE counts bytes/chars the opposite way as C, + NOTE: MGE counts bytes/chars the opposite way as C, see mge-utalk manpage. If status commands send two data items, these are separated by a space, so the elements of the second item are in buf[16..9]. @@ -710,13 +724,13 @@ static void extract_info(const char *buf, const mge_info_item_t *item, static int get_ups_status(void) { - char buf[BUFFLEN]; + char buf[BUFFLEN]; int rb_set= FALSE; /* has RB flag been set ? */ int over_set= FALSE; /* has OVER flag been set ? */ int tries = 0; int ok = FALSE; - int bytes_rcvd = 0; - + ssize_t bytes_rcvd = 0; + do { /* Check if we are asked to stop (reactivity++) */ if (exit_flag != 0) @@ -746,7 +760,7 @@ static int get_ups_status(void) if (buf[3] == '1') { rb_set = TRUE; - status_set("RB"); + status_set("RB"); } /* buf[2] not used */ if (buf[1] == '1') @@ -763,10 +777,10 @@ static int get_ups_status(void) if ( strlen(buf) > 7 ) { if ( !rb_set && ( buf[7] == '1' || buf[3] == '1' ) ) status_set("RB"); - + if (buf[1] == '1') status_set("CHRG"); - + if (buf[0] == '1') status_set("DISCHRG"); } /* if strlen */ @@ -796,7 +810,7 @@ static int get_ups_status(void) /* This is not the OFF status! if ( !(buf[8] == '1') ) status_set("OFF"); */ - } /* if strlen */ + } /* if strlen */ /* Bypass status */ mge_command(buf, sizeof(buf), "Ps"); @@ -810,39 +824,39 @@ static int get_ups_status(void) if (buf[6] == '1') status_set("BYPASS"); } /* if strlen */ - + } while ( !ok && tries++ < MAXTRIES ); status_commit(); - + return ok; } /* --------------------------------------------------------------- */ /* return proper variable "ok" given INFO_ type */ - -static bool_t info_variable_ok(const char *type) + +static bool_t info_variable_ok(const char *type) { mge_info_item_t *item = mge_info ; - + while ( strcasecmp(item->type, type )) item++; - + return item->ok; } /* --------------------------------------------------------------- */ /* return proper variable "cmd" given INFO_ type */ - -static const char *info_variable_cmd(const char *type) + +static const char *info_variable_cmd(const char *type) { mge_info_item_t *item = mge_info ; - + while ( strcasecmp(item->type, type )) item++; - + return item->cmd; } @@ -856,59 +870,69 @@ static const char *info_variable_cmd(const char *type) returns : no of chars received, -1 if error */ -static int mge_command(char *reply, int replylen, const char *fmt, ...) +static ssize_t mge_command(char *reply, size_t replylen, const char *fmt, ...) { const char *p; char command[BUFFLEN]; - int bytes_sent = 0; - int bytes_rcvd = 0; + ssize_t bytes_sent = 0; + ssize_t bytes_rcvd = 0; int ret; va_list ap; /* build command string */ va_start(ap, fmt); - +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif ret = vsnprintf(command, sizeof(command), fmt, ap); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif if ((ret < 1) || (ret >= (int) sizeof(command))) upsdebugx(4, "mge_command: command truncated"); - + va_end(ap); - /* Delay a bit to avoid overlap of a previous answer */ - usleep(100000); - + /* Delay a bit to avoid overlap of a previous answer (500 ms), as per + * http://old.networkupstools.org/protocols/mge/9261zwfa.pdf § 6.1. Timings */ + usleep(500000); + /* flush received, unread data */ tcflush(upsfd, TCIFLUSH); - + /* send command */ for (p = command; *p; p++) { - if ( isprint(*p & 0xFF) ) + if ( isprint((unsigned char)*p & 0xFF) ) upsdebugx(4, "mge_command: sending [%c]", *p); else upsdebugx(4, "mge_command: sending [%02X]", *p); if (write(upsfd, p, 1) != 1) return -1; - + bytes_sent++; usleep(MGE_CHAR_DELAY); } /* send terminating string */ - if (MGE_COMMAND_ENDCHAR) { - for (p = MGE_COMMAND_ENDCHAR; *p; p++) { - if ( isprint(*p & 0xFF) ) - upsdebugx(4, "mge_command: sending [%c]", *p); - else - upsdebugx(4, "mge_command: sending [%02X]", *p); + for (p = MGE_COMMAND_ENDCHAR; *p; p++) { + if ( isprint((unsigned char)*p & 0xFF) ) + upsdebugx(4, "mge_command: sending [%c]", *p); + else + upsdebugx(4, "mge_command: sending [%02X]", *p); - if (write(upsfd, p, 1) != 1) - return -1; + if (write(upsfd, p, 1) != 1) + return -1; - bytes_sent++; - usleep(MGE_CHAR_DELAY); - } + bytes_sent++; + usleep(MGE_CHAR_DELAY); } if ( !reply ) @@ -919,7 +943,7 @@ static int mge_command(char *reply, int replylen, const char *fmt, ...) bytes_rcvd = ser_get_line(upsfd, reply, replylen, MGE_REPLY_ENDCHAR, MGE_REPLY_IGNCHAR, 3, 0); - upsdebugx(4, "mge_command: received %d byte(s)", bytes_rcvd); + upsdebugx(4, "mge_command: received %zd byte(s)", bytes_rcvd); return bytes_rcvd; } diff --git a/drivers/mge-utalk.h b/drivers/mge-utalk.h index a90f815..67b1135 100644 --- a/drivers/mge-utalk.h +++ b/drivers/mge-utalk.h @@ -3,7 +3,7 @@ * Copyright (C) 2002 - 2005 * Arnaud Quette & * Hans Ekkehard Plesser - * Martin Loyer + * Martin Loyer * Patrick Agrain * Nicholas Reilly * Dave Abbott @@ -28,6 +28,9 @@ * */ +#ifndef NUT_MGE_UTALK_H_SEEN +#define NUT_MGE_UTALK_H_SEEN 1 + /* --------------------------------------------------------------- */ /* Default Values for UPS Variables */ /* --------------------------------------------------------------- */ @@ -36,7 +39,7 @@ /* delay between return of utility power and powering up of load (in MINUTES) */ #define DEFAULT_ONDELAY 1 -#define DEFAULT_OFFDELAY 20 /* delay before power off, in SECONDS */ +#define DEFAULT_OFFDELAY 20 /* delay before power off, in SECONDS */ #define MIN_CONFIRM_TIME 3 /* shutdown must be confirmed in */ #define MAX_CONFIRM_TIME 15 /* this interval */ @@ -49,7 +52,7 @@ typedef struct { const char *finalname; } models_name_t; -models_name_t Si1_models_names [] = +static models_name_t Si1_models_names [] = { /* Pulsar EX */ { "Pulsar EX7", "Pulsar EX 7" }, @@ -142,10 +145,10 @@ static mge_model_info_t mge_model[] = { /* Multiplier Tables */ /* --------------------------------------------------------------- */ -/* First index : Table number, fetched with "Ai" command - * Second index: unit, as in enum above +/* First index : Table number, fetched with "Ai" command + * Second index: unit, as in enum above * NOTE: - * - to make the table index the same as the MGE table number, + * - to make the table index the same as the MGE table number, * dummy table row multiplier[0][] is inserted * - unit MIN2SEC is used to convert values in minutes sent by * the UPS (WAKEDELAY) to seconds @@ -153,11 +156,11 @@ static mge_model_info_t mge_model[] = { */ /* units in multiplier table */ -typedef enum eunits +typedef enum eunits { VOLT = 0, AMPERE, HERTZ, VOLTAMP, WATT, DEGCELS, MIN2SEC, NONE } units_t; static const double multiplier[4][8] = { -/* V A Hz VA W C MIN2SEC NONE */ +/* V A Hz VA W C MIN2SEC NONE */ { 1 , 1 , 1 , 1, 1, 1, 60, 1 }, { 1 , 1 , 0.1 , 1000, 1000, 1, 60, 1 }, { 0.01, 0.01, 1 , 1, 1, 1, 60, 1 }, @@ -188,7 +191,7 @@ typedef enum ebool { FALSE=0, TRUE } bool_t; typedef struct { const char *type; /* INFO_* element */ int flags; /* INFO-element flags to set in addinfo */ - int length; /* INFO-element length of strings */ + int length; /* INFO-element length of strings */ const char *cmd; /* UPS command string to requets element */ const char *fmt; /* printf format string for INFO entry */ units_t unit; /* unit of measurement, or NONE */ @@ -196,7 +199,7 @@ typedef struct { } mge_info_item_t; /* Array containing information to translate between UTalk and NUT info - * NOTE: + * NOTE: * - Array is terminated by element with type NULL. * - Essential INFO items (_MFR, _MODEL, _FIRMWARE, _STATUS) are * handled separately. @@ -231,5 +234,7 @@ static mge_info_item_t mge_info[] = { { "input.transfer.high", ST_FLAG_RW | ST_FLAG_STRING, 5, "Eu ?", "%05.1f", VOLT, TRUE }, { "input.transfer.trim.high", ST_FLAG_RW | ST_FLAG_STRING, 5, "Eu ?", "%05.1f", VOLT, TRUE }, /* terminating element */ - { NULL, 0, 0, "\0", "\0", NONE, FALSE } + { NULL, 0, 0, "\0", "\0", NONE, FALSE } }; + +#endif /* NUT_MGE_UTALK_H_SEEN */ diff --git a/drivers/mge-xml.c b/drivers/mge-xml.c index c64f3eb..4177b69 100644 --- a/drivers/mge-xml.c +++ b/drivers/mge-xml.c @@ -2,7 +2,8 @@ Copyright (C) 2008-2009 Arjen de Korte - 2009 Arnaud Quette + 2009-2021 Eaton (author: Arnaud Quette ) + 2017 Eaton (author: Jim Klimov ) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,6 +20,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" /* must be the first header */ + #include #include #include @@ -30,8 +33,10 @@ #include "netxml-ups.h" #include "mge-xml.h" +#include "main.h" /* for testvar() */ + +#define MGE_XML_VERSION "MGEXML/0.36" -#define MGE_XML_VERSION "MGEXML/0.22" #define MGE_XML_INITUPS "/" #define MGE_XML_INITINFO "/mgeups/product.xml /product.xml /ws/product.xml" @@ -40,11 +45,27 @@ static int mge_ambient_value = 0; +/* The number of phases is not present in XML data as a separate node, + * but we can infer it from presence of non-zero data on several + * per-line nodes. */ +static int + inited_phaseinfo_in = 0, + inited_phaseinfo_bypass = 0, + inited_phaseinfo_out = 0, + num_inphases = -1, + num_bypassphases = -1, + num_outphases = -1; + static char mge_scratch_buf[256]; static char var[128]; static char val[128]; +static int mge_shutdown_pending = 0; + +/* This flag flips to 0 when/if we post the detailed deprecation message */ +static int mge_report_deprecation__convert_deci = 1; + typedef enum { ROOTPARENT = NE_XML_STATEROOT, @@ -110,9 +131,9 @@ typedef struct { NULL if no further processing is required) */ } xml_info_t; -static const char *online_info(const char *val) +static const char *online_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(ONLINE); } else { STATUS_CLR(ONLINE); @@ -121,10 +142,14 @@ static const char *online_info(const char *val) return NULL; } -static const char *discharging_info(const char *val) +static const char *discharging_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(DISCHRG); + /* Workaround NMC bug: both charging and discharging set to 1 */ + if(STATUS_BIT(CHRG)) { + STATUS_CLR(CHRG); + } } else { STATUS_CLR(DISCHRG); } @@ -132,9 +157,9 @@ static const char *discharging_info(const char *val) return NULL; } -static const char *charging_info(const char *val) +static const char *charging_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(CHRG); } else { STATUS_CLR(CHRG); @@ -143,9 +168,9 @@ static const char *charging_info(const char *val) return NULL; } -static const char *lowbatt_info(const char *val) +static const char *lowbatt_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(LOWBATT); } else { STATUS_CLR(LOWBATT); @@ -154,9 +179,9 @@ static const char *lowbatt_info(const char *val) return NULL; } -static const char *overload_info(const char *val) +static const char *overload_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(OVERLOAD); } else { STATUS_CLR(OVERLOAD); @@ -165,9 +190,9 @@ static const char *overload_info(const char *val) return NULL; } -static const char *replacebatt_info(const char *val) +static const char *replacebatt_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(REPLACEBATT); } else { STATUS_CLR(REPLACEBATT); @@ -176,9 +201,9 @@ static const char *replacebatt_info(const char *val) return NULL; } -static const char *trim_info(const char *val) +static const char *trim_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(TRIM); } else { STATUS_CLR(TRIM); @@ -187,9 +212,9 @@ static const char *trim_info(const char *val) return NULL; } -static const char *boost_info(const char *val) +static const char *boost_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(BOOST); } else { STATUS_CLR(BOOST); @@ -198,9 +223,9 @@ static const char *boost_info(const char *val) return NULL; } -static const char *bypass_aut_info(const char *val) +static const char *bypass_aut_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(BYPASSAUTO); } else { STATUS_CLR(BYPASSAUTO); @@ -209,9 +234,9 @@ static const char *bypass_aut_info(const char *val) return NULL; } -static const char *bypass_man_info(const char *val) +static const char *bypass_man_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(BYPASSMAN); } else { STATUS_CLR(BYPASSMAN); @@ -220,9 +245,9 @@ static const char *bypass_man_info(const char *val) return NULL; } -static const char *off_info(const char *val) +static const char *off_info(const char *arg_val) { - if (val[0] == '0') { + if (arg_val[0] == '0') { STATUS_SET(OFF); } else { STATUS_CLR(OFF); @@ -234,9 +259,9 @@ static const char *off_info(const char *val) /* note: this value is reverted (0=set, 1=not set). We report "battery not installed" rather than "battery installed", so that devices that don't implement this variable have a battery by default */ -static const char *nobattery_info(const char *val) +static const char *nobattery_info(const char *arg_val) { - if (val[0] == '0') { + if (arg_val[0] == '0') { STATUS_SET(NOBATTERY); } else { STATUS_CLR(NOBATTERY); @@ -245,9 +270,9 @@ static const char *nobattery_info(const char *val) return NULL; } -static const char *fanfail_info(const char *val) +static const char *fanfail_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(FANFAIL); } else { STATUS_CLR(FANFAIL); @@ -255,10 +280,11 @@ static const char *fanfail_info(const char *val) return NULL; } -/* -static const char *shutdownimm_info(const char *val) + +#if 0 +static const char *shutdownimm_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(SHUTDOWNIMM); } else { STATUS_CLR(SHUTDOWNIMM); @@ -266,10 +292,11 @@ static const char *shutdownimm_info(const char *val) return NULL; } - */ -static const char *overheat_info(const char *val) +#endif + +static const char *overheat_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(OVERHEAT); } else { STATUS_CLR(OVERHEAT); @@ -278,9 +305,9 @@ static const char *overheat_info(const char *val) return NULL; } -static const char *commfault_info(const char *val) +static const char *commfault_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(COMMFAULT); } else { STATUS_CLR(COMMFAULT); @@ -289,9 +316,9 @@ static const char *commfault_info(const char *val) return NULL; } -static const char *internalfailure_info(const char *val) +static const char *internalfailure_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(INTERNALFAULT); } else { STATUS_CLR(INTERNALFAULT); @@ -300,9 +327,9 @@ static const char *internalfailure_info(const char *val) return NULL; } -static const char *battvoltlo_info(const char *val) +static const char *battvoltlo_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(BATTVOLTLO); } else { STATUS_CLR(BATTVOLTLO); @@ -311,9 +338,9 @@ static const char *battvoltlo_info(const char *val) return NULL; } -static const char *battvolthi_info(const char *val) +static const char *battvolthi_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(BATTVOLTHI); } else { STATUS_CLR(BATTVOLTHI); @@ -322,9 +349,9 @@ static const char *battvolthi_info(const char *val) return NULL; } -static const char *chargerfail_info(const char *val) +static const char *chargerfail_info(const char *arg_val) { - if ((val[0] == '1') || !strncasecmp(val, "Yes", 3)) { + if ((arg_val[0] == '1') || !strncasecmp(arg_val, "Yes", 3)) { STATUS_SET(CHARGERFAIL); } else { STATUS_CLR(CHARGERFAIL); @@ -333,9 +360,9 @@ static const char *chargerfail_info(const char *val) return NULL; } -static const char *vrange_info(const char *val) +static const char *vrange_info(const char *arg_val) { - if ((val[0] == '1') || !strncasecmp(val, "Yes", 3)) { + if ((arg_val[0] == '1') || !strncasecmp(arg_val, "Yes", 3)) { STATUS_SET(VRANGE); } else { STATUS_CLR(VRANGE); @@ -344,9 +371,9 @@ static const char *vrange_info(const char *val) return NULL; } -static const char *frange_info(const char *val) +static const char *frange_info(const char *arg_val) { - if ((val[0] == '1') || !strncasecmp(val, "Yes", 3)) { + if ((arg_val[0] == '1') || !strncasecmp(arg_val, "Yes", 3)) { STATUS_SET(FRANGE); } else { STATUS_CLR(FRANGE); @@ -355,9 +382,9 @@ static const char *frange_info(const char *val) return NULL; } -static const char *fuse_fault_info(const char *val) +static const char *fuse_fault_info(const char *arg_val) { - if (val[0] == '1') { + if (arg_val[0] == '1') { STATUS_SET(FUSEFAULT); } else { STATUS_CLR(FUSEFAULT); @@ -366,88 +393,112 @@ static const char *fuse_fault_info(const char *val) return NULL; } -static const char *yes_no_info(const char *val) +static const char *yes_no_info(const char *arg_val) { - switch(val[0]) + switch(arg_val[0]) { case '1': return "yes"; case '0': return "no"; default: - upsdebugx(2, "%s: unexpected value [%s]", __func__, val); + upsdebugx(2, "%s: unexpected value [%s]", __func__, arg_val); return ""; } } -static const char *on_off_info(const char *val) +static const char *on_off_info(const char *arg_val) { - switch(val[0]) + switch(arg_val[0]) { case '1': return "on"; case '0': return "off"; default: - upsdebugx(2, "%s: unexpected value [%s]", __func__, val); + upsdebugx(2, "%s: unexpected value [%s]", __func__, arg_val); return ""; } } -static const char *convert_deci(const char *val) +static const char *convert_deci(const char *arg_val) { - snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.1f", 0.1 * (float)atoi(val)); + /* Note: this routine was needed for original MGE devices, before the company + * was bought out and split in 2007 between Eaton (1ph devices) and Schneider + * (3ph devices). Those firmwares back when the driver was written apparently + * served 10x the measured values. Not sure if any such units are in service + * now (with same FW, and with no upgrade path). Reign of XML/PDC is waning. + * For currently known NetXML servers, the value served is good without more + * conversions. If older devices pop up in the field, we can add an estimation + * by e.g. reported voltage and amps (to be an order of magnitude for power). + * Alternately we can look at model names and/or firmware versions or release + * dates, if we get those and if we know enough to map them to either logic. */ - return mge_scratch_buf; + if (testvar("do_convert_deci")) { + /* Old code for old devices: */ + if (mge_report_deprecation__convert_deci) { + upslogx(LOG_NOTICE, "%s() is now deprecated, so values from XML are normally not decimated. This driver instance has however configured do_convert_deci in your ups.conf, so this behavior for old MGE NetXML-capable devices is preserved.", __func__); + mge_report_deprecation__convert_deci = 0; + } + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.1f", 0.1 * (float)(atoi(arg_val))); + return mge_scratch_buf; + } + + if (mge_report_deprecation__convert_deci) { + upslogx(LOG_NOTICE, "%s() is now deprecated, so values from XML are not decimated. If you happen to have an old MGE NetXML-capable device that now shows measurements 10x too big, and a firmware update does not solve this, please inform NUT devs via the issue tracker at %s with details about your hardware and firmware versions. Also try to enable do_convert_deci in your ups.conf", __func__, PACKAGE_BUGREPORT ); + mge_report_deprecation__convert_deci = 0; + } + upsdebugx(5, "%s() is now deprecated, so value '%s' is not decimated. If this change broke your setup, please see details logged above.", __func__, arg_val); + return arg_val; } /* Ignore a zero value if the UPS is not switched off */ -static const char *ignore_if_zero(const char *val) +static const char *ignore_if_zero(const char *arg_val) { - if (atoi(val) == 0) { + if (atoi(arg_val) == 0) { return NULL; } - return convert_deci(val); + return convert_deci(arg_val); } /* Set the 'ups.date' from the combined value * (ex. 2008/03/01 15:23:26) and return the time */ -static const char *split_date_time(const char *val) +static const char *split_date_time(const char *arg_val) { char *last = NULL; - snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", val); + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%s", arg_val); dstate_setinfo("ups.date", "%s", strtok_r(mge_scratch_buf, " -", &last)); return strtok_r(NULL, " ", &last); } -static const char *url_convert(const char *val) +static const char *url_convert(const char *arg_val) { char buf[256], *last = NULL; - snprintf(buf, sizeof(buf), "%s", val); + snprintf(buf, sizeof(buf), "%s", arg_val); snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "/%s", strtok_r(buf, " \r\n\t", &last)); return mge_scratch_buf; } -static const char *mge_battery_capacity(const char *val) +static const char *mge_battery_capacity(const char *arg_val) { - snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.2f", (float)atoi(val) / 3600); + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.2f", (float)(atoi(arg_val)) / 3600.0); return mge_scratch_buf; } -static const char *mge_powerfactor_conversion(const char *val) +static const char *mge_powerfactor_conversion(const char *arg_val) { - snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.2f", (float)atoi(val) / 100); + snprintf(mge_scratch_buf, sizeof(mge_scratch_buf), "%.2f", (float)(atoi(arg_val)) / 100.0); return mge_scratch_buf; } -static const char *mge_beeper_info(const char *val) +static const char *mge_beeper_info(const char *arg_val) { - switch (atoi(val)) + switch (atoi(arg_val)) { case 1: return "disabled"; @@ -459,9 +510,9 @@ static const char *mge_beeper_info(const char *val) return NULL; } -static const char *mge_upstype_conversion(const char *val) +static const char *mge_upstype_conversion(const char *arg_val) { - switch (atoi(val)) + switch (atoi(arg_val)) { case 1: return "offline / line interactive"; @@ -477,9 +528,9 @@ static const char *mge_upstype_conversion(const char *val) return NULL; } -static const char *mge_sensitivity_info(const char *val) +static const char *mge_sensitivity_info(const char *arg_val) { - switch (atoi(val)) + switch (atoi(arg_val)) { case 0: return "normal"; @@ -491,9 +542,10 @@ static const char *mge_sensitivity_info(const char *val) return NULL; } -static const char *mge_test_result_info(const char *val) +static const char *mge_test_result_info(const char *arg_val) { - switch (atoi(val)) + STATUS_CLR(CAL); + switch (atoi(arg_val)) { case 1: return "done and passed"; @@ -504,6 +556,7 @@ static const char *mge_test_result_info(const char *val) case 4: return "aborted"; case 5: + STATUS_SET(CAL); return "in progress"; case 6: return "no test initiated"; @@ -513,35 +566,354 @@ static const char *mge_test_result_info(const char *val) return NULL; } -static const char *mge_ambient_info(const char *val) +static const char *mge_ambient_info(const char *arg_val) { switch (mge_ambient_value) { case 1: - return val; + return arg_val; default: return NULL; } } -static const char *mge_timer_shutdown(const char *val) +static const char *mge_drycontact_info(const char *arg_val) { - const char *delay = dstate_getinfo("ups.delay.shutdown"); + /* these values should theoretically be obtained through + * Environment.Input[1].State[x].Description + * Examples: + * open + * closed + */ + switch (atoi(arg_val)) + { + case 0: + return "opened"; + case 1: + return "closed"; + default: + return NULL; + } +} - if ((delay) && (atoi(val) > -1) && (atoi(val) < atoi(delay))) { +static const char *mge_timer_shutdown(const char *delay_before_shutoff) +{ + if (atoi(delay_before_shutoff) > -1 ) { STATUS_SET(SHUTDOWNIMM); + mge_shutdown_pending = 1; + + if( atoi(delay_before_shutoff) > shutdown_duration ) { + STATUS_CLR(SHUTDOWNIMM); + mge_shutdown_pending = 0; + } } else { STATUS_CLR(SHUTDOWNIMM); + mge_shutdown_pending = 0; } return val; } +static const char *mge_shutdown_imminent(const char *arg_val) +{ + const int shutdown_delay = atoi(arg_val); + + /* shutdown is already managed by mge_timer_shutdown, give up */ + if(mge_shutdown_pending) { + return NULL; + } + + /* We may have "NONE" or "-1" or ?? as value + * We also double check both the string and numeric values to be zero!*/ + if ((arg_val) && (arg_val[0] == '0') && (shutdown_delay == 0)) { + STATUS_SET(SHUTDOWNIMM); + } else { + STATUS_CLR(SHUTDOWNIMM); + } + + return NULL; +} + static xml_info_t mge_xml2nut[] = { + /* NMC configuration (mapped 1:1 for now) */ + { "device.contact", ST_FLAG_RW, 0, "System.Contact", 0, 0, NULL }, + { "device.location", ST_FLAG_RW, 0, "System.Location", 0, 0, NULL }, + /* Not used for now; might however be used in future for history & stats collection + { "System.History.Log.Interval", ST_FLAG_RW, 0, "System.History.Log.Interval", 0, 0, NULL }, + */ +#if (0) /* not interresting for NUT */ + { "System.Environment.Log.Interval", ST_FLAG_RW, 0, "System.Environment.Log.Interval", 0, 0, NULL }, + { "System.Outlet[1].iName", ST_FLAG_RW, 0, "System.Outlet[1].iName", 0, 0, NULL }, + /* Mapped as ups.delay.shutdown + { "System.ShutdownDuration", ST_FLAG_RW, 0, "System.ShutdownDuration", 0, 0, NULL }, + */ + { "System.ShutdownTimerSelected", ST_FLAG_RW, 0, "System.ShutdownTimerSelected", 0, 0, NULL }, + { "System.ShutdownTimer", ST_FLAG_RW, 0, "System.ShutdownTimer", 0, 0, NULL }, + /* Mapped as battery.runtime.low + { "System.RunTimeToEmptyLimit", ST_FLAG_RW, 0, "System.RunTimeToEmptyLimit", 0, 0, NULL }, + */ + { "System.RemainingCapacityLimit", ST_FLAG_RW, 0, "System.RemainingCapacityLimit", 0, 0, NULL }, + { "System.RestartLevel", ST_FLAG_RW, 0, "System.RestartLevel", 0, 0, NULL }, + { "System.Outlet[2].iName", ST_FLAG_RW, 0, "System.Outlet[2].iName", 0, 0, NULL }, + /* Mapped as outlet.1.delay.shutdown + { "System.Outlet[2].ShutdownDuration", ST_FLAG_RW, 0, "System.Outlet[2].ShutdownDuration", 0, 0, NULL }, + */ + { "System.Outlet[2].ShutdownTimer", ST_FLAG_RW, 0, "System.Outlet[2].ShutdownTimer", 0, 0, NULL }, + { "System.Outlet[2].StartupTimer", ST_FLAG_RW, 0, "System.Outlet[2].StartupTimer", 0, 0, NULL }, + { "System.Outlet[2].RemainingCapacityLimit", ST_FLAG_RW, 0, "System.Outlet[2].RemainingCapacityLimit", 0, 0, NULL }, + /* For future extension, and support of shutdown on load segment + * { "System.Outlet[2].RunTimeToShutdown", ST_FLAG_RW, 0, "System.Outlet[2].RunTimeToShutdown", 0, 0, NULL }, */ + { "System.Outlet[3].iName", ST_FLAG_RW, 0, "System.Outlet[3].iName", 0, 0, NULL }, + /* Mapped as outlet.2.delay.shutdown + { "System.Outlet[3].ShutdownDuration", ST_FLAG_RW, 0, "System.Outlet[3].ShutdownDuration", 0, 0, NULL }, + */ + { "System.Outlet[3].ShutdownTimer", ST_FLAG_RW, 0, "System.Outlet[3].ShutdownTimer", 0, 0, NULL }, + { "System.Outlet[3].StartupTimer", ST_FLAG_RW, 0, "System.Outlet[3].StartupTimer", 0, 0, NULL }, + { "System.Outlet[3].RemainingCapacityLimit", ST_FLAG_RW, 0, "System.Outlet[3].RemainingCapacityLimit", 0, 0, NULL }, + /* For future extension, and support of shutdown on load segment + * { "System.Outlet[3].RunTimeToShutdown", ST_FLAG_RW, 0, "System.Outlet[3].RunTimeToShutdown", 0, 0, NULL }, */ + { "System.Outlet[1].OffDelay", ST_FLAG_RW, 0, "System.Outlet[1].OffDelay", 0, 0, NULL }, + { "System.Outlet[1].Toggle", ST_FLAG_RW, 0, "System.Outlet[1].Toggle", 0, 0, NULL }, + { "System.Outlet[1].OnDelay", ST_FLAG_RW, 0, "System.Outlet[1].OnDelay", 0, 0, NULL }, + { "System.Outlet[2].OffDelay", ST_FLAG_RW, 0, "System.Outlet[2].OffDelay", 0, 0, NULL }, + { "System.Outlet[2].Toggle", ST_FLAG_RW, 0, "System.Outlet[2].Toggle", 0, 0, NULL }, + { "System.Outlet[2].OnDelay", ST_FLAG_RW, 0, "System.Outlet[2].OnDelay", 0, 0, NULL }, + { "System.Outlet[3].OffDelay", ST_FLAG_RW, 0, "System.Outlet[3].OffDelay", 0, 0, NULL }, + { "System.Outlet[3].Toggle", ST_FLAG_RW, 0, "System.Outlet[3].Toggle", 0, 0, NULL }, + { "System.Outlet[3].OnDelay", ST_FLAG_RW, 0, "System.Outlet[3].OnDelay", 0, 0, NULL }, + { "System.Login", ST_FLAG_RW, 0, "System.Login", 0, 0, NULL }, + { "System.Password", ST_FLAG_RW, 0, "System.Password", 0, 0, NULL }, + { "System.Security", ST_FLAG_RW, 0, "System.Security", 0, 0, NULL }, + { "System.FirmwareUpgrade", ST_FLAG_RW, 0, "System.FirmwareUpgrade", 0, 0, NULL }, + { "System.Network.SNMP.ReadCommunity", ST_FLAG_RW, 0, "System.Network.SNMP.ReadCommunity", 0, 0, NULL }, + { "System.Network.SNMP.ReadCommunityName", 0, 0, "System.Network.SNMP.ReadCommunityName", 0, 0, NULL }, + { "System.Network.SNMP.ReadCommunitySecurityLevel", 0, 0, "System.Network.SNMP.ReadCommunitySecurityLevel", 0, 0, NULL }, + { "System.Network.SNMP.ReadCommunitySecurityRight", 0, 0, "System.Network.SNMP.ReadCommunitySecurityRight", 0, 0, NULL }, + { "System.Network.SNMP.WriteCommunity", ST_FLAG_RW, 0, "System.Network.SNMP.WriteCommunity", 0, 0, NULL }, + { "System.Network.SNMP.WriteCommunityName", 0, 0, "System.Network.SNMP.WriteCommunityName", 0, 0, NULL }, + { "System.Network.SNMP.WriteCommunitySecurityLevel", 0, 0, "System.Network.SNMP.WriteCommunitySecurityLevel", 0, 0, NULL }, + { "System.Network.SNMP.WriteCommunitySecurityRight", ST_FLAG_RW, 0, "System.Network.SNMP.WriteCommunitySecurityRight", 0, 0, NULL }, + { "System.Network.SNMP.Admin", ST_FLAG_RW, 0, "System.Network.SNMP.Admin", 0, 0, NULL }, + { "System.Network.SNMP.AdminPassword", ST_FLAG_RW, 0, "System.Network.SNMP.AdminPassword", 0, 0, NULL }, + { "System.Network.SNMP.AdminSecurityLevel", ST_FLAG_RW, 0, "System.Network.SNMP.AdminSecurityLevel", 0, 0, NULL }, + { "System.Network.SNMP.AdminSecurityRight", 0, 0, "System.Network.SNMP.AdminSecurityRight", 0, 0, NULL }, + { "System.Network.SNMP.User", ST_FLAG_RW, 0, "System.Network.SNMP.User", 0, 0, NULL }, + { "System.Network.SNMP.UserPassword", ST_FLAG_RW, 0, "System.Network.SNMP.UserPassword", 0, 0, NULL }, + { "System.Network.SNMP.UserSecurityLevel", ST_FLAG_RW, 0, "System.Network.SNMP.UserSecurityLevel", 0, 0, NULL }, + { "System.Network.SNMP.UserSecurityRight", 0, 0, "System.Network.SNMP.UserSecurityRight", 0, 0, NULL }, + { "System.Network.SNMP.NotificationUserName", ST_FLAG_RW, 0, "System.Network.SNMP.NotificationUserName", 0, 0, NULL }, + { "System.Network.SNMP.snmpVersion", ST_FLAG_RW, 0, "System.Network.SNMP.snmpVersion", 0, 0, NULL }, + { "System.Network.SNMP.engineBoots", 0, 0, "System.Network.SNMP.engineBoots", 0, 0, NULL }, + { "System.Network.Telnet.Access", ST_FLAG_RW, 0, "System.Network.Telnet.Access", 0, 0, NULL }, + { "System.Network.Telnet.Security", ST_FLAG_RW, 0, "System.Network.Telnet.Security", 0, 0, NULL }, + { "System.Network.Telnet.Console", ST_FLAG_RW, 0, "System.Network.Telnet.Console", 0, 0, NULL }, + { "System.Email.Sender", ST_FLAG_RW, 0, "System.Email.Sender", 0, 0, NULL }, + { "System.Email.Subject", ST_FLAG_RW, 0, "System.Email.Subject", 0, 0, NULL }, + { "System.Email.UPSName", ST_FLAG_RW, 0, "System.Email.UPSName", 0, 0, NULL }, + { "System.Email.Message", ST_FLAG_RW, 0, "System.Email.Message", 0, 0, NULL }, + { "System.Email.Localization", ST_FLAG_RW, 0, "System.Email.Localization", 0, 0, NULL }, + { "System.Email.EventName", ST_FLAG_RW, 0, "System.Email.EventName", 0, 0, NULL }, + { "System.Email[0].Recipient", ST_FLAG_RW, 0, "System.Email[0].Recipient", 0, 0, NULL }, + { "System.Email[0].Selected", ST_FLAG_RW, 0, "System.Email[0].Selected", 0, 0, NULL }, + { "System.Email[0].Enotify", ST_FLAG_RW, 0, "System.Email[0].Enotify", 0, 0, NULL }, + { "System.Email[0].Measures.Log", ST_FLAG_RW, 0, "System.Email[0].Measures.Log", 0, 0, NULL }, + { "System.Email[0].Events.Log", ST_FLAG_RW, 0, "System.Email[0].Events.Log", 0, 0, NULL }, + { "System.Email[0].SystemEvents.Log", ST_FLAG_RW, 0, "System.Email[0].SystemEvents.Log", 0, 0, NULL }, + { "System.Email[0].Environment.Log", ST_FLAG_RW, 0, "System.Email[0].Environment.Log", 0, 0, NULL }, + { "System.Email[0].Report.Periodicity", ST_FLAG_RW, 0, "System.Email[0].Report.Periodicity", 0, 0, NULL }, + { "System.Email[0].Report.Hour", ST_FLAG_RW, 0, "System.Email[0].Report.Hour", 0, 0, NULL }, + { "System.Email[0].Report.Next", ST_FLAG_RW, 0, "System.Email[0].Report.Next", 0, 0, NULL }, + { "System.Email[0].EventList.Discharging", ST_FLAG_RW, 0, "System.Email[0].EventList.Discharging", 0, 0, NULL }, + { "System.Email[0].EventList.ACPresent", ST_FLAG_RW, 0, "System.Email[0].EventList.ACPresent", 0, 0, NULL }, + { "System.Email[0].EventList.RunTimeToShutdown", ST_FLAG_RW, 0, "System.Email[0].EventList.RunTimeToShutdown", 0, 0, NULL }, + { "System.Email[0].EventList.BelowRemainingCapacityLimit", ST_FLAG_RW, 0, "System.Email[0].EventList.BelowRemainingCapacityLimit", 0, 0, NULL }, + { "System.Email[0].EventList.NeedReplacement.1", ST_FLAG_RW, 0, "System.Email[0].EventList.NeedReplacement.1", 0, 0, NULL }, + { "System.Email[0].EventList.NeedReplacement.0", ST_FLAG_RW, 0, "System.Email[0].EventList.NeedReplacement.0", 0, 0, NULL }, + { "System.Email[0].EventList.Overload.1", ST_FLAG_RW, 0, "System.Email[0].EventList.Overload.1", 0, 0, NULL }, + { "System.Email[0].EventList.Overload.0", ST_FLAG_RW, 0, "System.Email[0].EventList.Overload.0", 0, 0, NULL }, + { "System.Email[0].EventList.InternalFailure.1", ST_FLAG_RW, 0, "System.Email[0].EventList.InternalFailure.1", 0, 0, NULL }, + { "System.Email[0].EventList.InternalFailure.0", ST_FLAG_RW, 0, "System.Email[0].EventList.InternalFailure.0", 0, 0, NULL }, + { "System.Email[0].EventList.CommunicationLost.1", ST_FLAG_RW, 0, "System.Email[0].EventList.CommunicationLost.1", 0, 0, NULL }, + { "System.Email[0].EventList.CommunicationLost.0", ST_FLAG_RW, 0, "System.Email[0].EventList.CommunicationLost.0", 0, 0, NULL }, + { "System.Email[0].EventList.Charger.InternalFailure", ST_FLAG_RW, 0, "System.Email[0].EventList.Charger.InternalFailure", 0, 0, NULL }, + { "System.Email[0].EventList.Input[2].Used.1", ST_FLAG_RW, 0, "System.Email[0].EventList.Input[2].Used.1", 0, 0, NULL }, + { "System.Email[0].EventList.Input[2].Used.0", ST_FLAG_RW, 0, "System.Email[0].EventList.Input[2].Used.0", 0, 0, NULL }, + { "System.Email[0].EventList.PowerModule.RedundancyLost.1", ST_FLAG_RW, 0, "System.Email[0].EventList.PowerModule.RedundancyLost.1", 0, 0, NULL }, + { "System.Email[0].EventList.PowerModule.RedundancyLost.0", ST_FLAG_RW, 0, "System.Email[0].EventList.PowerModule.RedundancyLost.0", 0, 0, NULL }, + { "System.Email[0].EventList.PowerModule.ProtectionLost.1", ST_FLAG_RW, 0, "System.Email[0].EventList.PowerModule.ProtectionLost.1", 0, 0, NULL }, + { "System.Email[0].EventList.PowerModule.ProtectionLost.0", ST_FLAG_RW, 0, "System.Email[0].EventList.PowerModule.ProtectionLost.0", 0, 0, NULL }, + { "System.Email[0].EventList.FirmwareUpgrade", ST_FLAG_RW, 0, "System.Email[0].EventList.FirmwareUpgrade", 0, 0, NULL }, + { "System.Email[0].EventList.Environment.CommunicationLost", ST_FLAG_RW, 0, "System.Email[0].EventList.Environment.CommunicationLost", 0, 0, NULL }, + { "System.Email[0].EventList.Environment.Notify", ST_FLAG_RW, 0, "System.Email[0].EventList.Environment.Notify", 0, 0, NULL }, + { "System.Email[1].Recipient", ST_FLAG_RW, 0, "System.Email[1].Recipient", 0, 0, NULL }, + { "System.Email[1].Selected", ST_FLAG_RW, 0, "System.Email[1].Selected", 0, 0, NULL }, + { "System.Email[1].Enotify", ST_FLAG_RW, 0, "System.Email[1].Enotify", 0, 0, NULL }, + { "System.Email[1].Measures.Log", ST_FLAG_RW, 0, "System.Email[1].Measures.Log", 0, 0, NULL }, + { "System.Email[1].Events.Log", ST_FLAG_RW, 0, "System.Email[1].Events.Log", 0, 0, NULL }, + { "System.Email[1].SystemEvents.Log", ST_FLAG_RW, 0, "System.Email[1].SystemEvents.Log", 0, 0, NULL }, + { "System.Email[1].Environment.Log", ST_FLAG_RW, 0, "System.Email[1].Environment.Log", 0, 0, NULL }, + { "System.Email[1].Report.Periodicity", ST_FLAG_RW, 0, "System.Email[1].Report.Periodicity", 0, 0, NULL }, + { "System.Email[1].Report.Hour", ST_FLAG_RW, 0, "System.Email[1].Report.Hour", 0, 0, NULL }, + { "System.Email[1].Report.Next", ST_FLAG_RW, 0, "System.Email[1].Report.Next", 0, 0, NULL }, + { "System.Email[1].EventList.Discharging", ST_FLAG_RW, 0, "System.Email[1].EventList.Discharging", 0, 0, NULL }, + { "System.Email[1].EventList.ACPresent", ST_FLAG_RW, 0, "System.Email[1].EventList.ACPresent", 0, 0, NULL }, + { "System.Email[1].EventList.RunTimeToShutdown", ST_FLAG_RW, 0, "System.Email[1].EventList.RunTimeToShutdown", 0, 0, NULL }, + { "System.Email[1].EventList.BelowRemainingCapacityLimit", ST_FLAG_RW, 0, "System.Email[1].EventList.BelowRemainingCapacityLimit", 0, 0, NULL }, + { "System.Email[1].EventList.NeedReplacement.1", ST_FLAG_RW, 0, "System.Email[1].EventList.NeedReplacement.1", 0, 0, NULL }, + { "System.Email[1].EventList.NeedReplacement.0", ST_FLAG_RW, 0, "System.Email[1].EventList.NeedReplacement.0", 0, 0, NULL }, + { "System.Email[1].EventList.Overload.1", ST_FLAG_RW, 0, "System.Email[1].EventList.Overload.1", 0, 0, NULL }, + { "System.Email[1].EventList.Overload.0", ST_FLAG_RW, 0, "System.Email[1].EventList.Overload.0", 0, 0, NULL }, + { "System.Email[1].EventList.InternalFailure.1", ST_FLAG_RW, 0, "System.Email[1].EventList.InternalFailure.1", 0, 0, NULL }, + { "System.Email[1].EventList.InternalFailure.0", ST_FLAG_RW, 0, "System.Email[1].EventList.InternalFailure.0", 0, 0, NULL }, + { "System.Email[1].EventList.CommunicationLost.1", ST_FLAG_RW, 0, "System.Email[1].EventList.CommunicationLost.1", 0, 0, NULL }, + { "System.Email[1].EventList.CommunicationLost.0", ST_FLAG_RW, 0, "System.Email[1].EventList.CommunicationLost.0", 0, 0, NULL }, + { "System.Email[1].EventList.Charger.InternalFailure", ST_FLAG_RW, 0, "System.Email[1].EventList.Charger.InternalFailure", 0, 0, NULL }, + { "System.Email[1].EventList.Input[2].Used.1", ST_FLAG_RW, 0, "System.Email[1].EventList.Input[2].Used.1", 0, 0, NULL }, + { "System.Email[1].EventList.Input[2].Used.0", ST_FLAG_RW, 0, "System.Email[1].EventList.Input[2].Used.0", 0, 0, NULL }, + { "System.Email[1].EventList.PowerModule.RedundancyLost.1", ST_FLAG_RW, 0, "System.Email[1].EventList.PowerModule.RedundancyLost.1", 0, 0, NULL }, + { "System.Email[1].EventList.PowerModule.RedundancyLost.0", ST_FLAG_RW, 0, "System.Email[1].EventList.PowerModule.RedundancyLost.0", 0, 0, NULL }, + { "System.Email[1].EventList.PowerModule.ProtectionLost.1", ST_FLAG_RW, 0, "System.Email[1].EventList.PowerModule.ProtectionLost.1", 0, 0, NULL }, + { "System.Email[1].EventList.PowerModule.ProtectionLost.0", ST_FLAG_RW, 0, "System.Email[1].EventList.PowerModule.ProtectionLost.0", 0, 0, NULL }, + { "System.Email[1].EventList.FirmwareUpgrade", ST_FLAG_RW, 0, "System.Email[1].EventList.FirmwareUpgrade", 0, 0, NULL }, + { "System.Email[1].EventList.Environment.CommunicationLost", ST_FLAG_RW, 0, "System.Email[1].EventList.Environment.CommunicationLost", 0, 0, NULL }, + { "System.Email[1].EventList.Environment.Notify", ST_FLAG_RW, 0, "System.Email[1].EventList.Environment.Notify", 0, 0, NULL }, + { "System.Email[2].Recipient", ST_FLAG_RW, 0, "System.Email[2].Recipient", 0, 0, NULL }, + { "System.Email[2].Selected", ST_FLAG_RW, 0, "System.Email[2].Selected", 0, 0, NULL }, + { "System.Email[2].Enotify", ST_FLAG_RW, 0, "System.Email[2].Enotify", 0, 0, NULL }, + { "System.Email[2].Measures.Log", ST_FLAG_RW, 0, "System.Email[2].Measures.Log", 0, 0, NULL }, + { "System.Email[2].Events.Log", ST_FLAG_RW, 0, "System.Email[2].Events.Log", 0, 0, NULL }, + { "System.Email[2].SystemEvents.Log", ST_FLAG_RW, 0, "System.Email[2].SystemEvents.Log", 0, 0, NULL }, + { "System.Email[2].Environment.Log", ST_FLAG_RW, 0, "System.Email[2].Environment.Log", 0, 0, NULL }, + { "System.Email[2].Report.Periodicity", ST_FLAG_RW, 0, "System.Email[2].Report.Periodicity", 0, 0, NULL }, + { "System.Email[2].Report.Hour", ST_FLAG_RW, 0, "System.Email[2].Report.Hour", 0, 0, NULL }, + { "System.Email[2].Report.Next", ST_FLAG_RW, 0, "System.Email[2].Report.Next", 0, 0, NULL }, + { "System.Email[2].EventList.Discharging", ST_FLAG_RW, 0, "System.Email[2].EventList.Discharging", 0, 0, NULL }, + { "System.Email[2].EventList.ACPresent", ST_FLAG_RW, 0, "System.Email[2].EventList.ACPresent", 0, 0, NULL }, + { "System.Email[2].EventList.RunTimeToShutdown", ST_FLAG_RW, 0, "System.Email[2].EventList.RunTimeToShutdown", 0, 0, NULL }, + { "System.Email[2].EventList.BelowRemainingCapacityLimit", ST_FLAG_RW, 0, "System.Email[2].EventList.BelowRemainingCapacityLimit", 0, 0, NULL }, + { "System.Email[2].EventList.NeedReplacement.1", ST_FLAG_RW, 0, "System.Email[2].EventList.NeedReplacement.1", 0, 0, NULL }, + { "System.Email[2].EventList.NeedReplacement.0", ST_FLAG_RW, 0, "System.Email[2].EventList.NeedReplacement.0", 0, 0, NULL }, + { "System.Email[2].EventList.Overload.1", ST_FLAG_RW, 0, "System.Email[2].EventList.Overload.1", 0, 0, NULL }, + { "System.Email[2].EventList.Overload.0", ST_FLAG_RW, 0, "System.Email[2].EventList.Overload.0", 0, 0, NULL }, + { "System.Email[2].EventList.InternalFailure.1", ST_FLAG_RW, 0, "System.Email[2].EventList.InternalFailure.1", 0, 0, NULL }, + { "System.Email[2].EventList.InternalFailure.0", ST_FLAG_RW, 0, "System.Email[2].EventList.InternalFailure.0", 0, 0, NULL }, + { "System.Email[2].EventList.CommunicationLost.1", ST_FLAG_RW, 0, "System.Email[2].EventList.CommunicationLost.1", 0, 0, NULL }, + { "System.Email[2].EventList.CommunicationLost.0", ST_FLAG_RW, 0, "System.Email[2].EventList.CommunicationLost.0", 0, 0, NULL }, + { "System.Email[2].EventList.Charger.InternalFailure", ST_FLAG_RW, 0, "System.Email[2].EventList.Charger.InternalFailure", 0, 0, NULL }, + { "System.Email[2].EventList.Input[2].Used.1", ST_FLAG_RW, 0, "System.Email[2].EventList.Input[2].Used.1", 0, 0, NULL }, + { "System.Email[2].EventList.Input[2].Used.0", ST_FLAG_RW, 0, "System.Email[2].EventList.Input[2].Used.0", 0, 0, NULL }, + { "System.Email[2].EventList.PowerModule.RedundancyLost.1", ST_FLAG_RW, 0, "System.Email[2].EventList.PowerModule.RedundancyLost.1", 0, 0, NULL }, + { "System.Email[2].EventList.PowerModule.RedundancyLost.0", ST_FLAG_RW, 0, "System.Email[2].EventList.PowerModule.RedundancyLost.0", 0, 0, NULL }, + { "System.Email[2].EventList.PowerModule.ProtectionLost.1", ST_FLAG_RW, 0, "System.Email[2].EventList.PowerModule.ProtectionLost.1", 0, 0, NULL }, + { "System.Email[2].EventList.PowerModule.ProtectionLost.0", ST_FLAG_RW, 0, "System.Email[2].EventList.PowerModule.ProtectionLost.0", 0, 0, NULL }, + { "System.Email[2].EventList.FirmwareUpgrade", ST_FLAG_RW, 0, "System.Email[2].EventList.FirmwareUpgrade", 0, 0, NULL }, + { "System.Email[2].EventList.Environment.CommunicationLost", ST_FLAG_RW, 0, "System.Email[2].EventList.Environment.CommunicationLost", 0, 0, NULL }, + { "System.Email[2].EventList.Environment.Notify", ST_FLAG_RW, 0, "System.Email[2].EventList.Environment.Notify", 0, 0, NULL }, + { "System.Email[3].Recipient", ST_FLAG_RW, 0, "System.Email[3].Recipient", 0, 0, NULL }, + { "System.Email[3].Selected", ST_FLAG_RW, 0, "System.Email[3].Selected", 0, 0, NULL }, + { "System.Email[3].Enotify", ST_FLAG_RW, 0, "System.Email[3].Enotify", 0, 0, NULL }, + { "System.Email[3].Measures.Log", ST_FLAG_RW, 0, "System.Email[3].Measures.Log", 0, 0, NULL }, + { "System.Email[3].Events.Log", ST_FLAG_RW, 0, "System.Email[3].Events.Log", 0, 0, NULL }, + { "System.Email[3].SystemEvents.Log", ST_FLAG_RW, 0, "System.Email[3].SystemEvents.Log", 0, 0, NULL }, + { "System.Email[3].Environment.Log", ST_FLAG_RW, 0, "System.Email[3].Environment.Log", 0, 0, NULL }, + { "System.Email[3].Report.Periodicity", ST_FLAG_RW, 0, "System.Email[3].Report.Periodicity", 0, 0, NULL }, + { "System.Email[3].Report.Hour", ST_FLAG_RW, 0, "System.Email[3].Report.Hour", 0, 0, NULL }, + { "System.Email[3].Report.Next", ST_FLAG_RW, 0, "System.Email[3].Report.Next", 0, 0, NULL }, + { "System.Email[3].EventList.Discharging", ST_FLAG_RW, 0, "System.Email[3].EventList.Discharging", 0, 0, NULL }, + { "System.Email[3].EventList.ACPresent", ST_FLAG_RW, 0, "System.Email[3].EventList.ACPresent", 0, 0, NULL }, + { "System.Email[3].EventList.RunTimeToShutdown", ST_FLAG_RW, 0, "System.Email[3].EventList.RunTimeToShutdown", 0, 0, NULL }, + { "System.Email[3].EventList.BelowRemainingCapacityLimit", ST_FLAG_RW, 0, "System.Email[3].EventList.BelowRemainingCapacityLimit", 0, 0, NULL }, + { "System.Email[3].EventList.NeedReplacement.1", ST_FLAG_RW, 0, "System.Email[3].EventList.NeedReplacement.1", 0, 0, NULL }, + { "System.Email[3].EventList.NeedReplacement.0", ST_FLAG_RW, 0, "System.Email[3].EventList.NeedReplacement.0", 0, 0, NULL }, + { "System.Email[3].EventList.Overload.1", ST_FLAG_RW, 0, "System.Email[3].EventList.Overload.1", 0, 0, NULL }, + { "System.Email[3].EventList.Overload.0", ST_FLAG_RW, 0, "System.Email[3].EventList.Overload.0", 0, 0, NULL }, + { "System.Email[3].EventList.InternalFailure.1", ST_FLAG_RW, 0, "System.Email[3].EventList.InternalFailure.1", 0, 0, NULL }, + { "System.Email[3].EventList.InternalFailure.0", ST_FLAG_RW, 0, "System.Email[3].EventList.InternalFailure.0", 0, 0, NULL }, + { "System.Email[3].EventList.CommunicationLost.1", ST_FLAG_RW, 0, "System.Email[3].EventList.CommunicationLost.1", 0, 0, NULL }, + { "System.Email[3].EventList.CommunicationLost.0", ST_FLAG_RW, 0, "System.Email[3].EventList.CommunicationLost.0", 0, 0, NULL }, + { "System.Email[3].EventList.Charger.InternalFailure", ST_FLAG_RW, 0, "System.Email[3].EventList.Charger.InternalFailure", 0, 0, NULL }, + { "System.Email[3].EventList.Input[2].Used.1", ST_FLAG_RW, 0, "System.Email[3].EventList.Input[2].Used.1", 0, 0, NULL }, + { "System.Email[3].EventList.Input[2].Used.0", ST_FLAG_RW, 0, "System.Email[3].EventList.Input[2].Used.0", 0, 0, NULL }, + { "System.Email[3].EventList.PowerModule.RedundancyLost.1", ST_FLAG_RW, 0, "System.Email[3].EventList.PowerModule.RedundancyLost.1", 0, 0, NULL }, + { "System.Email[3].EventList.PowerModule.RedundancyLost.0", ST_FLAG_RW, 0, "System.Email[3].EventList.PowerModule.RedundancyLost.0", 0, 0, NULL }, + { "System.Email[3].EventList.PowerModule.ProtectionLost.1", ST_FLAG_RW, 0, "System.Email[3].EventList.PowerModule.ProtectionLost.1", 0, 0, NULL }, + { "System.Email[3].EventList.PowerModule.ProtectionLost.0", ST_FLAG_RW, 0, "System.Email[3].EventList.PowerModule.ProtectionLost.0", 0, 0, NULL }, + { "System.Email[3].EventList.FirmwareUpgrade", ST_FLAG_RW, 0, "System.Email[3].EventList.FirmwareUpgrade", 0, 0, NULL }, + { "System.Email[3].EventList.Environment.CommunicationLost", ST_FLAG_RW, 0, "System.Email[3].EventList.Environment.CommunicationLost", 0, 0, NULL }, + { "System.Email[3].EventList.Environment.Notify", ST_FLAG_RW, 0, "System.Email[3].EventList.Environment.Notify", 0, 0, NULL }, + { "System.Schedule[0].Off", ST_FLAG_RW, 0, "System.Schedule[0].Off", 0, 0, NULL }, + { "System.Schedule[0].On", ST_FLAG_RW, 0, "System.Schedule[0].On", 0, 0, NULL }, + { "System.Schedule[1].Off", ST_FLAG_RW, 0, "System.Schedule[1].Off", 0, 0, NULL }, + { "System.Schedule[1].On", ST_FLAG_RW, 0, "System.Schedule[1].On", 0, 0, NULL }, + { "System.Schedule[2].Off", ST_FLAG_RW, 0, "System.Schedule[2].Off", 0, 0, NULL }, + { "System.Schedule[2].On", ST_FLAG_RW, 0, "System.Schedule[2].On", 0, 0, NULL }, + { "System.Schedule[3].Off", ST_FLAG_RW, 0, "System.Schedule[3].Off", 0, 0, NULL }, + { "System.Schedule[3].On", ST_FLAG_RW, 0, "System.Schedule[3].On", 0, 0, NULL }, + { "System.Schedule[4].Off", ST_FLAG_RW, 0, "System.Schedule[4].Off", 0, 0, NULL }, + { "System.Schedule[4].On", ST_FLAG_RW, 0, "System.Schedule[4].On", 0, 0, NULL }, + { "System.Schedule[5].Off", ST_FLAG_RW, 0, "System.Schedule[5].Off", 0, 0, NULL }, + { "System.Schedule[5].On", ST_FLAG_RW, 0, "System.Schedule[5].On", 0, 0, NULL }, + { "System.Schedule[6].Off", ST_FLAG_RW, 0, "System.Schedule[6].Off", 0, 0, NULL }, + { "System.Schedule[6].On", ST_FLAG_RW, 0, "System.Schedule[6].On", 0, 0, NULL }, + { "System.NetworkManagementSystem[0].Name", ST_FLAG_RW, 0, "System.NetworkManagementSystem[0].Name", 0, 0, NULL }, + { "System.NetworkManagementSystem[0].HostName", ST_FLAG_RW, 0, "System.NetworkManagementSystem[0].HostName", 0, 0, NULL }, + { "System.NetworkManagementSystem[0].TrapCommunity", ST_FLAG_RW, 0, "System.NetworkManagementSystem[0].TrapCommunity", 0, 0, NULL }, + { "System.NetworkManagementSystem[0].TrapSnmpVersion", ST_FLAG_RW, 0, "System.NetworkManagementSystem[0].TrapSnmpVersion", 0, 0, NULL }, + { "System.NetworkManagementSystem[0].TrapSelectedMibs", ST_FLAG_RW, 0, "System.NetworkManagementSystem[0].TrapSelectedMibs", 0, 0, NULL }, + { "System.NetworkManagementSystem[1].Name", ST_FLAG_RW, 0, "System.NetworkManagementSystem[1].Name", 0, 0, NULL }, + { "System.NetworkManagementSystem[1].HostName", ST_FLAG_RW, 0, "System.NetworkManagementSystem[1].HostName", 0, 0, NULL }, + { "System.NetworkManagementSystem[1].TrapCommunity", ST_FLAG_RW, 0, "System.NetworkManagementSystem[1].TrapCommunity", 0, 0, NULL }, + { "System.NetworkManagementSystem[1].TrapSnmpVersion", ST_FLAG_RW, 0, "System.NetworkManagementSystem[1].TrapSnmpVersion", 0, 0, NULL }, + { "System.NetworkManagementSystem[1].TrapSelectedMibs", ST_FLAG_RW, 0, "System.NetworkManagementSystem[1].TrapSelectedMibs", 0, 0, NULL }, + { "System.NetworkManagementSystem[2].Name", ST_FLAG_RW, 0, "System.NetworkManagementSystem[2].Name", 0, 0, NULL }, + { "System.NetworkManagementSystem[2].HostName", ST_FLAG_RW, 0, "System.NetworkManagementSystem[2].HostName", 0, 0, NULL }, + { "System.NetworkManagementSystem[2].TrapCommunity", ST_FLAG_RW, 0, "System.NetworkManagementSystem[2].TrapCommunity", 0, 0, NULL }, + { "System.NetworkManagementSystem[2].TrapSnmpVersion", ST_FLAG_RW, 0, "System.NetworkManagementSystem[2].TrapSnmpVersion", 0, 0, NULL }, + { "System.NetworkManagementSystem[2].TrapSelectedMibs", ST_FLAG_RW, 0, "System.NetworkManagementSystem[2].TrapSelectedMibs", 0, 0, NULL }, + { "System.ClientCfg.ShutdownTimer.Select", ST_FLAG_RW, 0, "System.ClientCfg.ShutdownTimer.Select", 0, 0, NULL }, + { "System.ClientCfg.ShutdownTimer", ST_FLAG_RW, 0, "System.ClientCfg.ShutdownTimer", 0, 0, NULL }, + { "System.ClientCfg.ShutdownDuration", ST_FLAG_RW, 0, "System.ClientCfg.ShutdownDuration", 0, 0, NULL }, + { "System.ClientCfg.BroadcastAdmins", ST_FLAG_RW, 0, "System.ClientCfg.BroadcastAdmins", 0, 0, NULL }, + { "System.ClientCfg.BroadcastUsers", ST_FLAG_RW, 0, "System.ClientCfg.BroadcastUsers", 0, 0, NULL }, + { "Environment.iName", ST_FLAG_RW, 0, "Environment.iName", 0, 0, NULL }, + { "Environment.Temperature.Unit", ST_FLAG_RW, 0, "Environment.Temperature.Unit", 0, 0, NULL }, + { "Environment.Temperature.Hysteresis", ST_FLAG_RW, 0, "Environment.Temperature.Hysteresis", 0, 0, NULL }, + { "Environment.Temperature.Offset", ST_FLAG_RW, 0, "Environment.Temperature.Offset", 0, 0, NULL }, + { "Environment.Temperature.HighNotify", ST_FLAG_RW, 0, "Environment.Temperature.HighNotify", 0, 0, NULL }, + { "Environment.Temperature.LowNotify", ST_FLAG_RW, 0, "Environment.Temperature.LowNotify", 0, 0, NULL }, + { "Environment.Temperature.HighShutdown", ST_FLAG_RW, 0, "Environment.Temperature.HighShutdown", 0, 0, NULL }, + { "Environment.Temperature.LowShutdown", ST_FLAG_RW, 0, "Environment.Temperature.LowShutdown", 0, 0, NULL }, + { "Environment.Humidity.Hysteresis", ST_FLAG_RW, 0, "Environment.Humidity.Hysteresis", 0, 0, NULL }, + { "Environment.Humidity.Offset", ST_FLAG_RW, 0, "Environment.Humidity.Offset", 0, 0, NULL }, + { "Environment.Humidity.HighNotify", ST_FLAG_RW, 0, "Environment.Humidity.HighNotify", 0, 0, NULL }, + { "Environment.Humidity.LowNotify", ST_FLAG_RW, 0, "Environment.Humidity.LowNotify", 0, 0, NULL }, + { "Environment.Humidity.HighShutdown", ST_FLAG_RW, 0, "Environment.Humidity.HighShutdown", 0, 0, NULL }, + { "Environment.Humidity.LowShutdown", ST_FLAG_RW, 0, "Environment.Humidity.LowShutdown", 0, 0, NULL }, + { "Environment.Input[1].iName", ST_FLAG_RW, 0, "Environment.Input[1].iName", 0, 0, NULL }, + { "Environment.Input[1].State[0].Description", ST_FLAG_RW, 0, "Environment.Input[1].State[0].Description", 0, 0, NULL }, + { "Environment.Input[1].State[0].Notify", ST_FLAG_RW, 0, "Environment.Input[1].State[0].Notify", 0, 0, NULL }, + { "Environment.Input[1].State[0].Shutdown", ST_FLAG_RW, 0, "Environment.Input[1].State[0].Shutdown", 0, 0, NULL }, + { "Environment.Input[1].State[1].Description", ST_FLAG_RW, 0, "Environment.Input[1].State[1].Description", 0, 0, NULL }, + { "Environment.Input[1].State[1].Notify", ST_FLAG_RW, 0, "Environment.Input[1].State[1].Notify", 0, 0, NULL }, + { "Environment.Input[1].State[1].Shutdown", ST_FLAG_RW, 0, "Environment.Input[1].State[1].Shutdown", 0, 0, NULL }, + { "Environment.Input[2].iName", ST_FLAG_RW, 0, "Environment.Input[2].iName", 0, 0, NULL }, + { "Environment.Input[2].State[0].Description", ST_FLAG_RW, 0, "Environment.Input[2].State[0].Description", 0, 0, NULL }, + { "Environment.Input[2].State[0].Notify", ST_FLAG_RW, 0, "Environment.Input[2].State[0].Notify", 0, 0, NULL }, + { "Environment.Input[2].State[0].Shutdown", ST_FLAG_RW, 0, "Environment.Input[2].State[0].Shutdown", 0, 0, NULL }, + { "Environment.Input[2].State[1].Description", ST_FLAG_RW, 0, "Environment.Input[2].State[1].Description", 0, 0, NULL }, + { "Environment.Input[2].State[1].Notify", ST_FLAG_RW, 0, "Environment.Input[2].State[1].Notify", 0, 0, NULL }, + { "Environment.Input[2].State[1].Shutdown", ST_FLAG_RW, 0, "Environment.Input[2].State[1].Shutdown", 0, 0, NULL }, + { "System.TimeSync", ST_FLAG_RW, 0, "System.TimeSync", 0, 0, NULL }, + { "System.TimeNtp", ST_FLAG_RW, 0, "System.TimeNtp", 0, 0, NULL }, + { "System.TimeZone", ST_FLAG_RW, 0, "System.TimeZone", 0, 0, NULL }, + { "System.TimeDaylight", ST_FLAG_RW, 0, "System.TimeDaylight", 0, 0, NULL }, +#endif /* not interresting for NUT */ + /* Special case: boolean values that are mapped to ups.status and ups.alarm */ { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", 0, 0, online_info }, - { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", 0, 0, discharging_info }, { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.Charging", 0, 0, charging_info }, + /* NMC bug: keep discharging test AFTER charging test */ + { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", 0, 0, discharging_info }, { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", 0, 0, lowbatt_info }, { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.Overload", 0, 0, overload_info }, { NULL, 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", 0, 0, replacebatt_info }, @@ -573,14 +945,12 @@ static xml_info_t mge_xml2nut[] = { { "battery.charge.restart", 0, 0, "UPS.PowerSummary.RestartLevel", 0, 0, NULL }, { "battery.capacity", 0, 0, "UPS.BatterySystem.Battery.DesignCapacity", 0, 0, mge_battery_capacity }, /* conversion needed from As to Ah */ { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", 0, 0, NULL }, - { "battery.runtime.low", 0, 0, "System.RunTimeToEmptyLimit", 0, 0, NULL }, + { "battery.runtime.low", ST_FLAG_RW, 0, "System.RunTimeToEmptyLimit", 0, 0, NULL }, { "battery.temperature", 0, 0, "UPS.BatterySystem.Battery.Temperature", 0, 0, NULL }, + { "battery.packs.external", 0, 0, "UPS.BatterySystem.Battery.Count", 0, 0, NULL }, { "battery.type", ST_FLAG_STATIC, 0, "UPS.PowerSummary.iDeviceChemistry", 0, 0, NULL }, { "battery.type", ST_FLAG_STATIC, 0, "UPS.PowerSummary.iDeviceChemistery", 0, 0, NULL }, /* [sic] */ - { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", 0, 0, NULL }, { "battery.voltage.nominal", ST_FLAG_STATIC, 0, "UPS.BatterySystem.ConfigVoltage", 0, 0, NULL }, - { "battery.voltage.nominal", ST_FLAG_STATIC, 0, "UPS.PowerSummary.ConfigVoltage", 0, 0, NULL }, /* mge_battery_voltage_nominal */ - { "battery.current", 0, 0, "UPS.PowerSummary.Current", 0, 0, NULL }, { "battery.protection", 0, 0, "UPS.BatterySystem.Battery.DeepDischargeProtection", 0, 0, yes_no_info }, { "battery.energysave", 0, 0, "UPS.PowerConverter.Input[3].EnergySaving", 0, 0, yes_no_info }, @@ -598,24 +968,27 @@ static xml_info_t mge_xml2nut[] = { { "ups.firmware", ST_FLAG_STATIC, 0, "UPS.PowerSummary.iVersion", 0, 0, NULL }, { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", 0, 0, NULL }, { "ups.load.high", 0, 0, "UPS.Flow[4].ConfigPercentLoad", 0, 0, NULL }, - { "ups.delay.shutdown", 0, 0, "System.ShutdownDuration", 0, 0, NULL }, - { "ups.timer.start", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", 0, 0, NULL}, - { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", 0, 0, mge_timer_shutdown }, + { "ups.delay.shutdown", ST_FLAG_RW, 0, "System.ShutdownDuration", 0, 0, NULL }, + { "ups.timer.start", ST_FLAG_RW, 0, "UPS.PowerSummary.DelayBeforeStartup", 0, 0, NULL}, + { "ups.timer.shutdown", ST_FLAG_RW, 0, "UPS.PowerSummary.DelayBeforeShutdown", 0, 0, mge_timer_shutdown }, + /* Catch shutdown imminent criteria, keep it after + UPS.PowerSummary.DelayBeforeShutdown managment */ + { NULL, 0, 0, "System.RunTimeToShutdown", 0, 0, mge_shutdown_imminent }, { "ups.timer.reboot", 0, 0, "UPS.PowerSummary.DelayBeforeReboot", 0, 0, NULL }, { "ups.test.result", 0, 0, "UPS.BatterySystem.Battery.Test", 0, 0, mge_test_result_info }, { "ups.test.interval", 0, 0, "UPS.BatterySystem.Battery.TestPeriod", 0, 0, NULL }, { "ups.beeper.status", 0 ,0, "UPS.BatterySystem.Battery.AudibleAlarmControl", 0, 0, mge_beeper_info }, { "ups.beeper.status", 0 ,0, "UPS.PowerSummary.AudibleAlarmControl", 0, 0, mge_beeper_info }, { "ups.temperature", 0, 0, "UPS.PowerSummary.Temperature", 0, 0, NULL }, - { "ups.power", 0, 0, "UPS.PowerConverter.Output.ApparentPower", 0, 0, NULL }, - { "ups.L1.power", 0, 0, "UPS.PowerConverter.Output.Phase[1].ApparentPower", 0, 0, ignore_if_zero }, - { "ups.L2.power", 0, 0, "UPS.PowerConverter.Output.Phase[2].ApparentPower", 0, 0, ignore_if_zero }, - { "ups.L3.power", 0, 0, "UPS.PowerConverter.Output.Phase[3].ApparentPower", 0, 0, ignore_if_zero }, { "ups.power.nominal", ST_FLAG_STATIC, 0, "UPS.Flow[4].ConfigApparentPower", 0, 0, NULL }, + { "ups.power", 0, 0, "UPS.PowerConverter.Output.ApparentPower", 0, 0, NULL }, + { "ups.L1.power", 0, 0, "UPS.PowerConverter.Output.Phase[1].ApparentPower", 0, 0, NULL }, + { "ups.L2.power", 0, 0, "UPS.PowerConverter.Output.Phase[2].ApparentPower", 0, 0, NULL }, + { "ups.L3.power", 0, 0, "UPS.PowerConverter.Output.Phase[3].ApparentPower", 0, 0, NULL }, { "ups.realpower", 0, 0, "UPS.PowerConverter.Output.ActivePower", 0, 0, NULL }, - { "ups.L1.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[1].ActivePower", 0, 0, ignore_if_zero }, - { "ups.L2.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[2].ActivePower", 0, 0, ignore_if_zero }, - { "ups.L3.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[3].ActivePower", 0, 0, ignore_if_zero }, + { "ups.L1.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[1].ActivePower", 0, 0, NULL }, + { "ups.L2.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[2].ActivePower", 0, 0, NULL }, + { "ups.L3.realpower", 0, 0, "UPS.PowerConverter.Output.Phase[3].ActivePower", 0, 0, NULL }, { "ups.realpower.nominal", ST_FLAG_STATIC, 0, "UPS.Flow[4].ConfigActivePower", 0, 0, NULL }, { "ups.start.auto", 0, 0, "UPS.PowerConverter.Input[1].AutomaticRestart", 0, 0, yes_no_info }, { "ups.start.battery", 0, 0, "UPS.PowerConverter.Input[3].StartOnBattery", 0, 0, yes_no_info }, @@ -696,15 +1069,17 @@ static xml_info_t mge_xml2nut[] = { /* Ambient page */ { "ambient.humidity", 0, 0, "Environment.Humidity", 0, 0, NULL }, - { "ambient.humidity.high", 0, 0, "Environment.Humidity.HighThreshold", 0, 0, NULL }, - { "ambient.humidity.low", 0, 0, "Environment.Humidity.LowThreshold", 0, 0, NULL }, + { "ambient.humidity.high", ST_FLAG_RW, 0, "Environment.Humidity.HighThreshold", 0, 0, NULL }, + { "ambient.humidity.low", ST_FLAG_RW, 0, "Environment.Humidity.LowThreshold", 0, 0, NULL }, { "ambient.humidity.maximum", 0, 0, "Environment.PresentStatus.HighHumidity", 0, 0, mge_ambient_info }, { "ambient.humidity.minimum", 0, 0, "Environment.PresentStatus.LowHumidity", 0, 0, mge_ambient_info }, { "ambient.temperature", 0, 0, "Environment.Temperature", 0, 0, NULL }, - { "ambient.temperature.high", 0, 0, "Environment.Temperature.HighThreshold", 0, 0, NULL }, - { "ambient.temperature.low", 0, 0, "Environment.Temperature.LowThreshold", 0, 0, NULL }, + { "ambient.temperature.high", ST_FLAG_RW, 0, "Environment.Temperature.HighThreshold", 0, 0, NULL }, + { "ambient.temperature.low", ST_FLAG_RW, 0, "Environment.Temperature.LowThreshold", 0, 0, NULL }, { "ambient.temperature.maximum", 0, 0, "Environment.PresentStatus.HighTemperature", 0, 0, mge_ambient_info }, { "ambient.temperature.minimum", 0, 0, "Environment.PresentStatus.LowTemperature", 0, 0, mge_ambient_info }, + { "ambient.contacts.1.status", 0, 0, "Environment.Input[1].PresentStatus.State", 0, 0, mge_drycontact_info }, + { "ambient.contacts.2.status", 0, 0, "Environment.Input[2].PresentStatus.State", 0, 0, mge_drycontact_info }, /* Outlet page (using MGE UPS SYSTEMS - PowerShare technology) */ { "outlet.id", 0, 0, "UPS.OutletSystem.Outlet[1].OutletID", 0, 0, NULL }, @@ -722,7 +1097,7 @@ static xml_info_t mge_xml2nut[] = { { "outlet.1.timer.shutdown", 0, 0, "UPS.OutletSystem.Outlet[2].DelayBeforeShutdown", 0, 0, NULL }, { "outlet.1.delay.start", 0, 0, "UPS.OutletSystem.Outlet[2].StartupTimer", 0, 0, NULL }, /* { "outlet.1.delay.shutdown", 0, 0, "UPS.OutletSystem.Outlet[2].ShutdownTimer", 0, 0, NULL }, */ - { "outlet.1.delay.shutdown", 0, 0, "System.Outlet[2].ShutdownDuration", 0, 0, NULL }, + { "outlet.1.delay.shutdown", ST_FLAG_RW, 0, "System.Outlet[2].ShutdownDuration", 0, 0, NULL }, { "outlet.2.id", 0, 0, "UPS.OutletSystem.Outlet[3].OutletID", 0, 0, NULL }, { "outlet.2.desc", 0, 0, "UPS.OutletSystem.Outlet[3].iName", 0, 0, NULL }, @@ -733,7 +1108,7 @@ static xml_info_t mge_xml2nut[] = { { "outlet.2.timer.shutdown", 0, 0, "UPS.OutletSystem.Outlet[3].DelayBeforeShutdown", 0, 0, NULL }, { "outlet.2.delay.start", 0, 0, "UPS.OutletSystem.Outlet[3].StartupTimer", 0, 0, NULL }, /* { "outlet.2.delay.shutdown", 0, 0, "UPS.OutletSystem.Outlet[3].ShutdownTimer", 0, 0, NULL }, */ - { "outlet.2.delay.shutdown", 0, 0, "System.Outlet[3].ShutdownDuration", 0, 0, NULL }, + { "outlet.2.delay.shutdown", ST_FLAG_RW, 0, "System.Outlet[3].ShutdownDuration", 0, 0, NULL }, /* For newer ePDU Monitored */ { "outlet.1.desc", 0, 0, "PDU.OutletSystem.Outlet[1].iName", 0, 0, NULL }, @@ -764,6 +1139,8 @@ static xml_info_t mge_xml2nut[] = { static int mge_xml_startelm_cb(void *userdata, int parent, const char *nspace, const char *name, const char **atts) { int state = _UNEXPECTED; + NUT_UNUSED_VARIABLE(userdata); + NUT_UNUSED_VARIABLE(nspace); switch(parent) { @@ -772,6 +1149,7 @@ static int mge_xml_startelm_cb(void *userdata, int parent, const char *nspace, c /* name="Network Management Card" type="Mosaic M" version="BA" */ /* name="Network Management Card" type="Transverse" version="GB (SN 49EH29101)" */ /* name="Monitored ePDU" type="Monitored ePDU" version="Version Upgrade" */ + /* name="PDU Network Management Card" type="SCOB" version="02.00.0036" signature="34008876" protocol="XML.V4" */ int i; for (i = 0; atts[i] && atts[i+1]; i += 2) { if (!strcasecmp(atts[i], "name")) { @@ -790,11 +1168,18 @@ static int mge_xml_startelm_cb(void *userdata, int parent, const char *nspace, c snprintfcat(val, sizeof(val), "/%s", atts[i+1]); s = strstr(val, " (SN "); if (s) { - dstate_setinfo("ups.serial", "%s", rtrim(s + 5, ')')); + dstate_setinfo("ups.serial", "%s", str_rtrim(s + 5, ')')); s[0] = '\0'; } dstate_setinfo("ups.firmware.aux", "%s", val); } + /* netxml-ups currently only supports XML version 3 (for UPS), + * and not version 4 (for UPS and PDU)! */ + if (!strcasecmp(atts[i], "protocol")) { + if (!strcasecmp(atts[i+1], "XML.V4")) { + fatalx(EXIT_FAILURE, "XML v4 protocol is not supported!"); + } + } } state = PRODUCT_INFO; break; @@ -997,8 +1382,12 @@ static int mge_xml_startelm_cb(void *userdata, int parent, const char *nspace, c state = XC_GENERAL; break; } + /* FIXME? Is the fall-through to handling "GENERAL" intended? + * was so in legacy code before the goto below... */ + goto fallthrough_case_general; case XC_GENERAL: + fallthrough_case_general: if (!strcasecmp(name, "STARTUP")) { /* config="CENTRALIZED" */ state = XC_STARTUP; @@ -1032,6 +1421,8 @@ static int mge_xml_startelm_cb(void *userdata, int parent, const char *nspace, c /* Character data callback; may return non-zero to abort the parse. */ static int mge_xml_cdata_cb(void *userdata, int state, const char *cdata, size_t len) { + NUT_UNUSED_VARIABLE(userdata); + /* skip empty lines */ if ((len == 1) && (cdata[0] == '\n')) { upsdebugx(3, "%s: cdata ignored (state = %d)", __func__, state); @@ -1060,6 +1451,8 @@ static int mge_xml_endelm_cb(void *userdata, int state, const char *nspace, cons { xml_info_t *info; const char *value; + NUT_UNUSED_VARIABLE(userdata); + NUT_UNUSED_VARIABLE(nspace); /* ignore objects for which no value was set */ if (strlen(val) == 0) { @@ -1087,6 +1480,7 @@ static int mge_xml_endelm_cb(void *userdata, int state, const char *nspace, cons if (info->convert) { value = info->convert(val); + upsdebugx(4, "-> XML variable %s [%s] which maps to NUT variable %s was converted to value %s for the NUT driver state", var, val, info->nutname, value); } else { value = val; } @@ -1100,6 +1494,25 @@ static int mge_xml_endelm_cb(void *userdata, int state, const char *nspace, cons upsdebugx(3, "-> XML variable %s [%s] doesn't map to any NUT variable", var, val); break; + + case PI_GET_OBJECT: + case GET_OBJECT: + /* We've just got a snapshot of all runtime data, saved well into + * dstate's already, so can estimate missing values if needed. */ + + /* For phase setup, we assume it does not change during run-time. + * Essentially this means that once we've detected it is N-phase, + * it stays this way for the rest of the driver run/life-time. */ + /* To change this behavior just flip the maychange flag to "1" */ + + dstate_detect_phasecount("input.", 1, + &inited_phaseinfo_in, &num_inphases, 0); + dstate_detect_phasecount("input.bypass.", 1, + &inited_phaseinfo_bypass, &num_bypassphases, 0); + dstate_detect_phasecount("output.", 1, + &inited_phaseinfo_out, &num_outphases, 0); + + break; } return 0; @@ -1118,3 +1531,83 @@ subdriver_t mge_xml_subdriver = { mge_xml_cdata_cb, mge_xml_endelm_cb, }; + +const char *vname_nut2mge_xml(const char *name) { + assert(NULL != name); + + size_t i = 0; + + for (; i < sizeof(mge_xml2nut) / sizeof(xml_info_t); ++i) { + xml_info_t *info = mge_xml2nut + i; + + if (NULL != info->nutname) + if (0 == strcasecmp(name, info->nutname)) + return info->xmlname; + } + + return NULL; +} + +const char *vname_mge_xml2nut(const char *name) { + assert(NULL != name); + + size_t i = 0; + + for (; i < sizeof(mge_xml2nut) / sizeof(xml_info_t); ++i) { + xml_info_t *info = mge_xml2nut + i; + + if (NULL != info->xmlname) + if (0 == strcasecmp(name, info->xmlname)) + return info->nutname; + } + + return NULL; +} + +char *vvalue_mge_xml2nut(const char *name, const char *value, size_t len) { + assert(NULL != name); + + size_t i = 0; + + for (; i < sizeof(mge_xml2nut) / sizeof(xml_info_t); ++i) { + xml_info_t *info = mge_xml2nut + i; + + if (NULL != info->nutname) + if (0 == strcasecmp(name, info->nutname)) { + /* Copy value */ + char *vcpy = (char *)malloc((len + 1) * sizeof(char)); + + if (NULL == vcpy) + return vcpy; + + memcpy(vcpy, value, len * sizeof(char)); + vcpy[len] = '\0'; + + /* Convert */ + if (NULL != info->convert) { + char *vconv = (char *)info->convert(vcpy); + + free(vcpy); + + return vconv; + } + else + return vcpy; + } + } + + return NULL; +} + +void vname_register_rw(void) { + size_t i = 0; + + for (; i < sizeof(mge_xml2nut) / sizeof(xml_info_t); ++i) { + xml_info_t *info = mge_xml2nut + i; + + if (NULL != info->nutname && info->nutflags & ST_FLAG_RW) { + dstate_setinfo(info->nutname, "%s", ""); + dstate_setflags(info->nutname, ST_FLAG_RW); + } + } +} diff --git a/drivers/mge-xml.h b/drivers/mge-xml.h index bb4cf4f..97a30d6 100644 --- a/drivers/mge-xml.h +++ b/drivers/mge-xml.h @@ -1,4 +1,4 @@ -/* mge-xml.h Model specific data for MGE XML protocol UPSes +/* mge-xml.h Model specific data for MGE XML protocol UPSes Copyright (C) 2008 Arjen de Korte @@ -25,4 +25,41 @@ extern subdriver_t mge_xml_subdriver; +/** + * \brief Convert NUT variable name to MGE XML + * + * \param name NUT variable name + * + * \return MGE XML variable name + */ +const char *vname_nut2mge_xml(const char *name); + +/** + * \brief Convert MGE XML variable name to NUT + * + * \param name MGE XML variable name + * + * \return NUT variable name + */ +const char * vname_mge_xml2nut(const char *name); + +/** + * \brief Convert MGE XML variable value to NUT value + * + * The function produces a newly created C-string that should + * be destroyed using \c free. + * + * \param name NUT variable name + * \param value MGE XML variable value + * \param len MGE XML variable value length (in characters) + * + * \return NUT variable value + */ +char *vvalue_mge_xml2nut(const char *name, const char *value, size_t len); + +/** + * \brief Register set of R/W variables + */ +void vname_register_rw(void); + #endif /* MGE_XML_H */ diff --git a/drivers/microdowell.c b/drivers/microdowell.c index 82bcd43..846764e 100644 --- a/drivers/microdowell.c +++ b/drivers/microdowell.c @@ -26,15 +26,14 @@ anything else is mandatory */ -#define ENTERPRISE_PROTOCOL - -#include "microdowell.h" - #include "main.h" #include "serial.h" #include #include "timehead.h" +#include "nut_stdint.h" +#define ENTERPRISE_PROTOCOL +#include "microdowell.h" #define MAX_START_DELAY 999999 #define MAX_SHUTDOWN_DELAY 32767 @@ -43,7 +42,7 @@ #define MAX_SHUTDOWN_DELAY_LEN 5 #define DRIVER_NAME "MICRODOWELL UPS driver" -#define DRIVER_VERSION "0.01" +#define DRIVER_VERSION "0.02" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -54,39 +53,41 @@ upsdrv_info_t upsdrv_info = { { NULL } }; -ENT_STRUCT ups ; +static ENT_STRUCT ups ; + +/* common driver routines */ int instcmd(const char *cmdname, const char *extra); int setvar(const char *varname, const char *val); /* he knew... macros should evaluate their arguments only once */ #define CLAMP(x, min, max) (((x) < (min)) ? (min) : (((x) > (max)) ? (max) : (x))) -static int CheckDataChecksum(unsigned char *Buff, int Len) +static int CheckDataChecksum(unsigned char *Buff, size_t Len) { - int i, Idx ; - unsigned char Xor ; + size_t i, Idx ; + unsigned char Xor ; - ups.FramePointer = Xor = 0 ; - for (Idx=0 ; Idx < Len ; Idx++) - if (Buff[Idx] == STX_CHAR) - break ; + ups.FramePointer = Xor = 0 ; + for (Idx=0 ; Idx < Len ; Idx++) + if (Buff[Idx] == STX_CHAR) + break ; ups.FramePointer = Idx ; /* Memorise start point. */ - /* Check that the message is not to short... */ - if ( (Idx > (Len-4)) || (Idx+Buff[Idx+1]+2 > Len) ) - return(ERR_MSG_TOO_SHORT) ; /* To short message! */ + /* Check that the message is not to short... */ + if ( (Idx > (Len-4)) || (Idx+Buff[Idx+1]+2 > Len) ) + return(ERR_MSG_TOO_SHORT) ; /* Too short a message! */ - /* Calculate checksum */ - for (i=Idx+1 ; i < Idx+Buff[Idx+1]+2 ; i++) - Xor ^= Buff[i] ; + /* Calculate checksum */ + for (i=Idx+1 ; i < Idx+Buff[Idx+1]+2 ; i++) + Xor ^= Buff[i] ; - /* if Xor != then checksum error */ - if (Xor != Buff[i]) - return(ERR_MSG_CHECKSUM) ; /* error in checksum */ + /* if Xor != then checksum error */ + if (Xor != Buff[i]) + return(ERR_MSG_CHECKSUM) ; /* error in checksum */ - /* If checksum OK: return */ - return(0) ; + /* If checksum OK: return */ + return(0) ; } @@ -115,13 +116,13 @@ static const char *ErrMessages[] = { /* */ "" } ; -const char *PrintErr(int ErrCode) +static const char *PrintErr(int ErrCode) { int msgIndex = 0 ; /* The default 'msgIndex' is 0 (error code not defined) */ switch (ErrCode) { - case ERR_NO_ERROR : msgIndex = 19 ; break ; + case ERR_NO_ERROR : msgIndex = 19 ; break ; case ERR_I2C_BUSY : msgIndex = 1 ; break ; case ERR_CMD_CHECKSUM : msgIndex = 2 ; break ; @@ -142,21 +143,21 @@ const char *PrintErr(int ErrCode) case ERR_COM_NO_CHARS : msgIndex = 16 ; break ; case ERR_MSG_TOO_SHORT : msgIndex = 17 ; break ; case ERR_MSG_CHECKSUM : msgIndex = 18 ; break ; - default: msgIndex = 0 ; break ; + + default : msgIndex = 0 ; break ; } return(ErrMessages[msgIndex]) ; } - -int CheckErrCode(unsigned char * Buff) +static int CheckErrCode(unsigned char * Buff) { - auto int Ret ; + auto int Ret ; - switch (Buff[2]) { - /* I have found an error */ - case CMD_NACK : - Ret = Buff[3] ; - break ; + switch (Buff[2]) { + /* I have found an error */ + case CMD_NACK : + Ret = Buff[3] ; + break ; case CMD_ACK : case CMD_GET_STATUS : @@ -175,19 +176,19 @@ int CheckErrCode(unsigned char * Buff) case CMD_SET_EEP_BLOCK : case CMD_GET_EEP_SEED : case CMD_INIT : - Ret = 0 ; - break ; + Ret = 0 ; + break ; - /* command not recognized */ - default: - Ret = ERR_CMD_UNRECOG ; - break ; - } - return(Ret) ; + /* command not recognized */ + default: + Ret = ERR_CMD_UNRECOG ; + break ; + } + return(Ret) ; } -void SendCmdToSerial(unsigned char *Buff, int Len) +static void SendCmdToSerial(unsigned char *Buff, size_t Len) { int i; unsigned char Tmp[20], Xor ; @@ -208,22 +209,19 @@ void SendCmdToSerial(unsigned char *Buff, int Len) ser_send_buf(upsfd, Tmp, Len+3) ; /* send data to the UPS */ } - - - -unsigned char * CmdSerial(unsigned char *OutBuffer, int Len, unsigned char *RetBuffer) +static unsigned char * CmdSerial(unsigned char *OutBuffer, size_t Len, unsigned char *RetBuffer) { #define TMP_BUFF_LEN 1024 - unsigned char InpBuff[TMP_BUFF_LEN+1] ; + unsigned char InpBuff[TMP_BUFF_LEN+1] ; unsigned char TmpBuff[3] ; - int i, ErrCode ; - unsigned char *p ; - int BuffLen ; + int i, ErrCode ; + unsigned char *p ; + size_t BuffLen ; /* The default error code (no received character) */ ErrCode = ERR_COM_NO_CHARS ; - SendCmdToSerial(OutBuffer, Len) ; + SendCmdToSerial(OutBuffer, Len) ; usleep(10000) ; /* small delay (1/100 s) */ /* get chars until timeout */ @@ -273,8 +271,6 @@ unsigned char * CmdSerial(unsigned char *OutBuffer, int Len, unsigned char *RetB return(NULL) ; /* There have been errors in the reading of the data */ } - - static int detect_hardware(void) { unsigned char OutBuff[20] ; @@ -282,6 +278,7 @@ static int detect_hardware(void) unsigned char *p ; int i, retries ; struct tm *Time ; + struct tm tmbuf; time_t lTime ; ups.ge_2kVA = 0 ; @@ -296,7 +293,7 @@ static int detect_hardware(void) { /* got UPS model */ for (i=0 ; i<8 ; i++) - ups.UpsModel[i] = p[i+5] ; + ups.UpsModel[i] = (char)p[i+5] ; ups.UpsModel[8] = '\0' ; upsdebugx(2, "get 'UPS model': %s", PrintErr(ups.ErrCode)); break ; /* UPS identified: exit from ' for' LOOP */ @@ -321,7 +318,7 @@ static int detect_hardware(void) { /* got UPS serial # */ for (i=0 ; i<8 ; i++) - ups.SerialNumber[i] = p[i+5] ; + ups.SerialNumber[i] = (char)p[i+5] ; ups.SerialNumber[8] = '\0' ; upsdebugx(2, "get 'UPS Serial #': %s", PrintErr(ups.ErrCode)); } @@ -379,6 +376,7 @@ static int detect_hardware(void) return -1; } + /* Get working time (battery+normal)) */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_MIN_VBATT ; /* working time */ @@ -414,6 +412,7 @@ static int detect_hardware(void) return -1; } + /* Get the THRESHOLD table (1) */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_THRESHOLD_1 ; /* Thresholds table 0 */ @@ -431,6 +430,7 @@ static int detect_hardware(void) return -1; } + /* Get the THRESHOLD table (2) */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_THRESHOLD_2 ; /* Thresholds table 0 */ @@ -469,7 +469,6 @@ static int detect_hardware(void) } - /* Get UPS sensitivity (fault points) */ OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */ OutBuff[1] = EEP_FAULT_POINTS ; /* Number of fault points (sensitivity)) */ @@ -493,15 +492,16 @@ static int detect_hardware(void) return -1; } + /* Set internal UPS clock */ time(&lTime) ; - Time = localtime(&lTime) ; + Time = localtime_r(&lTime, &tmbuf); OutBuff[0] = CMD_SET_TIMER ; /* set UPS internal timer */ OutBuff[1] = (Time->tm_wday+6) % 7 ; /* week day (0=monday) */ - OutBuff[2] = Time->tm_hour ; /* hours */ - OutBuff[3] = Time->tm_min ; /* minutes */ - OutBuff[4] = Time->tm_sec; /* seconds */ + OutBuff[2] = (unsigned char)Time->tm_hour ; /* hours */ + OutBuff[3] = (unsigned char)Time->tm_min ; /* minutes */ + OutBuff[4] = (unsigned char)Time->tm_sec ; /* seconds */ if ((p = CmdSerial(OutBuff, LEN_SET_TIMER, InpBuff)) != NULL) { upsdebugx(2, "set 'UPS internal clock': %s", PrintErr(ups.ErrCode)); @@ -535,10 +535,14 @@ void upsdrv_updateinfo(void) status_init(); /* reset status flags */ /* store last UPS status */ - ups.StatusUPS = (int)p[0] | ((int)p[1]<<8) | ((int)p[2]<<16) | ((int)p[3]<<24) ; - ups.ShortStatus = (int)p[0] | ((int)p[1]<<8) ; - upsdebugx(1, "ups.StatusUPS: %08lX", ups.StatusUPS); - upsdebugx(1, "ups.ShortStatus: %04X", ups.ShortStatus); + ups.StatusUPS = (uint32_t)p[0] ; + ups.StatusUPS |= ((uint32_t)p[1]<<8) ; + ups.StatusUPS |= ((uint32_t)p[2]<<16) ; + ups.StatusUPS |= ((uint32_t)p[3]<<24) ; + ups.ShortStatus = (uint16_t)p[0]; + ups.ShortStatus |= ((uint16_t)p[1]<<8) ; + upsdebugx(1, "ups.StatusUPS: %08" PRIX32, ups.StatusUPS); + upsdebugx(1, "ups.ShortStatus: %04" PRIX16, ups.ShortStatus); /* on battery? */ if (p[0] & 0x01) @@ -642,8 +646,6 @@ void upsdrv_updateinfo(void) /* ========================= */ - - int instcmd(const char *cmdname, const char *extra) { unsigned char OutBuff[20] ; @@ -771,52 +773,115 @@ int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_UNKNOWN; } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) || defined (HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE_BESIDEFUNC) ) +# pragma GCC diagnostic push +#endif +#if (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#if (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#if (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE_BESIDEFUNC) +# pragma GCC diagnostic ignored "-Wtautological-compare" +#endif int setvar(const char *varname, const char *val) { - int delay; + unsigned int delay; - if (sscanf(val, "%d", &delay) != 1) + if (sscanf(val, "%u", &delay) != 1) { return STAT_SET_UNKNOWN; } if (strcasecmp(varname, "ups.delay.start") == 0) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || defined (HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-compare" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif delay = CLAMP(delay, 0, MAX_START_DELAY); - upsdebugx(1, "set 'WUDELAY': %d/%d", delay, ups.WakeUpDelay); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || defined (HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE) ) +# pragma GCC diagnostic pop +#endif + upsdebugx(1, "set 'WUDELAY': %u/%u", delay, ups.WakeUpDelay); ups.WakeUpDelay = delay ; - dstate_setinfo("ups.delay.start", "%d", ups.WakeUpDelay); + dstate_setinfo("ups.delay.start", "%u", ups.WakeUpDelay); dstate_dataok(); return STAT_SET_HANDLED; } if (strcasecmp(varname, "ups.delay.shutdown") == 0) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || defined (HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-compare" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-compare" +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif delay = CLAMP(delay, 0, MAX_SHUTDOWN_DELAY); - upsdebugx(1, "set 'SDDELAY': %d/%d", delay, ups.ShutdownDelay); +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || defined (HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE) ) +# pragma GCC diagnostic pop +#endif + upsdebugx(1, "set 'SDDELAY': %u/%u", delay, ups.ShutdownDelay); ups.ShutdownDelay = delay; - dstate_setinfo("ups.delay.shutdown", "%d", ups.ShutdownDelay); + dstate_setinfo("ups.delay.shutdown", "%u", ups.ShutdownDelay); dstate_dataok(); return STAT_SET_HANDLED; } return STAT_SET_UNKNOWN; } - - +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) || defined (HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE_BESIDEFUNC) ) +# pragma GCC diagnostic pop +#endif void upsdrv_initinfo(void) { /* Get vars from ups.conf */ if (getval("ups.delay.shutdown")) { - ups.ShutdownDelay = CLAMP(atoi(getval("ups.delay.shutdown")), 0, MAX_SHUTDOWN_DELAY); + int ipv = atoi(getval("ups.delay.shutdown")); + ups.ShutdownDelay = (unsigned int) CLAMP(ipv, 0, MAX_SHUTDOWN_DELAY); } else { ups.ShutdownDelay = 120; /* Shutdown delay in seconds */ } if (getval("ups.delay.start")) { - ups.WakeUpDelay = CLAMP(atoi(getval("ups.delay.start")), 0, MAX_START_DELAY); + int ipv = atoi(getval("ups.delay.start")); + ups.WakeUpDelay = (unsigned int) CLAMP(ipv, 0, MAX_START_DELAY); } else { ups.WakeUpDelay = 10; /* WakeUp delay in seconds */ @@ -848,8 +913,6 @@ void upsdrv_initinfo(void) dstate_setinfo("ups.mfr.date", "%04d/%02d/%02d", ups.YearOfProd, ups.MonthOfProd, ups.DayOfProd) ; dstate_setinfo("battery.packs", "%d", ups.BatteryNumber) ; - dstate_setinfo("driver.version.internal", "%s", DRIVER_VERSION) ; - /* Register the available variables. */ dstate_setinfo("ups.delay.start", "%d", ups.WakeUpDelay); dstate_setflags("ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING); @@ -864,23 +927,19 @@ void upsdrv_initinfo(void) dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); - /* Register the available instant commands. */ -/* dstate_addcmd("test.battery.start"); +/* + dstate_addcmd("test.battery.start"); dstate_addcmd("test.battery.stop"); dstate_addcmd("shutdown.stop"); dstate_addcmd("beeper.toggle"); - */ +*/ /* set handlers */ upsh.instcmd = instcmd ; upsh.setvar = setvar; } - - - - void upsdrv_shutdown(void) { unsigned char OutBuff[20] ; @@ -896,10 +955,14 @@ void upsdrv_shutdown(void) status_init(); /* reset status flags */ /* store last UPS status */ - ups.StatusUPS = (int)p[0] | ((int)p[1]<<8) | ((int)p[2]<<16) | ((int)p[3]<<24) ; - ups.ShortStatus = (int)p[0] | ((int)p[1]<<8) ; - upsdebugx(1, "ups.StatusUPS: %08lX", ups.StatusUPS); - upsdebugx(1, "ups.ShortStatus: %04X", ups.ShortStatus); + ups.StatusUPS = (uint32_t)p[0] ; + ups.StatusUPS |= ((uint32_t)p[1]<<8) ; + ups.StatusUPS |= ((uint32_t)p[2]<<16) ; + ups.StatusUPS |= ((uint32_t)p[3]<<24) ; + ups.ShortStatus = (uint16_t)p[0] ; + ups.ShortStatus |= ((uint16_t)p[1]<<8) ; + upsdebugx(1, "ups.StatusUPS: %08" PRIX32, ups.StatusUPS); + upsdebugx(1, "ups.ShortStatus: %04" PRIX16, ups.ShortStatus); /* on battery? */ if (p[0] & 0x01) diff --git a/drivers/microdowell.h b/drivers/microdowell.h index af5185c..1750027 100644 --- a/drivers/microdowell.h +++ b/drivers/microdowell.h @@ -4,324 +4,322 @@ #ifdef ENTERPRISE_PROTOCOL #include +#include "nut_stdint.h" + +#define STX_CHAR '[' +#define ERR_COM_NO_CHARS -999 /* nessun carattere dalla porta seriale */ +#define ERR_MSG_TOO_SHORT -998 /* messaggio troppo breve: non arriva sino al Checksum */ +#define ERR_MSG_CHECKSUM -997 /* checksum non valido */ +#define ERR_COM_TIMEOUT -996 /* timeout in lettura */ +#define ERR_CLR_RX_BUFF -900 /* bisogna cancellare il buffer (non è un errore!) */ +#define COMMAND_NOT_VALID -50 /* comando non valido */ +#define PARAMETER_NOT_VALID -51 /* parametro non valido */ +#define ERR_UPS_NOT_FOUND -10 /* non trovo un UPS */ +#define ERR_COM_PORT_OPEN -11 /* impossibile aprire la porta di comunicazione */ +#define ERR_COM_SET_PORT -12 /* impossibile configurare la porta di comunicazione */ +#define ERR_COM_UNDEF_PORT -20 /* porta non valida o non esistente */ +#define ERR_USB_DRIVER -13 /* impossibile aprire il driver USB */ +#define ERR_USB_COMM_KO -14 /* interfaccia USB OK: gruppo spento o guasto */ +#define ERR_UPS_UNKNOWN -30 /* impossibile identificare l'UPS */ +#define ERR_PRG_THREAD -40 /* impossibile creare uno thread */ +#define ERR_PRG_INVALID_DATA -60 /* dati non validi */ + +#define ERR_NO_ERROR 0x00 /* no errors */ +#define ERR_I2C_BUSY 0x01 /* I2C bus busy (e2prom) */ +#define ERR_CMD_CHECKSUM 0x10 /* Checksum not valid */ +#define ERR_CMD_UNRECOG 0x11 /* unrecognized command */ +#define ERR_EEP_NOBLOCK 0x08 /* WRITE: eeprom address not multiple of 8 */ +#define ERR_EEP_OOBOUND 0x09 /* READ: eeprom address out of bound (with size) */ +#define ERR_EEP_WADDR1 0x28 /* error writing e2prom address */ +#define ERR_EEP_WSADDR1 0x29 /* error writing e2prom subaddress */ +#define ERR_EEP_RDATA 0x2A /* error reading e2prom data */ +#define ERR_EEP_WADDR2 0x50 /* error writing e2prom address */ +#define ERR_EEP_WSADDR2 0x51 /* error reading e2prom subaddress */ +#define ERR_EEP_WDATA 0x52 /* error writing e2prom data */ +#define ERR_EEP_WADDRVER 0x53 /* error writing e2prom address during data verification */ +#define ERR_EEP_WDATAVER 0x54 /* error verification e2prom data */ +#define ERR_EEP_VERIFY 0x55 /* e2prom data are different from those in the write buffer */ +#define ERR_EEP_CHECKSUM 0x90 /* e2prom checksum error */ -#define STX_CHAR '[' -#define ERR_COM_NO_CHARS -999 // nessun carattere dalla porta seriale -#define ERR_MSG_TOO_SHORT -998 // messaggio troppo breve: non arriva sino al Checksum -#define ERR_MSG_CHECKSUM -997 // checksum non valido -#define ERR_COM_TIMEOUT -996 // timeout in lettura -#define ERR_CLR_RX_BUFF -900 // bisogna cancellare il buffer (non è un errore!) -#define COMMAND_NOT_VALID -50 // comando non valido -#define PARAMETER_NOT_VALID -51 // parametro non valido -#define ERR_UPS_NOT_FOUND -10 // non trovo un UPS -#define ERR_COM_PORT_OPEN -11 // impossibile aprire la porta di comunicazione -#define ERR_COM_SET_PORT -12 // impossibile configurare la porta di comunicazione -#define ERR_COM_UNDEF_PORT -20 // porta non valida o non esistente -#define ERR_USB_DRIVER -13 // impossibile aprire il driver USB -#define ERR_USB_COMM_KO -14 // interfaccia USB OK: gruppo spento o guasto -#define ERR_UPS_UNKNOWN -30 // impossibile identificare l'UPS -#define ERR_PRG_THREAD -40 // impossibile creare uno thread -#define ERR_PRG_INVALID_DATA -60 // dati non validi +#define CMD_ACK 0x06 /* ACK */ +#define CMD_NACK 0x15 /* NACK */ -#define ERR_NO_ERROR 0x00 // no errors -#define ERR_I2C_BUSY 0x01 // I2C bus busy (e2prom) -#define ERR_CMD_CHECKSUM 0x10 // Checksum not valid -#define ERR_CMD_UNRECOG 0x11 // unrecognized command -#define ERR_EEP_NOBLOCK 0x08 // WRITE: eeprom address not multiple of 8 -#define ERR_EEP_OOBOUND 0x09 // READ: eeprom address out of bound (with size) -#define ERR_EEP_WADDR1 0x28 // error writing e2prom address -#define ERR_EEP_WSADDR1 0x29 // error writing e2prom subaddress -#define ERR_EEP_RDATA 0x2A // error reading e2prom data -#define ERR_EEP_WADDR2 0x50 // error writing e2prom address -#define ERR_EEP_WSADDR2 0x51 // error reading e2prom subaddress -#define ERR_EEP_WDATA 0x52 // error writing e2prom data -#define ERR_EEP_WADDRVER 0x53 // error writing e2prom address during data verification -#define ERR_EEP_WDATAVER 0x54 // error verification e2prom data -#define ERR_EEP_VERIFY 0x55 // e2prom data are different from those in the write buffer -#define ERR_EEP_CHECKSUM 0x90 // e2prom checksum error +#define CMD_GET_STATUS 0x00 /* comando di acquisizione STATUS */ +#define CMD_GET_MEASURES 0x01 /* comando di acquisizione MISURE */ +#define CMD_GET_CONFIG 0x02 /* comando di acquisizione CONFIGURAZIONE */ +#define CMD_GET_BATT_STAT 0x03 /* comando di acquisizione Battery+Load Status */ +#define CMD_GET_BAT_LD 0x03 /* comando di acquisizione Battery+Load Status */ +#define CMD_GET_MASK 0x07 /* comando di acquisizione MASCHERA PUNTI */ +#define CMD_SET_TIMER 0x20 /* comando di CONFIGURAZIONE TIMER */ +#define CMD_BATT_TEST 0x21 /* comando di CONFIGURAZIONE TEST BATTERIA */ +#define CMD_GET_BATT_TEST 0x22 /* comando di LETTURA TEST BATTERIA */ +#define CMD_SD_ONESHOT 0x40 /* comando di SCRITTURA SHUTDOWN ONESHOT */ +#define CMD_GET_SD_ONESHOT 0x41 /* comando di LETTURA SHUTDOWN ONESHOT */ +#define CMD_SET_SCHEDULE 0x42 /* comando di SCRITTURA SCHEDULE */ +#define CMD_GET_SCHEDULE 0x43 /* comando di LETTURA SCHEDULE */ +#define CMD_GET_EEP_BLOCK 0x50 /* comando di LETTURA BLOCCO EEPROM */ +#define CMD_SET_EEP_BLOCK 0x51 /* comando di SCRITTURA BLOCCO EEPROM */ +#define CMD_GET_EEP_SEED 0x52 /* comando di acquisizione SEME PROGR. EEPROM */ +#define CMD_INIT 0xF0 /* comando di REINIZIALIZZAZIONE UPS */ +#define LEN_ACK 1 /* ACK */ +#define LEN_NACK 2 /* NACK */ -#define CMD_ACK 0x06 // ACK -#define CMD_NACK 0x15 // NACK - -#define CMD_GET_STATUS 0x00 // comando di acquisizione STATUS -#define CMD_GET_MEASURES 0x01 // comando di acquisizione MISURE -#define CMD_GET_CONFIG 0x02 // comando di acquisizione CONFIGURAZIONE -#define CMD_GET_BATT_STAT 0x03 // comando di acquisizione Battery+Load Status -#define CMD_GET_BAT_LD 0x03 // comando di acquisizione Battery+Load Status -#define CMD_GET_MASK 0x07 // comando di acquisizione MASCHERA PUNTI -#define CMD_SET_TIMER 0x20 // comando di CONFIGURAZIONE TIMER -#define CMD_BATT_TEST 0x21 // comando di CONFIGURAZIONE TEST BATTERIA -#define CMD_GET_BATT_TEST 0x22 // comando di LETTURA TEST BATTERIA -#define CMD_SD_ONESHOT 0x40 // comando di SCRITTURA SHUTDOWN ONESHOT -#define CMD_GET_SD_ONESHOT 0x41 // comando di LETTURA SHUTDOWN ONESHOT -#define CMD_SET_SCHEDULE 0x42 // comando di SCRITTURA SCHEDULE -#define CMD_GET_SCHEDULE 0x43 // comando di LETTURA SCHEDULE -#define CMD_GET_EEP_BLOCK 0x50 // comando di LETTURA BLOCCO EEPROM -#define CMD_SET_EEP_BLOCK 0x51 // comando di SCRITTURA BLOCCO EEPROM -#define CMD_GET_EEP_SEED 0x52 // comando di acquisizione SEME PROGR. EEPROM -#define CMD_INIT 0xF0 // comando di REINIZIALIZZAZIONE UPS +#define LEN_GET_STATUS 1 /* comando di acquisizione STATUS */ +#define LEN_GET_MEASURES 1 /* comando di acquisizione MISURE */ +#define LEN_GET_CONFIG 1 /* comando di acquisizione CONFIGURAZIONE */ +#define LEN_GET_BATT_STAT 1 /* comando di acquisizione Battery+Load Status */ +#define LEN_GET_BAT_LD 1 /* comando di acquisizione Battery+Load Status */ +#define LEN_GET_MASK 1 /* comando di acquisizione MASCHERA PUNTI */ +#define LEN_SET_TIMER 5 /* comando di CONFIGURAZIONE TIMER */ +#define LEN_BATT_TEST 4 /* comando di CONFIGURAZIONE TEST BATTERIA */ +#define LEN_GET_BATT_TEST 1 /* comando di LETTURA TEST BATTERIA */ +#define LEN_SD_ONESHOT 8 /* comando di SCRITTURA SHUTDOWN ONESHOT */ +#define LEN_GET_SD_ONESHOT 1 /* comando di LETTURA SHUTDOWN ONESHOT */ +#define LEN_SET_SCHEDULE 8 /* comando di SCRITTURA SCHEDULE */ +#define LEN_GET_SCHEDULE 2 /* comando di LETTURA SCHEDULE */ +#define LEN_GET_EEP_BLOCK 3 /* comando di SCRITTURA BLOCCO EEPROM */ +#define LEN_SET_EEP_BLOCK 12 /* comando di SCRITTURA BLOCCO EEPROM */ +#define LEN_GET_EEP_SEED 1 /* comando di acquisizione SEME PROGR. EEPROM */ +#define LEN_INIT 1 /* comando di REINIZIALIZZAZIONE UPS */ -#define LEN_ACK 1 // ACK -#define LEN_NACK 2 // NACK - -#define LEN_GET_STATUS 1 // comando di acquisizione STATUS -#define LEN_GET_MEASURES 1 // comando di acquisizione MISURE -#define LEN_GET_CONFIG 1 // comando di acquisizione CONFIGURAZIONE -#define LEN_GET_BATT_STAT 1 // comando di acquisizione Battery+Load Status -#define LEN_GET_BAT_LD 1 // comando di acquisizione Battery+Load Status -#define LEN_GET_MASK 1 // comando di acquisizione MASCHERA PUNTI -#define LEN_SET_TIMER 5 // comando di CONFIGURAZIONE TIMER -#define LEN_BATT_TEST 4 // comando di CONFIGURAZIONE TEST BATTERIA -#define LEN_GET_BATT_TEST 1 // comando di LETTURA TEST BATTERIA -#define LEN_SD_ONESHOT 8 // comando di SCRITTURA SHUTDOWN ONESHOT -#define LEN_GET_SD_ONESHOT 1 // comando di LETTURA SHUTDOWN ONESHOT -#define LEN_SET_SCHEDULE 8 // comando di SCRITTURA SCHEDULE -#define LEN_GET_SCHEDULE 2 // comando di LETTURA SCHEDULE -#define LEN_GET_EEP_BLOCK 3 // comando di SCRITTURA BLOCCO EEPROM -#define LEN_SET_EEP_BLOCK 12 // comando di SCRITTURA BLOCCO EEPROM -#define LEN_GET_EEP_SEED 1 // comando di acquisizione SEME PROGR. EEPROM -#define LEN_INIT 1 // comando di REINIZIALIZZAZIONE UPS +/* non completamente definiti! */ +#define RET_GET_STATUS 10 /* comando di acquisizione STATUS */ +#define RET_GET_MEASURES 15 /* comando di acquisizione MISURE */ +#define RET_GET_CONFIG 11 /* comando di acquisizione CONFIGURAZIONE */ +#define RET_GET_BATT_STAT 10 /* comando di acquisizione Battery+Load Status */ +#define RET_GET_BAT_LD 10 /* comando di acquisizione Battery+Load Status */ +#define RET_GET_MASK 1 /* comando di acquisizione MASCHERA PUNTI */ +#define RET_SET_TIMER 5 /* comando di CONFIGURAZIONE TIMER */ +#define RET_BATT_TEST 4 /* comando di CONFIGURAZIONE TEST BATTERIA */ +#define RET_GET_BATT_TEST 1 /* comando di LETTURA TEST BATTERIA */ +#define RET_SD_ONESHOT 8 /* comando di SCRITTURA SHUTDOWN ONESHOT */ +#define RET_GET_SD_ONESHOT 1 /* comando di LETTURA SHUTDOWN ONESHOT */ +#define RET_SET_SCHEDULE 8 /* comando di SCRITTURA SCHEDULE */ +#define RET_GET_SCHEDULE 8 /* comando di LETTURA SCHEDULE */ +#define RET_GET_EEP_BLOCK 11 /* comando di SCRITTURA BLOCCO EEPROM */ +#define RET_SET_EEP_BLOCK 12 /* comando di SCRITTURA BLOCCO EEPROM */ +#define RET_GET_EEP_SEED 1 /* comando di acquisizione SEME PROGR. EEPROM */ +#define RET_INIT 1 /* comando di REINIZIALIZZAZIONE UPS */ -// non completamente definiti! -#define RET_GET_STATUS 10 // comando di acquisizione STATUS -#define RET_GET_MEASURES 15 // comando di acquisizione MISURE -#define RET_GET_CONFIG 11 // comando di acquisizione CONFIGURAZIONE -#define RET_GET_BATT_STAT 10 // comando di acquisizione Battery+Load Status -#define RET_GET_BAT_LD 10 // comando di acquisizione Battery+Load Status -#define RET_GET_MASK 1 // comando di acquisizione MASCHERA PUNTI -#define RET_SET_TIMER 5 // comando di CONFIGURAZIONE TIMER -#define RET_BATT_TEST 4 // comando di CONFIGURAZIONE TEST BATTERIA -#define RET_GET_BATT_TEST 1 // comando di LETTURA TEST BATTERIA -#define RET_SD_ONESHOT 8 // comando di SCRITTURA SHUTDOWN ONESHOT -#define RET_GET_SD_ONESHOT 1 // comando di LETTURA SHUTDOWN ONESHOT -#define RET_SET_SCHEDULE 8 // comando di SCRITTURA SCHEDULE -#define RET_GET_SCHEDULE 8 // comando di LETTURA SCHEDULE -#define RET_GET_EEP_BLOCK 11 // comando di SCRITTURA BLOCCO EEPROM -#define RET_SET_EEP_BLOCK 12 // comando di SCRITTURA BLOCCO EEPROM -#define RET_GET_EEP_SEED 1 // comando di acquisizione SEME PROGR. EEPROM -#define RET_INIT 1 // comando di REINIZIALIZZAZIONE UPS +/* ======================================================= */ +/* Indirizzi delle variabili memorizzate nella EEPROM: // */ +/* ======================================================= */ +#define EEP_OPT_BYTE_BLK 0x00 /* Option Bytes block */ +#define EEP_MAGIC 0x00 /* magic number: deve essere a 0xAA */ +#define EEP_CHECKSUM 0x01 /* XOR longitudinale da 0x?? a 0x?? */ +#define EEP_OUT_CONFIG 0x02 /* vedi documentazione */ +#define EEP_OPT_BYTE_0 0x02 /* vedi documentazione */ +#define EEP_OPT_BYTE_1 0x03 /* vedi documentazione */ +#define EEP_STATUS 0x04 /* Status UPS */ + +#define EEP_THRESHOLD_0 0x08 /* Blocco 0 con gli threshold */ +#define EEP_MEAN_MIN 0x08 /* UPS minimum AC voltage intervention point */ +#define EEP_MEAN_MAX 0x0A /* UPS maximum AC voltage intervention point */ +#define EEP_AVR_MIN 0x0C /* AVR minimum AC voltage intervention point */ +#define EEP_AVR_MAX 0x0E /* AVR maximum AC voltage intervention point */ + +#define EEP_THRESHOLD_1 0x10 /* Blocco 1 con gli threshold */ +#define EEP_BLOW_VALUE 0x10 /* Battery LOW threshold voltage */ +#define EEP_BEND_VALUE 0x12 /* Battery END threshold voltage */ +#define EEP_VMETER_0 0x14 /* Led 0 Voltage indicator threshold */ +#define EEP_VMETER_1 0x16 /* Led 1 Voltage indicator threshold */ + +#define EEP_THRESHOLD_2 0x18 /* Blocco 2 con gli threshold */ +#define EEP_VMETER_2 0x18 /* Led 2 Voltage indicator threshold */ +#define EEP_VMETER_3 0x1A /* Led 3 Voltage indicator threshold */ +#define EEP_VMETER_4 0x1C /* Led 4 Voltage indicator threshold */ + +#define EEP_FAULT_POINTS 0x20 /* number of fault points (in an half sine wave) after which USP enter battery mode */ +#define EEP_CHARGER_MIN 0x21 /* Threshold voltage for battery charger */ + +#define EEP_TEMP_MAX 0x28 /* Maximum temperature: over this value an alarm will be generated */ +#define EEP_TEMP_FAULT 0x2A /* Fault temperature: over this value, UPS will shut down immediately */ +#define EEP_FANSPEED_0 0x2C /* Temperature for fan activation (low speed) */ +#define EEP_FANSPEED_1 0x2E /* Temperature for fan activation (medium speed) */ + +#define EEP_FANSPEED_2 0x30 /* Temperature for fan activation (high speed) */ + +#define EEP_IOUT 0x38 /* */ +#define EEP_IOUT_OVRLD 0x38 /* Maximum current: over this value an alarm will be generated */ +#define EEP_IOUT_FAULT 0x3A /* Fault current: over this value, UPS will shut down immediately */ +#define EEP_PWRMTR_0 0x3C /* Led 0 Power indicator threshold */ +#define EEP_PWRMTR_1 0x3E /* Led 1 Power indicator threshold */ + +#define EEP_PWRMTR_2 0x40 /* Led 2 Power indicator threshold */ +#define EEP_PWRMTR_3 0x42 /* Led 3 Power indicator threshold */ +#define EEP_PWRMTR_4 0x44 /* Led 4 Power indicator threshold */ +#define EEP_PWRMTR_5 0x46 /* Between "Full power" and "Overload" Power indicator level */ + +#define EEP_INPUT_MASK_0 0x48 /* Half sine input mask (0-7 of 10 points) */ +#define EEP_INPUT_MASK_8 0x50 /* Half sine input mask (8-9 of 10 points) */ + +#define EEP_MIN_VBATT 0x60 /* Minimum battery voltaged reached from the last power-on */ +#define EEP_RUNTIME_H 0x62 /* Working time in minutes (max ~=32 years) (MSB first) */ +#define EEP_BAT_TIME_M 0x65 /* Time in Battery mode: seconds*2 (max ~=390 days). (MSB first) */ -//======================================================= -// Indirizzi delle variabili memorizzate nella EEPROM: // -//======================================================= -#define EEP_OPT_BYTE_BLK 0x00 // Option Bytes block -#define EEP_MAGIC 0x00 // magic number: deve essere a 0xAA -#define EEP_CHECKSUM 0x01 // XOR longitudinale da 0x?? a 0x?? -#define EEP_OUT_CONFIG 0x02 // vedi documentazione -#define EEP_OPT_BYTE_0 0x02 // vedi documentazione -#define EEP_OPT_BYTE_1 0x03 // vedi documentazione -#define EEP_STATUS 0x04 // Status UPS +#define EEP_UPS_MODEL 0x80 /* Model of the UPS in ASCII. For the Enterprise line, the structure is: */ + /* ENTxxyyy */ + /* where: xx = Power rating in watts/100 (zero padded at left) */ + /* yyy = model variants (if not defined, space padded to the right) */ -#define EEP_THRESHOLD_0 0x08 // Blocco 0 con gli threshold -#define EEP_MEAN_MIN 0x08 // UPS minimum AC voltage intervention point -#define EEP_MEAN_MAX 0x0A // UPS maximum AC voltage intervention point -#define EEP_AVR_MIN 0x0C // AVR minimum AC voltage intervention point -#define EEP_AVR_MAX 0x0E // AVR maximum AC voltage intervention point +#define EEP_NETWRK_ID 0x88 /* Network ID in ASCII (not used) - space padded */ +#define EEP_NETWRK_NAME 0x90 /* Network Name in ASCII (not used) - space padded */ +#define EEP_SERIAL_NUM 0x98 /* Serial Number in ASCII - zero padded (at left) */ -#define EEP_THRESHOLD_1 0x10 // Blocco 1 con gli threshold -#define EEP_BLOW_VALUE 0x10 // Battery LOW threshold voltage -#define EEP_BEND_VALUE 0x12 // Battery END threshold voltage -#define EEP_VMETER_0 0x14 // Led 0 Voltage indicator threshold -#define EEP_VMETER_1 0x16 // Led 1 Voltage indicator threshold - -#define EEP_THRESHOLD_2 0x18 // Blocco 2 con gli threshold -#define EEP_VMETER_2 0x18 // Led 2 Voltage indicator threshold -#define EEP_VMETER_3 0x1A // Led 3 Voltage indicator threshold -#define EEP_VMETER_4 0x1C // Led 4 Voltage indicator threshold - -#define EEP_FAULT_POINTS 0x20 // number of fault points (in an half sine wave) after which USP enter battery mode -#define EEP_CHARGER_MIN 0x21 // Threshold voltage for battery charger - -#define EEP_TEMP_MAX 0x28 // Maximum temperature: over this value an alarm will be generated -#define EEP_TEMP_FAULT 0x2A // Fault temperature: over this value, UPS will shut down immediately -#define EEP_FANSPEED_0 0x2C // Temperature for fan activation (low speed) -#define EEP_FANSPEED_1 0x2E // Temperature for fan activation (medium speed) - -#define EEP_FANSPEED_2 0x30 // Temperature for fan activation (high speed) - -#define EEP_IOUT 0x38 // -#define EEP_IOUT_OVRLD 0x38 // Maximum current: over this value an alarm will be generated -#define EEP_IOUT_FAULT 0x3A // Fault current: over this value, UPS will shut down immediately -#define EEP_PWRMTR_0 0x3C // Led 0 Power indicator threshold -#define EEP_PWRMTR_1 0x3E // Led 1 Power indicator threshold - -#define EEP_PWRMTR_2 0x40 // Led 2 Power indicator threshold -#define EEP_PWRMTR_3 0x42 // Led 3 Power indicator threshold -#define EEP_PWRMTR_4 0x44 // Led 4 Power indicator threshold -#define EEP_PWRMTR_5 0x46 // Between "Full power" and "Overload" Power indicator level - -#define EEP_INPUT_MASK_0 0x48 // Half sine input mask (0-7 of 10 points) -#define EEP_INPUT_MASK_8 0x50 // Half sine input mask (8-9 of 10 points) - -#define EEP_MIN_VBATT 0x60 // Minimum battery voltaged reached from the last power-on -#define EEP_RUNTIME_H 0x62 // Working time in minutes (max ~=32 years) (MSB first) -#define EEP_BAT_TIME_M 0x65 // Time in Battery mode: seconds*2 (max ~=390 days). (MSB first) +#define EEP_PROD_DATE 0xA0 /* */ +#define EEP_PROD_YEAR 0xA0 /* Year of production (add 2000) */ +#define EEP_PROD_WEEK 0xA1 /* Week of production [0-51] */ +#define EEP_PROD_HW_VER 0xA2 /* Hardware version: 2 nibbles ? Major and Minor number. */ +#define EEP_PROD_BRD_VER 0xA3 /* Board version: 2 nibbles ? Major and Minor number. */ +#define EEP_PROD_FW_VER 0xA4 /* Firmware version: 2 nibbles ? Major and Minor number. */ +#define EEP_PROD_FW_SVER 0xA5 /* Firmware SUBversion: 2 nibbles ? Major and Minor number. */ +#define EEP_PROD_BTTRS 0xA6 /* number of batteries installed in the UPS */ -#define EEP_UPS_MODEL 0x80 // Model of the UPS in ASCII. For the Enterprise line, the structure is: - // ENTxxyyy - // where: xx = Power rating in watts/100 (zero padded at left) - // yyy = model variants (if not defined, space padded to the right) - -#define EEP_NETWRK_ID 0x88 // Network ID in ASCII (not used) - space padded -#define EEP_NETWRK_NAME 0x90 // Network Name in ASCII (not used) - space padded -#define EEP_SERIAL_NUM 0x98 // Serial Number in ASCII - zero padded (at left) - -#define EEP_PROD_DATE 0xA0 // -#define EEP_PROD_YEAR 0xA0 // Year of production (add 2000) -#define EEP_PROD_WEEK 0xA1 // Week of production [0-51] -#define EEP_PROD_HW_VER 0xA2 // Hardware version: 2 nibbles ? Major and Minor number. -#define EEP_PROD_BRD_VER 0xA3 // Board version: 2 nibbles ? Major and Minor number. -#define EEP_PROD_FW_VER 0xA4 // Firmware version: 2 nibbles ? Major and Minor number. -#define EEP_PROD_FW_SVER 0xA5 // Firmware SUBversion: 2 nibbles ? Major and Minor number. -#define EEP_PROD_BTTRS 0xA6 // number of batteries installed in the UPS +#define EEP_BATT_SUBST 0xA8 /* battery replacement block */ +#define EEP_BATT_YEAR_0 0xA8 /* Year of 1st battery replacement (add 2000) */ +#define EEP_BATT_WEEK_0 0xA9 /* Week of 1st battery replacement */ +#define EEP_BATT_YEAR_1 0xAA /* Year of 2nd battery replacement (add 2000) */ +#define EEP_BATT_WEEK_1 0xAB /* Week of 2nd battery replacement */ +#define EEP_BATT_YEAR_2 0xAC /* Year of 3rd battery replacement (add 2000) */ +#define EEP_BATT_WEEK_2 0xAD /* Week of 3rd battery replacement */ +#define EEP_BATT_YEAR_3 0xAE /* Year of 4th battery replacement (add 2000) */ +#define EEP_BATT_WEEK_3 0xAF /* Week of 4th battery replacement */ -#define EEP_BATT_SUBST 0xA8 // battery replacement block -#define EEP_BATT_YEAR_0 0xA8 // Year of 1st battery replacement (add 2000) -#define EEP_BATT_WEEK_0 0xA9 // Week of 1st battery replacement -#define EEP_BATT_YEAR_1 0xAA // Year of 2nd battery replacement (add 2000) -#define EEP_BATT_WEEK_1 0xAB // Week of 2nd battery replacement -#define EEP_BATT_YEAR_2 0xAC // Year of 3rd battery replacement (add 2000) -#define EEP_BATT_WEEK_2 0xAD // Week of 3rd battery replacement -#define EEP_BATT_YEAR_3 0xAE // Year of 4th battery replacement (add 2000) -#define EEP_BATT_WEEK_3 0xAF // Week of 4th battery replacement +#define EEP_SCHEDULE 0xC8 /* Start of SCHEDULING table */ +#define EEP_SCHEDULE_0 0xC8 /* 1st Scheduling slot */ +#define EEP_SCHEDULE_1 0xD0 /* 2nd Scheduling slot */ +#define EEP_SCHEDULE_2 0xD8 /* 3rd Scheduling slot */ +#define EEP_SCHEDULE_3 0xE0 /* 4th Scheduling slot */ +#define EEP_SCHEDULE_4 0xE8 /* 5th Scheduling slot */ +#define EEP_SCHEDULE_5 0xF0 /* 6th Scheduling slot */ -#define EEP_SCHEDULE 0xC8 // Start of SCHEDULING table -#define EEP_SCHEDULE_0 0xC8 // 1st Scheduling slot -#define EEP_SCHEDULE_1 0xD0 // 2nd Scheduling slot -#define EEP_SCHEDULE_2 0xD8 // 3rd Scheduling slot -#define EEP_SCHEDULE_3 0xE0 // 4th Scheduling slot -#define EEP_SCHEDULE_4 0xE8 // 5th Scheduling slot -#define EEP_SCHEDULE_5 0xF0 // 6th Scheduling slot - - -#define BIT_NO_COMM_UPS 0x0001 // Event 2 - communications with the UPS lost -#define BIT_COMM_OK 0x0002 // Event 3 - communications with the UPS reestablished -#define BIT_BATTERY_MODE 0x0004 // Event 5 - the UPS is in Battery mode -#define BIT_BATTERY_LOW 0x0008 // Event 6 - the UPS has a LOW battery level -#define BIT_MAINS_OK 0x0010 // Event 7 - return of the mains -#define BIT_OVERLOAD 0x0020 // Event 8 - the UPS is overloaded -#define BIT_HIGH_TEMPERATURE 0x0040 // Event 9 - the UPS is in overtemperature -#define BIT_GENERIC_FAULT 0x0080 // Event 11 - there is a generic fault -#define BIT_SYSTEM_SHUTDOWN 0x0100 // Event 14 - the UPS will shut down IMMEDIATELY -#define BIT_STANDBY 0x0200 // Event 32 - the UPS went in STANDBY MODE -#define BIT_BATTERY_END 0x0400 // Event 33 - the UPS went in battery END -#define BIT_EVENT_0 0x1000 // evento ?? -#define BIT_EVENT_1 0x2000 // evento ?? -#define BIT_FIRST_TIME 0x4000 // se è la prima volta che chiamo la funzione -#define BIT_POLL_RESTART 0x8000 // se riesco ad identificare l'UPS dopo lo scollegamento, reinvio i dati +#define BIT_NO_COMM_UPS 0x0001 /* Event 2 - communications with the UPS lost */ +#define BIT_COMM_OK 0x0002 /* Event 3 - communications with the UPS reestablished */ +#define BIT_BATTERY_MODE 0x0004 /* Event 5 - the UPS is in Battery mode */ +#define BIT_BATTERY_LOW 0x0008 /* Event 6 - the UPS has a LOW battery level */ +#define BIT_MAINS_OK 0x0010 /* Event 7 - return of the mains */ +#define BIT_OVERLOAD 0x0020 /* Event 8 - the UPS is overloaded */ +#define BIT_HIGH_TEMPERATURE 0x0040 /* Event 9 - the UPS is in overtemperature */ +#define BIT_GENERIC_FAULT 0x0080 /* Event 11 - there is a generic fault */ +#define BIT_SYSTEM_SHUTDOWN 0x0100 /* Event 14 - the UPS will shut down IMMEDIATELY */ +#define BIT_STANDBY 0x0200 /* Event 32 - the UPS went in STANDBY MODE */ +#define BIT_BATTERY_END 0x0400 /* Event 33 - the UPS went in battery END */ +#define BIT_EVENT_0 0x1000 /* evento ?? */ +#define BIT_EVENT_1 0x2000 /* evento ?? */ +#define BIT_FIRST_TIME 0x4000 /* se è la prima volta che chiamo la funzione */ +#define BIT_POLL_RESTART 0x8000 /* se riesco ad identificare l'UPS dopo lo scollegamento, reinvio i dati */ typedef struct - { - int Port ; // porta a cui è collegato l' UPS: - // 0 = USB - // n = COMn - char Opened ; // BOOL flag che identifica se la porta è aperta - int ErrCode ; // ultimo codice di errore in fase di lettura - int ErrCount ; // conteggio degli errori - unsigned char PollFlag ; // identifica se posso accedere all'UPS - // 0x01: se TRUE, polling abilitato - // 0x02: se TRUE, la routine di polling dati è attiva: devo attendere - // 0x04: se TRUE, bisogna rileggere la configurazione del gruppo - //------------------------------------------------------------------------ - unsigned long Counter ; // contatore: viene incrementato ad ogni nuovo POLL - unsigned char CommStatus ; // stato delle comunicazioni - unsigned char FramePointer ; // puntatore al carattere di START dei dati ricevuti - //------------------------------------------------------------------------ - char UpsModel[9] ; // modello UPS (8 caratteri) - unsigned char ge_2kVA ; // if more or equal to 2KVA - char SerialNumber[9] ; // numero di serie dell'UPS +{ + int Port ; /* porta a cui è collegato l' UPS: */ + /* 0 = USB */ + /* n = COMn */ + char Opened ; /* BOOL flag che identifica se la porta è aperta */ + int ErrCode ; /* ultimo codice di errore in fase di lettura */ + int ErrCount ; /* conteggio degli errori */ + unsigned char PollFlag ; /* identifica se posso accedere all'UPS */ + /* 0x01: se TRUE, polling abilitato */ + /* 0x02: se TRUE, la routine di polling dati è attiva: devo attendere */ + /* 0x04: se TRUE, bisogna rileggere la configurazione del gruppo */ + /* ------------------------------------------------------------------------ */ + unsigned long Counter ; /* contatore: viene incrementato ad ogni nuovo POLL */ + unsigned char CommStatus ; /* stato delle comunicazioni */ + /* unsigned char FramePointer ; */ + size_t FramePointer ; /* puntatore al carattere di START dei dati ricevuti */ + /* ------------------------------------------------------------------------ */ + char UpsModel[9] ; /* modello UPS (8 caratteri) */ + unsigned char ge_2kVA ; /* if more or equal to 2KVA */ + char SerialNumber[9] ; /* numero di serie dell'UPS */ - unsigned short int YearOfProd ; // anno di produzione dell'UPS - unsigned char MonthOfProd ; // Mese di produzione dell'UPS - unsigned char DayOfProd ; // Giorno di produzione dell'UPS - unsigned char HW_MajorVersion ; // Hardware: Major version - unsigned char HW_MinorVersion ; // Hardware: Minor version - unsigned char BR_MajorVersion ; // BoardHardware: Major version - unsigned char BR_MinorVersion ; // BoardHardware: Minor version - unsigned char FW_MajorVersion ; // Firmware: Major version - unsigned char FW_MinorVersion ; // Firmware: Minor version - unsigned char FW_SubVersion ; // Firmware: SUBVERSION (special releases - // for particular customers) + unsigned short int YearOfProd ; /* anno di produzione dell'UPS */ + unsigned char MonthOfProd ; /* Mese di produzione dell'UPS */ + unsigned char DayOfProd ; /* Giorno di produzione dell'UPS */ + unsigned char HW_MajorVersion ; /* Hardware: Major version */ + unsigned char HW_MinorVersion ; /* Hardware: Minor version */ + unsigned char BR_MajorVersion ; /* BoardHardware: Major version */ + unsigned char BR_MinorVersion ; /* BoardHardware: Minor version */ + unsigned char FW_MajorVersion ; /* Firmware: Major version */ + unsigned char FW_MinorVersion ; /* Firmware: Minor version */ + unsigned char FW_SubVersion ; /* Firmware: SUBVERSION (special releases */ + /* for particular customers) */ - unsigned char BatteryNumber ; // number of batteries in UPS - //------------------------------------------------------------------------ - unsigned long StatusUPS ; // flag di stato dell'UPS 4 byte): 1=TRUE - // bit 0 => BATTERY_MODE - // bit 1 => BATTERY_LOW - // bit 2 => BATTERY_END - // bit 3 => ONLINE (funzionamento in rete) - // bit 4 => MAINS_ON (rete di ingresso presente) - // bit 5 => STANDBY (UPS in modo Standby) - // bit 6 => WAIT_MAINS (UPS in fase di INIT + attesa rete) - // bit 7 => INIT (UPS in fase di inizializzazione) - // ------ - // bit 8 => MASK_OK (semionda OK) - // bit 9 => MEAN_OK (media tensione di ingresso OK) - // bit 10 => SYNC_OK (sincronizzazione semionda OK) - // bit 11 => FAULT (generico) - // bit 12 => TEMP_MAX (superato livello critico di temperatura) - // bit 13 => TEMP_FAULT (Fault da temperatura: UPS si spegne) - // bit 14 => LOAD_MAX (soglia overload superata) - // bit 15 => LOAD_FAULT (Fault da carico: UPS si spegne) - // ------ - // bit 16 => INV_FAULT (Fault dell'inverter) - // bit 17 => IINV_MAX (eccessiva corrente sull'inverter) - // bit 18 => IINV_FAULT (Fault sulla corrente inverter: l'UPS si spegne) - // bit 19 => 50_60Hz (1=60Hz) - // bit 20 => EEP_FAULT (problemi con la EEPROM) - // bit 21 => VOUT_FAULT (tensione uscita troppo bassa) - // bit 22 => - non definito - - // bit 23 => - non definito - - // ------ - // bit 24 to 31 => - NON DEFINITI - + unsigned char BatteryNumber ; /* number of batteries in UPS */ + /* ------------------------------------------------------------------------ */ + uint32_t StatusUPS ; /* flag di stato dell'UPS 4 byte): 1=TRUE */ + /* bit 0 => BATTERY_MODE */ + /* bit 1 => BATTERY_LOW */ + /* bit 2 => BATTERY_END */ + /* bit 3 => ONLINE (funzionamento in rete) */ + /* bit 4 => MAINS_ON (rete di ingresso presente) */ + /* bit 5 => STANDBY (UPS in modo Standby) */ + /* bit 6 => WAIT_MAINS (UPS in fase di INIT + attesa rete) */ + /* bit 7 => INIT (UPS in fase di inizializzazione) */ + /* ------ */ + /* bit 8 => MASK_OK (semionda OK) */ + /* bit 9 => MEAN_OK (media tensione di ingresso OK) */ + /* bit 10 => SYNC_OK (sincronizzazione semionda OK) */ + /* bit 11 => FAULT (generico) */ + /* bit 12 => TEMP_MAX (superato livello critico di temperatura) */ + /* bit 13 => TEMP_FAULT (Fault da temperatura: UPS si spegne) */ + /* bit 14 => LOAD_MAX (soglia overload superata) */ + /* bit 15 => LOAD_FAULT (Fault da carico: UPS si spegne) */ + /* ------ */ + /* bit 16 => INV_FAULT (Fault dell'inverter) */ + /* bit 17 => IINV_MAX (eccessiva corrente sull'inverter) */ + /* bit 18 => IINV_FAULT (Fault sulla corrente inverter: l'UPS si spegne) */ + /* bit 19 => 50_60Hz (1=60Hz) */ + /* bit 20 => EEP_FAULT (problemi con la EEPROM) */ + /* bit 21 => VOUT_FAULT (tensione uscita troppo bassa) */ + /* bit 22 => - non definito - */ + /* bit 23 => - non definito - */ + /* ------ */ + /* bit 24 to 31 => - NON DEFINITI - */ - unsigned short int ShortStatus ; // the LSB 2 bytes of the status - unsigned char OutConfig ; // stato uscite UPS - float Vinput ; // tensione di INPUT in 1/10 di Volt - float Voutput ; // tensione di OUTPUT in 1/10 di Volt - float Temp ; // temperatura in 1/10 di grado - float InpFreq ; // Frequenza di INPUT in 1/10 di Hz - float OutFreq ; // Frequenza di OUTPUT in 1/10 di Hz - float OutCurrent ; // Corrente di Output in 1/10 di A - unsigned char LoadPerc ; // Percentuale di carico - unsigned short int LoadVA ; // Carico in VA - float ChgCurrent ; // corrente carica batterie + uint16_t ShortStatus ; /* the LSB 2 bytes of the status */ + unsigned char OutConfig ; /* stato uscite UPS */ + float Vinput ; /* tensione di INPUT in 1/10 di Volt */ + float Voutput ; /* tensione di OUTPUT in 1/10 di Volt */ + float Temp ; /* temperatura in 1/10 di grado */ + float InpFreq ; /* Frequenza di INPUT in 1/10 di Hz */ + float OutFreq ; /* Frequenza di OUTPUT in 1/10 di Hz */ + float OutCurrent ; /* Corrente di Output in 1/10 di A */ + unsigned char LoadPerc ; /* Percentuale di carico */ + unsigned short int LoadVA ; /* Carico in VA */ + float ChgCurrent ; /* corrente carica batterie */ - float Vbatt ; // tensione delle batterie in 1/10 di Volt - unsigned char PercBatt ; // percentuale di carica della batteria - unsigned short int RtimeEmpty; // minuti alla scarica della batteria - unsigned char PercEmpty ; // percentuale alla scarica: 0%=scarica, 100%=carica - unsigned char OutStatus ; // stato delle uscite + float Vbatt ; /* tensione delle batterie in 1/10 di Volt */ + unsigned char PercBatt ; /* percentuale di carica della batteria */ + unsigned short int RtimeEmpty; /* minuti alla scarica della batteria */ + unsigned char PercEmpty ; /* percentuale alla scarica: 0%=scarica, 100%=carica */ + unsigned char OutStatus ; /* stato delle uscite */ - unsigned char Year ; // - unsigned char Month ; // - unsigned char Day ; // - unsigned char WeekDay ; // Giorno della settimana - unsigned char Hour ; // Ora - unsigned char Min ; // minuti - unsigned char Sec ; // secondi + unsigned char Year ; /* */ + unsigned char Month ; /* */ + unsigned char Day ; /* */ + unsigned char WeekDay ; /* Giorno della settimana */ + unsigned char Hour ; /* Ora */ + unsigned char Min ; /* minuti */ + unsigned char Sec ; /* secondi */ - unsigned char BattLowPerc ; // percentuale carica batteria quando va in BLOW - unsigned long Rtime ; // numero di minuti di accensione dell'UPS - unsigned long RtimeBatt ; // numero di secondi*2 in modalità batteria dall'accensione - //------------------------------------------------------------------------ - unsigned int ShutdownDelay ; // Shutdown delay in seconds - unsigned int WakeUpDelay ; // WakeUp delay in seconds - } ENT_STRUCT ; + unsigned char BattLowPerc ; /* percentuale carica batteria quando va in BLOW */ + unsigned long Rtime ; /* numero di minuti di accensione dell'UPS */ + unsigned long RtimeBatt ; /* numero di secondi*2 in modalità batteria dall'accensione */ + /* ------------------------------------------------------------------------ */ + unsigned int ShutdownDelay ; /* Shutdown delay in seconds */ + unsigned int WakeUpDelay ; /* WakeUp delay in seconds */ +} ENT_STRUCT ; - - -#endif // ENTERPRISE_PROTOCOL +#endif /* ENTERPRISE_PROTOCOL */ #endif diff --git a/drivers/microsol-apc.c b/drivers/microsol-apc.c new file mode 100644 index 0000000..20e0cd3 --- /dev/null +++ b/drivers/microsol-apc.c @@ -0,0 +1,188 @@ +/* microsol-apc.c - APC Back-UPS BR series UPS driver + + Copyright (C) 2004 Silvino B. Magalhães + 2019 Roberto Panerai Velloso + 2021 Ygor A. S. Regados + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + 2021/03/19 - Version 0.70 - Initial release, based on solis driver + +*/ + +#include "config.h" /* must be first */ + +#include +#include +#include "main.h" +#include "serial.h" +#include "nut_float.h" +#include "timehead.h" + +#include "microsol-common.h" +#include "microsol-apc.h" + +#define DRIVER_NAME "APC Back-UPS BR series UPS driver" +#define DRIVER_VERSION "0.69" + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Silvino B. Magalhães " + "Roberto Panerai Velloso " + "Ygor A. S. Regados ", + DRV_STABLE, + { NULL } +}; + +#define false 0 +#define true 1 +#define RESP_END 0xFE +#define ENDCHAR 13 /* replies end with CR */ +/* solis commands */ +#define CMD_UPSCONT 0xCC +#define CMD_SHUT 0xDD +#define CMD_SHUTRET 0xDE +#define CMD_EVENT 0xCE +#define CMD_DUMP 0xCD + +/** Check if UPS model is available here. */ +bool_t ups_model_defined(void) +{ + unsigned int model_index; + + for (model_index = 0; MODELS[model_index] != ups_model; model_index++); + if (model_index == MODEL_COUNT) { + return 0; + } else { + return 1; + } +} + +/** Set UPS model name. */ +void set_ups_model(void) +{ + switch (ups_model) { + case 183: + model_name = "BZ2200I-BR"; + break; + case 190: + model_name = "BZ1500-BR"; + break; + case 191: + model_name = "BZ2200BI-BR"; + break; + default: + model_name = "Unknown UPS"; + } +} + +/** + * Parse received packet with UPS instantaneous data. + * This function parses model-specific values, such as voltage and battery times. + */ +void scan_received_pack_model_specific(void) +{ + unsigned int relay_state; + unsigned int model_index; + float real_power_curve_1, real_power_curve_2, real_power_curve_3; + float power_difference_1, power_difference_2, power_difference_3; + bool_t recharging; + + /* Extract unprocessed data from packet */ + input_voltage = received_packet[2]; + output_voltage = received_packet[1]; + output_current = received_packet[5]; + battery_voltage = received_packet[3]; + + relay_state = (received_packet[6] & 0x28) >> 3; + + /* Find array indexes for detected UPS model */ + for (model_index = 0; MODELS[model_index] != ups_model && model_index < MODEL_COUNT - 1; model_index++); + if (MODELS[model_index] != ups_model) { + upslogx(LOG_NOTICE, "UPS model not found, using fallback option."); + } + + /* Start processing according to model */ + nominal_power = NOMINAL_POWER[model_index]; + + input_voltage = INPUT_VOLTAGE_MULTIPLIER_A[model_index][output_220v] * input_voltage + INPUT_VOLTAGE_MULTIPLIER_B[model_index][output_220v]; + + battery_voltage = BATTERY_VOLTAGE_MULTIPLIER_A[model_index] * battery_voltage + BATTERY_VOLTAGE_MULTIPLIER_B[model_index]; + + output_current = OUTPUT_CURRENT_MULTIPLIER_A[model_index][line_unpowered] * output_current + OUTPUT_CURRENT_MULTIPLIER_B[model_index][line_unpowered]; + + if (ups_model == 190 && line_unpowered) { + /* Special calculation for BZ1500 on battery */ + output_voltage = battery_voltage * sqrt(output_voltage / 64.0) * OUTPUT_VOLTAGE_MULTIPLIER_A[model_index][line_unpowered][relay_state] + - output_current * OUTPUT_VOLTAGE_MULTIPLIER_B[model_index][line_unpowered][relay_state]; + output_voltage = 1.5091 * output_voltage + 1.5823; + + if (output_current > 4.0) + output_voltage += output_current * 4.0; + + if (output_current > 3.0) + output_voltage += output_current * 2.0; + else if (output_voltage > 0.9) + output_voltage += output_current / 3.0; + else + output_voltage -= 5.0; + + if (output_voltage < 100.0) + output_voltage = 100; + } else { + output_voltage = OUTPUT_VOLTAGE_MULTIPLIER_A[model_index][line_unpowered][relay_state] * output_voltage + OUTPUT_VOLTAGE_MULTIPLIER_B[model_index][line_unpowered][relay_state]; + } + + if (line_unpowered) { + input_frequency = 0; + output_frequency = 60; + } else { + input_frequency = 0.37 * (257 - ((received_packet[21] + received_packet[22] * 256) >> 8)); + output_frequency = input_frequency; + } + + apparent_power = output_current * output_voltage; + + real_power = received_packet[7] + 256 * received_packet[8]; + real_power_curve_1 = REAL_POWER_CURVE_SELECTOR_A1[model_index][relay_state] * real_power + REAL_POWER_CURVE_SELECTOR_B1[model_index][relay_state]; + real_power_curve_2 = REAL_POWER_CURVE_SELECTOR_A2[model_index][relay_state] * real_power + REAL_POWER_CURVE_SELECTOR_B2[model_index][relay_state]; + real_power_curve_3 = REAL_POWER_CURVE_SELECTOR_A3[model_index][relay_state] * real_power + REAL_POWER_CURVE_SELECTOR_B3[model_index][relay_state]; + + power_difference_1 = fabs(real_power_curve_1 - apparent_power); + power_difference_2 = fabs(real_power_curve_2 - apparent_power); + power_difference_3 = fabs(real_power_curve_3 - apparent_power); + + if (power_difference_1 < power_difference_2 && power_difference_1 < power_difference_3) { + real_power = REAL_POWER_MULTIPLIER_A1[model_index][relay_state] * real_power + REAL_POWER_MULTIPLIER_B1[model_index][relay_state]; + } else if (power_difference_2 < power_difference_3) { + real_power = REAL_POWER_MULTIPLIER_A2[model_index][relay_state] * real_power + REAL_POWER_MULTIPLIER_B2[model_index][relay_state]; + } else { + real_power = REAL_POWER_MULTIPLIER_A3[model_index][relay_state] * real_power + REAL_POWER_MULTIPLIER_B3[model_index][relay_state]; + } + + /* If real power is greater than apparent power, invert values */ + if (apparent_power < real_power) { + apparent_power = apparent_power + real_power; + real_power = apparent_power - real_power; + apparent_power = apparent_power - real_power; + } + + input_current = 1.1 * apparent_power / input_voltage; + + recharging = (0x02 & received_packet[20]) == 0x02; + battery_charge = (100.0 * (battery_voltage - MIN_BATTERY_VOLTAGE[model_index])) / (MAX_BATTERY_VOLTAGE[model_index][recharging] - MIN_BATTERY_VOLTAGE[model_index]); +} diff --git a/drivers/microsol-apc.h b/drivers/microsol-apc.h new file mode 100644 index 0000000..6f38d28 --- /dev/null +++ b/drivers/microsol-apc.h @@ -0,0 +1,230 @@ +/* microsol-apc.h - APC Back-UPS BR series UPS driver + + Copyright (C) 2004 Silvino B. Magalhaes + 2019 Roberto Panerai Velloso + 2021 Ygor A. S. Regados + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + 2021/03/19 - Version 0.70 - Initial release, based on solis driver + +*/ + +#ifndef INCLUDED_MICROSOL_APC_H +#define INCLUDED_MICROSOL_APC_H + +#define MODEL_COUNT 3 + +/* Supported UPS models */ +static const unsigned int MODELS[MODEL_COUNT] = { + 183 /* APC Back-UPS BZ2200I-BR */ , + 190 /* APC Back-UPS BZ1500-BR */ , + 191 /* APC Back-UPS BZ2200BI-BR */ +}; + +/* Note: int type here is aligned with the "nominal_power" + * variable in microsol-common.h and many related drivers. + */ +static const int NOMINAL_POWER[MODEL_COUNT] = { + 2200 /* Model 183 */ , + 1500 /* Model 190 */ , + 2200 /* Model 191 */ +}; + +/* Curves for output voltage (depends on relay state and battery state) + * Second index: On-line (0) or On-battery (1) + * Third index: Electric relay state + */ +/* For on-line UPS */ +static const float OUTPUT_VOLTAGE_MULTIPLIER_A[MODEL_COUNT][2][8] = { + { + { 1.831, 1.831, 1.831, 1.831, 1.831, 1.831, 1.831, 1.831}, + { 0.1566, 0.1566, 0.1566, 0.1566, 0.1566, 0.1566, 0.1566, 0.1566} + } /* Model 183 */ , + { + { 0.9266, 0.9266, 0.9266, 0.9266, 0.9266, 0.9266, 0.9266, 0.9266}, + { 5.59, 9.47, 13.7, 0.0, 0.0, 0.0, 0.0, 0.0} + } /* Model 190 */ , + { + { 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00, 1.00}, + { 1.26, 1.26, 1.26, 1.26, 1.26, 1.26, 1.26, 1.26} + } /* Model 191 */ +}; + +static const float OUTPUT_VOLTAGE_MULTIPLIER_B[MODEL_COUNT][2][8] = { + { + { -2.1374, -2.1374, -2.1374, -2.1374, -2.1374, -2.1374, -2.1374, -2.1374}, + { 204.93, 204.93, 204.93, 204.93, 204.93, 204.93, 204.93, 204.93} + } /* Model 183 */ , + { + { 5.0694, 5.0694, 5.0694, 5.0694, 5.0694, 5.0694, 5.0694, 5.0694}, + { 5.4, 6.5, 17.6, 0.0, 0.0, 0.0, 0.0, 0.0} + } /* Model 190 */ , + { + { 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0}, + { -5.91, -5.91, -5.91, -5.91, -5.91, -5.91, -5.91, -5.91} + } /* Model 191 */ +}; + +/* Curves for output current + * Second index: On-line (0) or On-battery (1) + */ +static const float OUTPUT_CURRENT_MULTIPLIER_A[MODEL_COUNT][2] = { + { + 0.0892999991774559, + 0.09070000052452087 } /* Model 183 */ , + { + 0.1264, + 0.1303 } /* Model 190 */ , + { + 0.13819999992847443, + 0.08959999680519104 } /* Model 191 */ , +}; + +static const float OUTPUT_CURRENT_MULTIPLIER_B[MODEL_COUNT][2] = { + { + 0.09350000321865082, + 0.14550000429153442 } /* Model 183 */ , + { + 0.522, + 0.468 } /* Model 190 */ , + { + 0.12999999523162842, + 0.1881999969482422 } /* Model 191 */ +}; + +/* Curves for input voltage (depends on nominal output voltage) + * Second index: Nominal output voltage = 220V (1) or 115V (0). + */ +static const float INPUT_VOLTAGE_MULTIPLIER_A[MODEL_COUNT][2] = { + { 1.7937, 1.7937 } /* Model 183 */ , + { 1.8, 1.8 } /* Model 190 */ , + { 1.4825, 1.4952 } /* Model 191 */ +}; + +static const float INPUT_VOLTAGE_MULTIPLIER_B[MODEL_COUNT][2] = { + { 1.854, 1.854 } /* Model 183 */ , + { 2.224, 2.224 } /* Model 190 */ , + { 0.0853, -2.4241 } /* Model 191 */ +}; + +/* Curves for battery voltage */ +static const float BATTERY_VOLTAGE_MULTIPLIER_A[MODEL_COUNT] = { + 0.1551 /* Model 183 */ , + 0.1513 /* Model 190 */ , + 0.1543 /* Model 191 */ +}; + +static const float BATTERY_VOLTAGE_MULTIPLIER_B[MODEL_COUNT] = { + 0.0654 /* Model 183 */ , + 0.7153 /* Model 190 */ , + 0.1414 /* Model 191 */ +}; + +/* Real power estimation curves (depends on relay state) */ +/* + * Remarks: + * - Model 190 use a direct real power determination (no need for curve selectors) + */ +static const float REAL_POWER_CURVE_SELECTOR_A1[MODEL_COUNT][8] = { + { 0.24, 0.26, 0.0, 0.0, 0.24, 0.26, 0.0, 0.28 } /* Model 183 */ , + { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } /* Model 190 */ , + { 0.24, 0.26, 0.0, 0.0, 0.24, 0.26, 0.0, 0.28 } /* Model 191 */ +}; + +static const float REAL_POWER_CURVE_SELECTOR_B1[MODEL_COUNT][8] = { + { 83.15, 81.23, 0.0, 0.0, 83.49, 79.05, 0.0, 85.67 } /* Model 183 */ , + { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } /* Model 190 */ , + { 83.15, 81.23, 0.0, 0.0, 83.49, 79.05, 0.0, 85.67 } /* Model 191 */ +}; + +static const float REAL_POWER_CURVE_SELECTOR_A2[MODEL_COUNT][8] = { + { 0.0763, 0.081, 0.0919, 0.0, 0.0741, 0.0828, 0.0, 0.0938 } /* Model 183 */ , + { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } /* Model 190 */ , + { 0.0763, 0.081, 0.0919, 0.0, 0.0741, 0.0828, 0.0, 0.0938 } /* Model 191 */ +}; + +static const float REAL_POWER_CURVE_SELECTOR_B2[MODEL_COUNT][8] = { + { 81.732, 94.459, 86.686, 0.0, 84.657, 84.999, 0.0, 78.097 } /* Model 183 */ , + { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } /* Model 190 */ , + { 81.732, 94.459, 86.686, 0.0, 84.657, 84.999, 0.0, 78.097 } /* Model 191 */ +}; + +static const float REAL_POWER_CURVE_SELECTOR_A3[MODEL_COUNT][8] = { + { 0.0744, 0.0808, 0.0885, 0.0, 0.0732, 0.084, 0.0, 0.0955 } /* Model 183 */ , + { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } /* Model 190 */ , + { 0.0744, 0.0808, 0.0885, 0.0, 0.0732, 0.084, 0.0, 0.0955 } /* Model 191 */ +}; + +static const float REAL_POWER_CURVE_SELECTOR_B3[MODEL_COUNT][8] = { + { 122.06, 122.9, 125.75, 0.0, 120.39, 108.52, 0.0, 92.239 } /* Model 183 */ , + { 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 } /* Model 190 */ , + { 122.06, 122.9, 125.75, 0.0, 120.39, 108.52, 0.0, 92.239 } /* Model 191 */ +}; + +static const float REAL_POWER_MULTIPLIER_A1[MODEL_COUNT][8] = { + { 0.08040007075206226, 0.0894, 0.0999, 0.0, 0.0813, 0.0905, 0.0, 0.1005 } /* Model 183 */ , + { 0.1127, 0.1127, 0.1127, 0.1127, 0.1127, 0.1127, 0.1127, 0.1127 } /* Model 190 */ , + { 0.08040007075206226, 0.0894, 0.0999, 0.0, 0.0813, 0.0905, 0.0, 0.1005 } /* Model 191 */ +}; + +static const float REAL_POWER_MULTIPLIER_B1[MODEL_COUNT][8] = { + { 45.292, 41.928, 41.727, 0.0, 40.269, 41.81, 0.0, 43.458 } /* Model 183 */ , + { 50.031, 50.031, 50.031, 50.031, 50.031, 50.031, 50.031, 50.031 } /* Model 190 */ , + { 45.292, 41.928, 41.727, 0.0, 40.269, 41.81, 0.0, 43.458 } /* Model 191 */ +}; + +static const float REAL_POWER_MULTIPLIER_A2[MODEL_COUNT][8] = { + { 0.08630063689870031, 0.0946, 0.1068, 0.0, 0.086, 0.0967, 0.0, 0.1088 } /* Model 183 */ , + { 0.1127, 0.1127, 0.1127, 0.1127, 0.1127, 0.1127, 0.1127, 0.1127 } /* Model 190 */ , + { 0.08630063689870031, 0.0946, 0.1068, 0.0, 0.086, 0.0967, 0.0, 0.1088 } /* Model 191 */ +}; + +static const float REAL_POWER_MULTIPLIER_B2[MODEL_COUNT][8] = { + { 8.3927, 9.2393, 8.2852, 0.0, 8.301, 6.7636, 0.0, 8.2842 } /* Model 183 */ , + { 50.031, 50.031, 50.031, 50.031, 50.031, 50.031, 50.031, 50.031 } /* Model 190 */ , + { 8.3927, 9.2393, 8.2852, 0.0, 8.301, 6.7636, 0.0, 8.2842 } /* Model 191 */ +}; + +static const float REAL_POWER_MULTIPLIER_A3[MODEL_COUNT][8] = { + { 0.0896001146881468, 0.0991, 0.1116, 0.0, 0.0967, 0.1068, 0.0, 0.1169 } /* Model 183 */ , + { 0.1127, 0.1127, 0.1127, 0.1127, 0.1127, 0.1127, 0.1127, 0.1127 } /* Model 190 */ , + { 0.0896001146881468, 0.0991, 0.1116, 0.0, 0.0967, 0.1068, 0.0, 0.1169 } /* Model 191 */ +}; + +static const float REAL_POWER_MULTIPLIER_B3[MODEL_COUNT][8] = { + { -31.115, -33.777, -33.826, 0.0, -59.513, -57.729, 0.0, -41.333 } /* Model 183 */ , + { 50.031, 50.031, 50.031, 50.031, 50.031, 50.031, 50.031, 50.031 } /* Model 190 */ , + { -31.115, -33.777, -33.826, 0.0, -59.513, -57.729, 0.0, -41.333 } /* Model 191 */ +}; + +/** + * Maximum battery voltage, used to estimate battery charge + * Second index: Recharging battery flag: charging (1) or charged/discharging (0) + */ +static const float MAX_BATTERY_VOLTAGE[MODEL_COUNT][2] = { + { 27.0, 29.5 } /* Model 183 */ , + { 27.0, 29.5 } /* Model 190 */ , + { 27.0, 29.5 } /* Model 191 */ +}; + +/** Minimum battery voltage, used to estimate battery charge */ +static const float MIN_BATTERY_VOLTAGE[MODEL_COUNT] = { + 20 /* Model 183 */ , + 20 /* Model 190 */ , + 20 /* Model 191 */ +}; + +#endif /* INCLUDED_MICROSOL_APC_H */ diff --git a/drivers/microsol-common.c b/drivers/microsol-common.c new file mode 100644 index 0000000..84ed6fe --- /dev/null +++ b/drivers/microsol-common.c @@ -0,0 +1,809 @@ +/* microsol-common.c - common framework for Microsol Solis-based UPS hardware + + Copyright (C) 2004 Silvino B. Magalhães + 2019 Roberto Panerai Velloso + 2021 Ygor A. S. Regados + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + 2021/03/19 - Version 0.70 - Initial release, based on solis driver + +*/ + +#include "main.h" /* Includes "config.h", must be first */ + +#include +#include +#include +#include "serial.h" +#include "nut_float.h" +#include "nut_stdint.h" +#include "microsol-common.h" +#include "timehead.h" + +#define false 0 +#define true 1 +#define RESP_END 0xFE +#define ENDCHAR 13 /* replies end with CR */ +/* solis commands */ +#define CMD_UPSCONT 0xCC +#define CMD_SHUT 0xDD +#define CMD_SHUTRET 0xDE +#define CMD_EVENT 0xCE +#define CMD_DUMP 0xCD + +#define M_UNKN "Unknown solis model" +#define NO_SOLIS "Solis not detected! aborting ..." +#define UPS_DATE "UPS Date %4d/%02d/%02d" +#define SYS_DATE "System Date %4d/%02d/%02d day of week %s" +#define ERR_PACK "Wrong package" +#define NO_EVENT "No events" +#define UPS_TIME "UPS internal Time %0d:%02d:%02d" +#define PRG_DAYS "Programming Shutdown Sun Mon Tue Wed Thu Fri Sat" +#define PRG_ONON "External shutdown programming active" +#define PRG_ONOU "Internal shutdown programming active" +#define TIME_OFF "UPS Time power off %02d:%02d" +#define TIME_ON "UPS Time power on %02d:%02d" +#define PRG_ONOF "Shutdown programming not activated" +#define TODAY_DD "Shutdown today at %02d:%02d" +#define SHUT_NOW "Shutdown now!" + +#define FMT_DAYS " %d %d %d %d %d %d %d" + +/* Date, time and programming group */ +static int const BASE_YEAR = 1998; /* Note: code below uses relative "unsigned char" years */ +static int device_day, device_month, device_year; +static int device_hour, device_minute, device_second; +static int power_off_hour, power_off_minute; +static int power_on_hour, power_on_minute; +static uint8_t device_days_on = 0, device_days_off = 0, days_to_shutdown = 0; + +static int isprogram = 0, progshut = 0, prgups = 0; +static int hourshut, minshut; + +static int host_year, host_month, host_day; +static int host_week; +static int host_hour, host_minute, host_second; + +/* buffers */ +unsigned char received_packet[PACKET_SIZE]; + +/* Identification */ +const char *model_name; +unsigned int ups_model; +bool_t input_220v, output_220v; + +/* logical */ +bool_t detected = 0; +bool_t line_unpowered, overheat; +bool_t overload, critical_battery, inverter_working; +static bool_t recharging; +static bool_t packet_parsed = false; + +double input_voltage, input_current, input_frequency; +double output_voltage, output_current, output_frequency; + +double input_low_limit, input_high_limit; + +int battery_extension; +double battery_voltage, battery_charge; +double temperature; + +double apparent_power, real_power, ups_load; +int load_power_factor, nominal_power; + +/** + * Convert standard days string to firmware format + * This is needed because UPS sends binary date rotated + * from current week day (first bit = current day) + */ +static char *convert_days(char *cop) +{ + static char alt[8]; + + int ish, fim; + /* FIXME? Are range-checks needed for values more than 6? wire noise etc? */ + if (host_week == 6) + ish = 0; + else + ish = 1 + host_week; + + fim = 7 - ish; + /* rotate left only 7 bits */ + + if (fim > 0) { + memcpy(alt, &cop[ish], (size_t)fim); + } else { + fatalx(EXIT_FAILURE, "%s: value out of range: %d (%d)", + __func__, fim, ish); + } + + if (ish > 0) + memcpy(&alt[fim], cop, (size_t)ish); + + alt[7] = 0; /* string terminator */ + + return alt; +} + +/** Convert bitstring (e.g. 1100101) to binary */ +static uint8_t bitstring_to_binary(char *binStr) +{ + uint8_t result = 0; + unsigned int i; + + for (i = 0; i < 7; ++i) { + char ch = binStr[i]; + if (ch == '1' || ch == '0') + result += ((ch - '0') << (6 - i)); + else + return 0; + } + + return result; +} + +/** + * Revert firmware format to standard string binary days + * This is needed because UPS sends binary date rotated + * from current week day (first bit = current day) + */ +static uint8_t revert_days(unsigned char firmware_week) +{ + char ordered_week[8]; + int i; + + for (i = 0; i < (6 - host_week); ++i) + ordered_week[i] = (firmware_week >> (5 - host_week - i)) & 0x01; + + for (i = 0; i < host_week + 1; ++i) + ordered_week[i + (6 - host_week)] = (firmware_week >> (6 - i)) & 0x01; + + for (i = 0; i < 7; i++) + ordered_week[i] += '0'; + + ordered_week[7] = 0; /* string terminator */ + + return bitstring_to_binary(ordered_week); +} + +/** Parse time string from parameters and store their values */ +static bool_t set_schedule_time(char *hour, bool_t off_time) +{ + int string_hour, string_minute; + + if ((strlen(hour) != 5) || (sscanf(hour, "%d:%d", &string_hour, &string_minute) != 2)) + return 0; + + if (off_time) { + power_off_hour = string_hour; + power_off_minute = string_minute; + } else { + power_on_hour = string_hour; + power_on_minute = string_minute; + } + return 1; +} + +/** Send immediate shutdown command to UPS */ +static void send_shutdown(void) +{ + unsigned int i; + + for (i = 0; i < 10; i++) + ser_send_char(upsfd, CMD_SHUT); + + upslogx(LOG_NOTICE, "UPS shutdown command sent"); +} + +/** Store clock updates and shutdown schedules to UPS */ +static void save_ups_config(void) +{ + unsigned int i; + int checksum = 0; + unsigned char configuration_packet[12]; + + /* Prepare configuration packet */ + /* FIXME? Check for overflows with int => char truncations? */ + configuration_packet[0] = (unsigned char)0xCF; + configuration_packet[1] = (unsigned char)host_hour; + configuration_packet[2] = (unsigned char)host_minute; + configuration_packet[3] = (unsigned char)host_second; + configuration_packet[4] = (unsigned char)power_on_hour; + configuration_packet[5] = (unsigned char)power_on_minute; + configuration_packet[6] = (unsigned char)power_off_hour; + configuration_packet[7] = (unsigned char)power_off_minute; + configuration_packet[8] = (unsigned char)(host_week << 5); + configuration_packet[8] = (unsigned char)configuration_packet[8] | (unsigned char)host_day; + configuration_packet[9] = (unsigned char)(host_month << 4); + configuration_packet[9] = (unsigned char)configuration_packet[9] | (unsigned char)(host_year - BASE_YEAR); + configuration_packet[10] = (unsigned char)device_days_off; + + /* MSB zero */ + configuration_packet[10] = configuration_packet[10] & (~(0x80)); + + /* Calculate packet content checksum */ + for (i = 0; i < 11; i++) { + checksum += configuration_packet[i]; + } + /* FIXME? Does truncation to char have same effect as %256 ? */ + configuration_packet[11] = (unsigned char)(checksum % 256); + + /* Send final packet and checksum to serial port */ + for (i = 0; i < 12; i++) { + ser_send_char(upsfd, configuration_packet[i]); + } +} + +/** Log shut-down schedule data stored in UPS */ +static void print_info(void) +{ + /* sunday, monday, tuesday, wednesday, thursday, friday, saturday */ + char week_days[7] = { 0, 0, 0, 0, 0, 0, 0 }; + unsigned int i; + + upslogx(LOG_NOTICE, UPS_DATE, device_year, device_month, device_day); + upslogx(LOG_NOTICE, UPS_TIME, device_hour, device_minute, device_second); + + if (prgups > 0) { + /* this is the string to binary standard */ + for (i = 0; i < 7; i++) { + week_days[i] = (days_to_shutdown >> (6 - i)) & 0x01; + } + + if (prgups == 3) + upslogx(LOG_NOTICE, PRG_ONOU); + else + upslogx(LOG_NOTICE, PRG_ONON); + + upslogx(LOG_NOTICE, TIME_ON, power_on_hour, power_on_minute); + upslogx(LOG_NOTICE, TIME_OFF, power_off_hour, power_off_minute); + + upslogx(LOG_NOTICE, PRG_DAYS); + + upslogx(LOG_NOTICE, FMT_DAYS, week_days[0], week_days[1], week_days[2], week_days[3], week_days[4], week_days[5], week_days[6]); + } else { + upslogx(LOG_NOTICE, PRG_ONOF); + } +} + +/** Parses received packet with UPS readings and configuration. */ +static void scan_received_pack(void) +{ + /* UPS internal time */ + device_year = (received_packet[19] & 0x0F) + BASE_YEAR; + device_month = (received_packet[19] & 0xF0) >> 4; + device_day = (received_packet[18] & 0x1F); + + device_hour = received_packet[11]; + device_minute = received_packet[10]; + device_second = received_packet[9]; + + /* UPS power cycle schedule if in programmed shutdown mode */ + if (prgups == 3) { + device_days_on = received_packet[17]; + days_to_shutdown = revert_days(device_days_on); + + /* Automatic UPS power-off time */ + power_off_hour = received_packet[15]; + power_off_minute = received_packet[16]; + + /* Automatic UPS power-on time */ + power_on_hour = received_packet[13]; + power_on_minute = received_packet[14]; + } + + /* These UPS have 110V- or 220V-output models */ + if ((0x01 & received_packet[20]) == 0x01) { + output_220v = 1; + } + + /* UPS state flags */ + critical_battery = (0x04 & received_packet[20]) == 0x04; + inverter_working = (0x08 & received_packet[20]) == 0x08; + overheat = (0x10 & received_packet[20]) == 0x10; + line_unpowered = (0x20 & received_packet[20]) == 0x20; + overload = (0x80 & received_packet[20]) == 0x80; + + recharging = (0x02 & received_packet[20]) == 0x02; + if (line_unpowered) { + recharging = false; + } + + /* Check if input voltage is 110V or 220V */ + if ((0x40 & received_packet[20]) == 0x40) { + input_220v = 1; + } else { + input_220v = 0; + } + + /* Internal battery temperature */ + temperature = 0x7F & received_packet[4]; + if (0x80 & received_packet[4]) { + temperature -= 128; + } + + /* Parse model-specific data (current and voltages). + * Doing it here as these values are used for the next calculations. */ + scan_received_pack_model_specific(); + + ups_load = (apparent_power / nominal_power) * 100.0; + + if (battery_charge > 100.0) { + battery_charge = 100.0; + } else if (battery_charge < 0.0) { + battery_charge = 0.0; + } + + output_frequency = 60; + if (!inverter_working) { + output_voltage = 0; + output_frequency = 0; + } + + if (!line_unpowered && inverter_working) + output_frequency = input_frequency; + + if (apparent_power < 0) + load_power_factor = 0; + else { + if (d_equal(apparent_power, 0)) + load_power_factor = 100; + else + load_power_factor = ((real_power / apparent_power) * 100); + + if (load_power_factor > 100) { + load_power_factor = 100; + } + } + + /* input 110V or 220v */ + if (input_220v == 0) { + input_low_limit = 75; + input_high_limit = 150; + } else { + input_low_limit = 150; + input_high_limit = 300; + } +} + +/** + * Start processing of received packets + * + * Packet format: 25-bytes binary structure + * Byte 1: Packet type/UPS model + * Byte 2: Output voltage data + * Byte 3: Input voltage data + * Byte 4: Battery voltage data + * Byte 5: UPS temperature data + * Byte 6: Output current data + * Byte 7: Electrical relay setup + * Byte 8-9: Real power data + * Byte 10: UPS clock - seconds + * Byte 11: UPS clock - minutes + * Byte 12: UPS clock - hours + * Byte 13: Zero + * Byte 14: UPS scheduler - power-on hour + * Byte 15: UPS scheduler - power-on minute + * Byte 16: UPS scheduler - power-off hour + * Byte 17: UPS scheduler - power-off minute + * Byte 18: UPS scheduler - weekdays + * Byte 19: UPS clock - day of month + * Byte 20: UPS clock - year (since 1998) (left 4 bits) and month (right 4 bits) + * Byte 21: UPS flags (power status, battery status, overload, overheat, nominal input voltage, nominal output voltage) + * Byte 22-23: Input frequency data + * Byte 24: Packet checksum + * Byte 25: Packet delimiter, always 0xFE + */ +static void comm_receive(const unsigned char *bufptr, size_t size) +{ + size_t i; + + if (size == PACKET_SIZE) { + int checksum = 0; + + upsdebug_hex(3, "comm_receive: bufptr", bufptr, size); + + /* Calculate packet checksum */ + for (i = 0; i < PACKET_SIZE - 2; i++) { + checksum += bufptr[i]; + } + checksum = checksum % 256; + upsdebugx(4, "%s: calculated checksum = 0x%02x, bufptr[23] = 0x%02x", __func__, checksum, bufptr[23]); + + /* Only proceed if checksum matches and packet delimiter is found */ + if (checksum == bufptr[23] && bufptr[24] == 254) { + upsdebugx(4, "%s: valid packet received", __func__); + memcpy(received_packet, bufptr, PACKET_SIZE); + + if ((received_packet[0] & 0xF0) == 0xA0 || (received_packet[0] & 0xF0) == 0xB0) { + /* If UPS still not detected, compare with available lists */ + if (!detected) { + ups_model = received_packet[0]; + + detected = true; + } + + if (!ups_model_defined()) { + upslogx(LOG_DEBUG, M_UNKN); + } + + scan_received_pack(); + } + } + } +} + +/** Refresh host time variables */ +static void refresh_host_time(void) +{ + const time_t epoch = time(NULL); + struct tm now; + + localtime_r(&epoch, &now); + host_year = now.tm_year + 1900; + host_month = now.tm_mon + 1; + host_day = now.tm_mday; + host_week = now.tm_wday; + host_hour = now.tm_hour; + host_minute = now.tm_min; + host_second = now.tm_sec; +} + +/** Query shut-down schedule configuration */ +static void setup_poweroff_schedule(void) +{ + bool_t i1 = 0, i2 = 0; + char *daysoff; + + refresh_host_time(); + + if (testvar("prgshut")) { + prgups = atoi(getval("prgshut")); + } + + if (prgups > 0 && prgups < 3) { + if (testvar("daysweek")) { + device_days_on = bitstring_to_binary(convert_days(getval("daysweek"))); + } + + if (testvar("daysoff")) { + daysoff = getval("daysoff"); + days_to_shutdown = bitstring_to_binary(daysoff); + device_days_off = bitstring_to_binary(convert_days(daysoff)); + } + + if (testvar("houron")) { + i1 = set_schedule_time(getval("houron"), 0); + } + + if (testvar("houroff")) { + i2 = set_schedule_time(getval("houroff"), 1); + } + + if (i1 && i2 && (device_days_on > 0)) { + isprogram = 1; + + /* If configured to shut-down UPS, push schedule to internal configuration */ + if (prgups == 2) { + save_ups_config(); + } + } else { + if (i2 == 1 && device_days_off > 0) { + isprogram = 1; + device_days_on = device_days_off; + } + } + } +} + +/** Check shut-down schedule and sets system to shut down if needed */ +static void check_shutdown_schedule(void) +{ + bool_t is_shutdown_day = 0; + + if (isprogram || prgups == 3) { + refresh_host_time(); + + is_shutdown_day = (days_to_shutdown >> (6 - host_week)) & 0x01; + + if (is_shutdown_day) { + upslogx(LOG_NOTICE, TODAY_DD, hourshut, minshut); + + if (host_hour == hourshut && host_minute >= minshut) { + upslogx(LOG_NOTICE, SHUT_NOW); + progshut = 1; + } + } + } +} + +/** Resynchronizes packet boundaries */ +static void resynchronize_packet(void) { + unsigned char sync_received_byte = 0; + unsigned short i; + + /* Flush serial port buffers */ + ser_flush_io(upsfd); + + upsdebugx(3, "%s: Synchronizing packet boundaries...", __func__); + + /* + * - Read until end-of-response character (0xFE): + * read up to 3 packets in size before giving up + * synchronizing with the device. + */ + for (i = 0; i < PACKET_SIZE * 3 && sync_received_byte != RESP_END; i++) { + ser_get_char(upsfd, &sync_received_byte, 3, 0); + } + + /* If no packet boundary was found, terminate communication */ + if (sync_received_byte != RESP_END) { + fatalx(EXIT_FAILURE, NO_SOLIS); + } +} + +/** Synchronize packet receiving and setup basic variables */ +static void get_base_info(void) +{ + unsigned char packet[PACKET_SIZE]; + ssize_t tam; + + if (testvar("battext")) { + battery_extension = atoi(getval("battext")); + } + + setup_poweroff_schedule(); + + /* dummy read attempt to sync - throw it out */ + upsdebugx(3, "%s: sending CMD_UPSCONT and ENDCHAR", __func__); + ser_send(upsfd, "%c%c", CMD_UPSCONT, ENDCHAR); + + resynchronize_packet (); + + upsdebugx(4, "%s: requesting %d bytes from ser_get_buf_len()", __func__, PACKET_SIZE); + tam = ser_get_buf_len(upsfd, packet, PACKET_SIZE, 3, 0); + upsdebugx(2, "%s: received %zd bytes from ser_get_buf_len()", __func__, tam); + if (tam > 0 && nut_debug_level >= 4) { + upsdebug_hex(4, "received from ser_get_buf_len()", packet, (size_t)tam); + } + comm_receive(packet, (size_t)tam); + + if (!detected) { + fatalx(EXIT_FAILURE, NO_SOLIS); + } + + set_ups_model(); + + /* Setup power-off times */ + if (prgups != 0) { + if (prgups == 1) { + /* If only this host is meant to be powered off, use proper time. */ + hourshut = power_off_hour; + minshut = power_off_minute; + } else { + /* If the UPS is to be powered off too, give + * a 5-minute grace time to shutdown hosts */ + if (power_off_minute < 5) { + if (power_off_hour > 1) + hourshut = power_off_hour - 1; + else + hourshut = 23; + + minshut = 60 - (5 - power_off_minute); + } else { + hourshut = power_off_hour; + minshut = power_off_minute - 5; + } + } + } + + /* manufacturer */ + dstate_setinfo("ups.mfr", "%s", "APC"); + + dstate_setinfo("ups.model", "%s", model_name); + dstate_setinfo("input.transfer.low", "%03.1f", input_low_limit); + dstate_setinfo("input.transfer.high", "%03.1f", input_high_limit); + + dstate_addcmd("shutdown.return"); /* CMD_SHUTRET */ + dstate_addcmd("shutdown.stayoff"); /* CMD_SHUT */ + + upslogx(LOG_NOTICE, "Detected %s on %s", dstate_getinfo("ups.model"), device_path); + + print_info(); +} + +/** Retrieves new packet from serial connection and parses it */ +static void get_updated_info(void) +{ + unsigned char temp[256]; + ssize_t tam; + + check_shutdown_schedule(); + + /* get update package */ + temp[0] = 0; /* flush temp buffer */ + + upsdebugx(3, "%s: requesting %d bytes from ser_get_buf_len()", __func__, PACKET_SIZE); + tam = ser_get_buf_len(upsfd, temp, PACKET_SIZE, 3, 0); + + upsdebugx(2, "%s: received %zd bytes from ser_get_buf_len()", __func__, tam); + if (tam > 0 && nut_debug_level >= 4) + upsdebug_hex(4, "received from ser_get_buf_len()", temp, (size_t)tam); + + packet_parsed = false; + if (temp[24] == RESP_END) { + /* Packet boundary found, process packet */ + comm_receive(temp, (size_t)tam); + + packet_parsed = true; + } else { + /* Malformed packet received, possible boundary desynchronization. */ + upsdebugx(3, "%s: Malformed packet received, trying to resynchronize...", __func__); + + resynchronize_packet (); + } +} + +static int instcmd(const char *cmdname, const char *extra) +{ + /* Power-cycle UPS */ + if (!strcasecmp(cmdname, "shutdown.return")) { + ser_send_char(upsfd, CMD_SHUTRET); /* 0xDE */ + return STAT_INSTCMD_HANDLED; + } + + /* Power-off UPS */ + if (!strcasecmp(cmdname, "shutdown.stayoff")) { + ser_send_char(upsfd, CMD_SHUT); /* 0xDD */ + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; +} + +void upsdrv_initinfo(void) +{ + get_base_info(); + + upsh.instcmd = instcmd; +} + +void upsdrv_updateinfo(void) +{ + get_updated_info(); + + if (packet_parsed) { + dstate_setinfo("battery.charge", "%03.1f", battery_charge); + dstate_setinfo("battery.voltage", "%02.1f", battery_voltage); + + dstate_setinfo("input.frequency", "%2.1f", input_frequency); + dstate_setinfo("input.voltage", "%03.1f", input_voltage); + + dstate_setinfo("output.current", "%03.1f", output_current); + dstate_setinfo("output.power", "%03.1f", apparent_power); + dstate_setinfo("output.powerfactor", "%0.2f", load_power_factor / 100.0); + dstate_setinfo("output.realpower", "%03.1f", real_power); + dstate_setinfo("output.voltage", "%03.1f", output_voltage); + + dstate_setinfo("ups.temperature", "%2.2f", temperature); + dstate_setinfo("ups.load", "%03.1f", ups_load); + + status_init(); + + if (!line_unpowered) { + status_set("OL"); /* On line */ + } else { + status_set("OB"); /* On battery */ + } + + if (overload) { + status_set("OVER"); /* Overload */ + } + + if (overheat) { + status_set("OVERHEAT"); /* Overheat */ + } + + if (recharging) { + status_set("CHRG"); /* Charging battery */ + } + + if (critical_battery) { + status_set("LB"); /* Critically low battery */ + } + + if (progshut) { + /* Software-based shutdown now */ + if (prgups == 2) + send_shutdown(); /* Send command to shutdown UPS in 4-5 minutes */ + + /* Workaround for triggering servers' power-off before UPS power-off */ + status_set("LB"); + } + + status_commit(); + + dstate_dataok(); + } else { + /* + * If no packet was processed, report data as stale. + * Most likely to be fixed on next received packet. + */ + dstate_datastale (); + } +} + +/*! @brief Power down the attached load immediately. + * Basic idea: find out line status and send appropriate command. + * - on battery: send normal shutdown, UPS will return by itself on utility + * - on line: send shutdown+return, UPS will cycle and return soon. + */ +void upsdrv_shutdown(void) +{ + if (!line_unpowered) { /* on line */ + upslogx(LOG_NOTICE, "On line, sending power cycle command..."); + ser_send_char(upsfd, CMD_SHUTRET); + } else { + upslogx(LOG_NOTICE, "On battery, sending power off command..."); + ser_send_char(upsfd, CMD_SHUT); + } +} + +void upsdrv_help(void) +{ + printf("\nAPC/Microsol options\n\n"); + printf(" Battery extension (AH)\n"); + printf(" battext = 80\n\n"); + printf(" Scheduled UPS power on/off\n"); + printf(" prgshut = 0 (default, no scheduled shutdown)\n"); + printf(" prgshut = 1 (software-based shutdown schedule without UPS power-off)\n"); + printf(" prgshut = 2 (software-based shutdown schedule with UPS power-off)\n"); + printf(" prgshut = 3 (internal UPS shutdown schedule)\n\n"); + printf(" Schedule configuration:\n"); + printf(" daysweek = 1010101 (power on days)\n"); + printf(" daysoff = 1010101 (power off days)\n"); + printf(" where each digit is a day from sun...sat with 0 = off and 1 = on\n\n"); + printf(" houron = hh:mm hh = hour 0-23 mm = minute 0-59 separated with :\n"); + printf(" houroff = hh:mm hh = hour 0-23 mm = minute 0-59 separated with :\n"); + printf(" where houron is power-on hour and houroff is shutdown and power-off hour\n\n"); + printf(" Use daysweek and houron to programming and save UPS power on/off\n"); + printf(" These are valid only if prgshut = 2 or 3\n"); +} + +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "battext", "Battery extension (0-80AH)"); + addvar(VAR_VALUE, "prgshut", "Scheduled power-off mode (0-3)"); + addvar(VAR_VALUE, "daysweek", "Days of week for UPS shutdown"); + addvar(VAR_VALUE, "daysoff", "Days of week for driver-induced shutdown"); + addvar(VAR_VALUE, "houron", "Power on hour (hh:mm)"); + addvar(VAR_VALUE, "houroff", "Power off hour (hh:mm)"); +} + +void upsdrv_initups(void) +{ + upsfd = ser_open(device_path); + ser_set_speed(upsfd, device_path, B9600); + + ser_set_dtr(upsfd, 1); + ser_set_rts(upsfd, 0); +} + +void upsdrv_cleanup(void) +{ + ser_close(upsfd, device_path); +} diff --git a/drivers/microsol-common.h b/drivers/microsol-common.h new file mode 100644 index 0000000..8381b13 --- /dev/null +++ b/drivers/microsol-common.h @@ -0,0 +1,67 @@ +/* microsol-common.h - common framework for Microsol Solis-based UPS hardware + + Copyright (C) 2004 Silvino B. Magalhaes + 2019 Roberto Panerai Velloso + 2021 Ygor A. S. Regados + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + 2021/03/19 - Version 0.70 - Initial release, based on solis driver + +*/ + +#ifndef INCLUDED_MICROSOL_COMMON_H +#define INCLUDED_MICROSOL_COMMON_H + +typedef unsigned int bool_t; + +#define PACKET_SIZE 25 + +/* buffers */ +extern unsigned char received_packet[PACKET_SIZE]; + +/* Identification */ +extern const char *model_name; +extern unsigned int ups_model; +extern bool_t input_220v, output_220v; + +/* logical */ +extern bool_t detected; +extern bool_t line_unpowered, overheat, overload; +extern bool_t critical_battery, inverter_working; +/*extern bool_t recharging;*/ /* microsol-apc.c has its own copy */ + +/* Input group */ +extern double input_voltage, input_current, input_frequency; +extern double input_minimum_voltage, input_maximum_voltage, input_nominal_voltage; +extern double input_low_limit, input_high_limit; + +/* Output group */ +extern double output_voltage, output_current, output_frequency; + +/* Battery group */ +extern int battery_extension; +extern double battery_voltage, battery_charge; +extern double temperature; + +/* Power group */ +extern double apparent_power, real_power, ups_load; +extern int load_power_factor, nominal_power, ups_power_factor; + +extern void scan_received_pack_model_specific(void); +extern void set_ups_model(void); +extern bool_t ups_model_defined(void); + +#endif /* INCLUDED_MICROSOL_COMMON_H */ diff --git a/drivers/netvision-mib.c b/drivers/netvision-mib.c index 91e4d35..51f4073 100644 --- a/drivers/netvision-mib.c +++ b/drivers/netvision-mib.c @@ -1,8 +1,10 @@ /* netvision-mib.c - data to monitor Socomec Sicon UPS equipped * with Netvision WEB/SNMP card/external box with NUT * - * Copyright (C) 2004 - * Thanos Chatziathanassiou + * Copyright (C) + * 2004 Thanos Chatziathanassiou + * 2012 Manuel Bouyer + * 2015 Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,7 +25,7 @@ #include "netvision-mib.h" -#define NETVISION_MIB_VERSION "0.1" +#define NETVISION_MIB_VERSION "0.44" #define NETVISION_SYSOID ".1.3.6.1.4.1.4555.1.1.1" @@ -37,12 +39,19 @@ /* UPS Battery */ #define NETVISION_OID_BATTERYSTATUS ".1.3.6.1.4.1.4555.1.1.1.1.2.1.0" static info_lkp_t netvision_batt_info[] = { - { 2, "" }, /* battery normal */ - { 3, "LB" }, /* battery low */ - { 4, "LB" }, /* battery depleted */ - { 5, "DISCHRG" }, /* battery discharging */ - { 6, "RB" }, /* battery failure */ - { 0, "NULL" } + { 2, "", NULL, NULL }, /* battery normal */ + { 3, "LB", NULL, NULL }, /* battery low */ + { 4, "LB", NULL, NULL }, /* battery depleted */ + { 5, "DISCHRG", NULL, NULL }, /* battery discharging */ + { 6, "RB", NULL, NULL }, /* battery failure */ + { 0, NULL, NULL, NULL } +}; + +/* Battery status: upsAlarmOnBattery */ +static info_lkp_t netvision_onbatt_info[] = { + { 0, "OL", NULL, NULL }, /* Online */ + { 1, "OB", NULL, NULL }, /* On battery */ + { 0, NULL, NULL, NULL } }; #define NETVISION_OID_SECONDSONBATTERY ".1.3.6.1.4.1.4555.1.1.1.1.2.2.0" @@ -51,8 +60,12 @@ static info_lkp_t netvision_batt_info[] = { #define NETVISION_OID_BATT_VOLTS ".1.3.6.1.4.1.4555.1.1.1.1.2.5.0" #define NETVISION_OID_INPUT_NUM_LINES ".1.3.6.1.4.1.4555.1.1.1.1.3.1.0" /* 1phase or 3phase UPS input */ +#define NETVISION_OID_INPUT_FREQ ".1.3.6.1.4.1.4555.1.1.1.1.3.2.0" #define NETVISION_OID_OUTPUT_NUM_LINES ".1.3.6.1.4.1.4555.1.1.1.1.4.3.0" /* 1phase or 3phase UPS output */ +#define NETVISION_OID_OUTPUT_FREQ ".1.3.6.1.4.1.4555.1.1.1.1.4.2.0" +#define NETVISION_OID_BYPASS_FREQ ".1.3.6.1.4.1.4555.1.1.1.1.5.1.0" +#define NETVISION_OID_BYPASS_NUM_LINES ".1.3.6.1.4.1.4555.1.1.1.1.5.2.0" /* 1phase or 3phase UPS input */ /* three phase ups provide input/output/load for each phase in case of one-phase output, only _P1 should be used @@ -62,34 +75,52 @@ static info_lkp_t netvision_batt_info[] = { #define NETVISION_OID_OUT_CURRENT_P1 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.3.1" #define NETVISION_OID_OUT_LOAD_PCT_P1 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.4.1" #define NETVISION_OID_IN_VOLTAGE_P1 ".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.5.1" +#define NETVISION_OID_IN_CURRENT_P1 ".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.3.1" +#define NETVISION_OID_BY_VOLTAGE_P1 ".1.3.6.1.4.1.4555.1.1.1.1.5.3.1.2.1" +#define NETVISION_OID_BY_CURRENT_P1 ".1.3.6.1.4.1.4555.1.1.1.1.5.3.1.3.1" #define NETVISION_OID_OUT_VOLTAGE_P2 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.2.2" #define NETVISION_OID_OUT_CURRENT_P2 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.3.2" #define NETVISION_OID_OUT_LOAD_PCT_P2 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.4.2" #define NETVISION_OID_IN_VOLTAGE_P2 ".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.5.2" +#define NETVISION_OID_IN_CURRENT_P2 ".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.3.2" +#define NETVISION_OID_BY_VOLTAGE_P2 ".1.3.6.1.4.1.4555.1.1.1.1.5.3.1.2.2" +#define NETVISION_OID_BY_CURRENT_P2 ".1.3.6.1.4.1.4555.1.1.1.1.5.3.1.3.2" #define NETVISION_OID_OUT_VOLTAGE_P3 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.2.3" #define NETVISION_OID_OUT_CURRENT_P3 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.3.3" #define NETVISION_OID_OUT_LOAD_PCT_P3 ".1.3.6.1.4.1.4555.1.1.1.1.4.4.1.4.3" #define NETVISION_OID_IN_VOLTAGE_P3 ".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.5.3" +#define NETVISION_OID_IN_CURRENT_P3 ".1.3.6.1.4.1.4555.1.1.1.1.3.3.1.3.3" +#define NETVISION_OID_BY_VOLTAGE_P3 ".1.3.6.1.4.1.4555.1.1.1.1.5.3.1.2.3" +#define NETVISION_OID_BY_CURRENT_P3 ".1.3.6.1.4.1.4555.1.1.1.1.5.3.1.3.3" #define NETVISION_OID_OUTPUT_SOURCE ".1.3.6.1.4.1.4555.1.1.1.1.4.1.0" +#define NETVISION_OID_CONTROL_STATUS ".1.3.6.1.4.1.4555.1.1.1.1.8.1" +#define NETVISION_OID_CONTROL_SHUTDOWN_DELAY ".1.3.6.1.4.1.4555.1.1.1.1.8.2" + static info_lkp_t netvision_output_info[] = { - { 1, "" }, /* output source other */ - { 2, "" }, /* output source none */ - { 3, "OL" }, /* output source normal */ - { 4, "OL BYPASS" }, /* output source bypass */ - { 5, "OB" }, /* output source battery */ - { 6, "OL BOOST" }, /* output source booster */ - { 7, "OL TRIM" }, /* output source reducer */ - { 8, "" }, /* output source standby */ - { 9, "" }, /* output source ecomode */ - { 0, "NULL" } + { 1, "", NULL, NULL }, /* output source other */ + { 2, "", NULL, NULL }, /* output source none */ + { 3, "OL", NULL, NULL }, /* output source normal */ + { 4, "OL BYPASS", NULL, NULL }, /* output source bypass */ + { 5, "OB", NULL, NULL }, /* output source battery */ + { 6, "OL BOOST", NULL, NULL }, /* output source booster */ + { 7, "OL TRIM", NULL, NULL }, /* output source reducer */ + { 8, "OL", NULL, NULL }, /* output source standby */ + { 9, "", NULL, NULL }, /* output source ecomode */ + { 0, NULL, NULL, NULL } }; /* Snmp2NUT lookup table */ static snmp_info_t netvision_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTAGENTSWVERSION, "SOCOMEC SICON UPS", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_UPSIDENTMODEL, @@ -102,14 +133,50 @@ static snmp_info_t netvision_mib[] = { SU_FLAG_OK | SU_STATUS_BATT, &netvision_batt_info[0] }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, NETVISION_OID_OUTPUT_SOURCE, "", SU_FLAG_OK | SU_STATUS_PWR, &netvision_output_info[0] }, + /* upsAlarmOnBattery */ + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.4555.1.1.1.1.6.3.2.0", "", + SU_FLAG_OK | SU_STATUS_PWR, &netvision_onbatt_info[0] }, /* ups load */ - { "ups.load", 0, 1, NETVISION_OID_OUT_LOAD_PCT_P1, 0, SU_FLAG_OK, NULL }, + { "ups.load", 0, 1, NETVISION_OID_OUT_LOAD_PCT_P1, 0, SU_INPUT_1, NULL }, /*ups input,output voltage, output frquency phase 1 */ - { "input.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P1, 0, SU_FLAG_OK, NULL }, - { "output.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P1, 0, SU_FLAG_OK, NULL }, - { "output.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P1, 0, SU_FLAG_OK, NULL }, + { "input.phases", 0, 1.0, NETVISION_OID_INPUT_NUM_LINES, NULL, 0, NULL }, + { "input.frequency", 0, 0.1, NETVISION_OID_INPUT_FREQ, NULL, SU_FLAG_OK, NULL }, + { "input.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P1, NULL, SU_INPUT_1, NULL }, + { "input.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P1, NULL, SU_INPUT_1, NULL }, + { "input.L1-N.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P1, NULL, SU_INPUT_3, NULL }, + { "input.L1.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P1, NULL, SU_INPUT_3, NULL }, + { "input.L2-N.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P2, NULL, SU_INPUT_3, NULL }, + { "input.L2.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P2, NULL, SU_INPUT_3, NULL }, + { "input.L3-N.voltage", 0, 0.1, NETVISION_OID_IN_VOLTAGE_P3, NULL, SU_INPUT_3, NULL }, + { "input.L3.current", 0, 0.1, NETVISION_OID_IN_CURRENT_P3, NULL, SU_INPUT_3, NULL }, + + { "output.phases", 0, 1.0, NETVISION_OID_OUTPUT_NUM_LINES, NULL, 0, NULL }, + { "output.frequency", 0, 0.1, NETVISION_OID_OUTPUT_FREQ, NULL, SU_FLAG_OK, NULL }, + { "output.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P1, NULL, SU_OUTPUT_1, NULL }, + { "output.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P1, NULL, SU_OUTPUT_1, NULL }, + { "output.load", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P1, NULL, SU_OUTPUT_1, NULL }, + { "output.L1-N.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P1, NULL, SU_OUTPUT_3, NULL }, + { "output.L1.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P1, NULL, SU_OUTPUT_3, NULL }, + { "output.L1.power.percent", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P1, NULL, SU_OUTPUT_3, NULL }, + { "output.L2-N.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P2, NULL, SU_OUTPUT_3, NULL }, + { "output.L2.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P2, NULL, SU_OUTPUT_3, NULL }, + { "output.L2.power.percent", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P2, NULL, SU_OUTPUT_3, NULL }, + { "output.L3-N.voltage", 0, 0.1, NETVISION_OID_OUT_VOLTAGE_P3, NULL, SU_OUTPUT_3, NULL }, + { "output.L3.current", 0, 0.1, NETVISION_OID_OUT_CURRENT_P3, NULL, SU_OUTPUT_3, NULL }, + { "output.L3.power.percent", 0, 1.0, NETVISION_OID_OUT_LOAD_PCT_P3, NULL, SU_OUTPUT_3, NULL }, + + { "input.bypass.phases", 0, 1.0, NETVISION_OID_BYPASS_NUM_LINES, NULL, 0, NULL }, + { "input.bypass.frequency", 0, 0.1, NETVISION_OID_BYPASS_FREQ, NULL, SU_FLAG_OK, NULL }, + { "input.bypass.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P1, NULL, SU_BYPASS_1, NULL }, + { "input.bypass.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P1, NULL, SU_BYPASS_1, NULL }, + { "input.bypass.L1-N.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P1, NULL, SU_BYPASS_3, NULL }, + { "input.bypass.L1.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P1, NULL, SU_BYPASS_3, NULL }, + { "input.bypass.L2-N.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P2, NULL, SU_BYPASS_3, NULL }, + { "input.bypass.L2.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P2, NULL, SU_BYPASS_3, NULL }, + { "input.bypass.L3-N.voltage", 0, 0.1, NETVISION_OID_BY_VOLTAGE_P3, NULL, SU_BYPASS_3, NULL }, + { "input.bypass.L3.current", 0, 0.1, NETVISION_OID_BY_CURRENT_P3, NULL, SU_BYPASS_3, NULL }, /* battery info */ { "battery.charge", 0, 1, NETVISION_OID_BATT_CHARGE, "", SU_FLAG_OK, NULL }, @@ -120,4 +187,4 @@ static snmp_info_t netvision_mib[] = { { NULL, 0, 0, NULL, NULL, 0, NULL } }; -mib2nut_info_t netvision = { "netvision", NETVISION_MIB_VERSION, "", NETVISION_OID_UPSIDENTMODEL, netvision_mib, NETVISION_SYSOID }; +mib2nut_info_t netvision = { "netvision", NETVISION_MIB_VERSION, NULL, NETVISION_OID_UPSIDENTMODEL, netvision_mib, NETVISION_SYSOID, NULL }; diff --git a/drivers/netxml-ups.c b/drivers/netxml-ups.c index 130d50f..330104a 100644 --- a/drivers/netxml-ups.c +++ b/drivers/netxml-ups.c @@ -1,7 +1,8 @@ -/* netxml-ups.c Driver routines for network XML UPS units +/* netxml-ups.c Driver routines for network XML UPS units Copyright (C) 2008-2009 Arjen de Korte + 2013 Vaclav Krpec This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,6 +22,7 @@ #include "main.h" #include "netxml-ups.h" #include "mge-xml.h" +#include "dstate.h" #include #include @@ -37,29 +39,205 @@ #include #include +#include "nut_stdint.h" + #define DRIVER_NAME "network XML UPS" -#define DRIVER_VERSION "0.30" +#define DRIVER_VERSION "0.44" + +/** *_OBJECT query multi-part body boundary */ +#define FORM_POST_BOUNDARY "NUT-NETXML-UPS-OBJECTS" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, - "Arjen de Korte ", + "Arjen de Korte " \ + "Vaclav Krpec ", DRV_EXPERIMENTAL, { NULL } }; + + +/** *_OBJECT query status */ +typedef enum { + OBJECT_OK = 0, /**< OK */ + OBJECT_PARSE_ERROR, /**< Parse error */ + OBJECT_ERROR, /**< Generic error */ +} object_query_status_t; /* end of typedef enum */ + + +/** *_OBJECT entry type */ +typedef enum { + SET_OBJECT_REQUEST, /**< SET_OBJECT request */ + SET_OBJECT_RESPONSE, /**< SET_OBJECT response */ +} object_query_type_t; /* end of typedef enum */ + + +/** *_OBJECT POST request mode */ +typedef enum { + RAW_POST, /**< RAW POST mode */ + FORM_POST, /**< FORM POST mode */ +} object_post_mode_t; /* end of typedef enum */ + + +typedef struct set_object_req set_object_req_t; /**< SET_OBJECT request carrier */ +typedef struct set_object_resp set_object_resp_t; /**< SET_OBJECT response carrier */ +typedef struct object_entry object_entry_t; /**< *_OBJECT entry carrier */ +typedef struct object_query object_query_t; /**< *_OBJECT query handle */ + + +/** SET_OBJECT request carrier */ +struct set_object_req { + char *name; /**< OBJECT name */ + char *value; /**< OBJECT value */ +}; /* end of struct set_object_req */ + + +/** SET_OBJECT response carrier */ +struct set_object_resp { + char *name; /**< OBJECT name */ + char *unit; /**< OBJECT unit */ + char *access; /**< OBJECT access */ + char *value; /**< OBJECT value */ +}; /* end of struct set_object_resp */ + + +/** *_OBJECT query entry */ +struct object_entry { + /** Payload */ + union { + set_object_req_t req; /**< Request entry */ + set_object_resp_t resp; /**< Response entry */ + } payld; + + /* Metadata */ + object_entry_t *next; /**< Next entry */ + object_entry_t *prev; /**< Previous entry */ +}; /* end of struct object_entry */ + + +/** *_OBJECT query handle */ +struct object_query { + object_query_status_t status; /**< Query status */ + object_query_type_t type; /**< List entries type */ + object_post_mode_t mode; /**< POST request mode */ + size_t cnt; /**< Count of entries */ + object_entry_t *head; /**< List head */ + object_entry_t *tail; /**< List tail */ +}; /* end of struct object_query */ + + +/** + * \brief *_OBJECT query constructor + * + * \param type Query type + * \param mode Query mode + * + * \return *_OBJECT query handle or \c NULL in case of memory error + */ +static object_query_t *object_query_create( + object_query_type_t type, + object_post_mode_t mode); + + +/** + * \brief Number of *_OBJECT query entries + * + * \param handle Query handle + * + * \return NUmber of entries + */ +static size_t object_query_size(object_query_t *handle); + + +/** + * \brief *_OBJECT query destructor + * + * \param handle Query handle + */ +static void object_query_destroy(object_query_t *handle); + + +/** + * \brief SET_OBJECT: add request query entry + * + * \param handle Request query handle + * \param name OBJECT name + * \param value OBJECT value + * + * \return Query entry or \c NULL in case of memory error + */ +static object_entry_t *set_object_add( + object_query_t *handle, + const char *name, + const char *value); + + +/** + * \brief SET_OBJECT: RAW POST mode implementation + * + * \param req SET_OBJECT request + * + * \return Response to the request + */ +static object_query_t *set_object_raw(object_query_t *req); + + +/** + * \brief SET_OBJECT: FORM POST mode implementation + * + * \param req SET_OBJECT request + * + * \return \c NULL (FORM POST mode resp. is ignored by specification) + */ +static object_query_t *set_object_form(object_query_t *req); + + +/** + * \brief SET_OBJECT: implementation + * + * \param req SET_OBJECT request + * + * \return Response to the request + */ +static object_query_t *set_object(object_query_t *req); + + +/** + * \brief SET_OBJECT: RAW POST mode request serialisation + * + * \param handle Request query handle + * + * \return POST request body + */ +static ne_buffer *set_object_serialise_raw(object_query_t *handle); + + +/** + * \brief SET_OBJECT: FORM POST mode request serialisation + * + * \param handle Request query handle + * + * \return POST request body + */ +static ne_buffer *set_object_serialise_form(object_query_t *handle); + + /* FIXME: - * "built with neon library %s" LIBNEON_VERSION + * "built with neon library %s" LIBNEON_VERSION * subdrivers (limited to MGE only ATM) */ /* Global vars */ uint32_t ups_status = 0; static int timeout = 5; +int shutdown_duration = 120; +static int shutdown_timer = 0; static time_t lastheard = 0; static subdriver_t *subdriver = &mge_xml_subdriver; static ne_session *session = NULL; static ne_socket *sock = NULL; static ne_uri uri; +static char *product_page = NULL; /* Support functions */ static void netxml_alarm_set(void); @@ -68,6 +246,9 @@ static int netxml_authenticate(void *userdata, const char *realm, int attempt, c static int netxml_dispatch_request(ne_request *request, ne_xml_parser *parser); static int netxml_get_page(const char *page); +static int instcmd(const char *cmdname, const char *extra); +static int setvar(const char *varname, const char *val); + static int netxml_alarm_subscribe(const char *page); #if HAVE_NE_SET_CONNECT_TIMEOUT && HAVE_NE_SOCK_CONNECT_TIMEOUT @@ -83,7 +264,7 @@ void upsdrv_initinfo(void) { char *page, *last = NULL; char buf[SMALLBUF]; - + snprintf(buf, sizeof(buf), "%s", subdriver->initinfo); for (page = strtok_r(buf, " ", &last); page != NULL; page = strtok_r(NULL, " ", &last)) { @@ -91,14 +272,23 @@ void upsdrv_initinfo(void) if (netxml_get_page(page) != NE_OK) { continue; } + /* store product page, for later use */ + product_page = xstrdup(page); - dstate_setinfo("driver.version.internal", "%s", subdriver->version); + dstate_setinfo("driver.version.data", "%s", subdriver->version); if (testvar("subscribe") && (netxml_alarm_subscribe(subdriver->subscribe) == NE_OK)) { extrafd = ne_sock_fd(sock); time(&lastheard); } + /* Register r/w variables */ + vname_register_rw(); + + /* Set UPS driver handler callbacks */ + upsh.setvar = &setvar; + upsh.instcmd = &instcmd; + return; } @@ -107,7 +297,8 @@ void upsdrv_initinfo(void) void upsdrv_updateinfo(void) { - int ret, errors = 0; + ssize_t ret; + int errors = 0; /* We really should be dealing with alarms through a separate callback, so that we can keep the * processing of alarms and polling for data separated. Currently, this isn't supported by the @@ -123,7 +314,7 @@ void upsdrv_updateinfo(void) /* alarm message received */ ne_xml_parser *parser = ne_xml_create(); - upsdebugx(2, "%s: ne_sock_read(%d bytes) => %s", __func__, ret, buf); + upsdebugx(2, "%s: ne_sock_read(%zd bytes) => %s", __func__, ret, buf); ne_xml_push_handler(parser, subdriver->startelm_cb, subdriver->cdata_cb, subdriver->endelm_cb, NULL); ne_xml_parse(parser, buf, strlen(buf)); ne_xml_destroy(parser); @@ -139,7 +330,7 @@ void upsdrv_updateinfo(void) upslogx(LOG_ERR, "NSM connection with '%s' lost", uri.host); - upsdebugx(2, "%s: ne_sock_read(%d) => %s", __func__, ret, ne_sock_error(sock)); + upsdebugx(2, "%s: ne_sock_read(%zd) => %s", __func__, ret, ne_sock_error(sock)); ne_sock_close(sock); if (netxml_alarm_subscribe(subdriver->subscribe) == NE_OK) { @@ -165,6 +356,12 @@ void upsdrv_updateinfo(void) errors++; } + /* also refresh the product information, at least for firmware information */ + ret = netxml_get_page(product_page); + if (ret != NE_OK) { + errors++; + } + if (errors > 1) { dstate_datastale(); return; @@ -182,15 +379,14 @@ void upsdrv_updateinfo(void) dstate_dataok(); } -void upsdrv_shutdown(void) -{ +void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ /* maybe try to detect the UPS here, but try a shutdown even if it doesn't respond at first if possible */ /* replace with a proper shutdown function */ - fatalx(EXIT_FAILURE, "shutdown not supported"); + /* fatalx(EXIT_FAILURE, "shutdown not supported"); */ /* you may have to check the line status since the commands for toggling power are frequently different for OL vs. OB */ @@ -198,33 +394,109 @@ void upsdrv_shutdown(void) /* OL: this must power cycle the load if possible */ /* OB: the load must remain off until the power returns */ + + int status = STAT_SET_FAILED; /* pessimistic assumption */ + + object_query_t *resp = NULL; + object_query_t *req = NULL; + + /* Pragmatic do { ... } while (0) loop allowing break to cleanup */ + do { + /* Create SET_OBJECT request */ + req = object_query_create(SET_OBJECT_REQUEST, FORM_POST); + + if (NULL == req) + break; + + if (NULL == set_object_add(req, "battery.runtime.low", "999999999")) + break; + + /* Send SET_OBJECT request */ + resp = set_object(req); + +#if (0) /* FORM_POST method response is ignored, we can only hope it worked... */ + if (NULL == resp) + break; + + /* Check if setting was done */ + if (1 > object_query_size(resp)) { + status = STAT_SET_UNKNOWN; + + break; + } +#endif /* end of code removal */ + + status = STAT_SET_HANDLED; /* success */ + + } while (0); /* end of pragmatic loop, break target */ + + /* Cleanup */ + if (NULL != req) + object_query_destroy(req); + + if (NULL != resp) + object_query_destroy(resp); + + if (STAT_SET_HANDLED != status) + fatalx(EXIT_FAILURE, "Shutdown failed: %d", status); } -/* static int instcmd(const char *cmdname, const char *extra) { +/* if (!strcasecmp(cmdname, "test.battery.stop")) { ser_send_buf(upsfd, ...); return STAT_INSTCMD_HANDLED; } +*/ - upslogx(LOG_NOTICE, "%s: unknown command [%s]", __func__, cmdname); + upslogx(LOG_NOTICE, "%s: unknown command [%s] [%s]", __func__, cmdname, extra); return STAT_INSTCMD_UNKNOWN; } -*/ -/* -static int setvar(const char *varname, const char *val) -{ - if (!strcasecmp(varname, "ups.test.interval")) { - ser_send_buf(upsfd, ...); - return STAT_SET_HANDLED; - } +static int setvar(const char *varname, const char *val) { + int status = STAT_SET_FAILED; /* pessimistic assumption */ - upslogx(LOG_NOTICE, "%s: unknown variable [%s]", __func__, varname); - return STAT_SET_UNKNOWN; + object_query_t *resp = NULL; + object_query_t *req = NULL; + + /* Pragmatic do { ... } while (0) loop allowing break to cleanup */ + do { + /* Create SET_OBJECT request */ + req = object_query_create(SET_OBJECT_REQUEST, FORM_POST); + + if (NULL == req) + break; + + if (NULL == set_object_add(req, varname, val)) + break; + + /* Send SET_OBJECT request */ + resp = set_object(req); + + if (NULL == resp) + break; + + /* Check if setting was done */ + if (1 > object_query_size(resp)) { + status = STAT_SET_UNKNOWN; + + break; + } + + status = STAT_SET_HANDLED; /* success */ + + } while (0); /* end of pragmatic loop, break target */ + + /* Cleanup */ + if (NULL != req) + object_query_destroy(req); + + if (NULL != resp) + object_query_destroy(resp); + + return status; } -*/ void upsdrv_help(void) { @@ -238,10 +510,24 @@ void upsdrv_makevartable(void) snprintf(buf, sizeof(buf), "network timeout (default: %d seconds)", timeout); addvar(VAR_VALUE, "timeout", buf); - addvar(VAR_FLAG, "subscribe", "NSM subscribe to NMC"); + addvar(VAR_FLAG, "subscribe", "authenticated subscription on NMC"); addvar(VAR_VALUE | VAR_SENSITIVE, "login", "login value for authenticated mode"); addvar(VAR_VALUE | VAR_SENSITIVE, "password", "password value for authenticated mode"); + + snprintf(buf, sizeof(buf), "shutdown duration in second (default: %d seconds)", shutdown_duration); + addvar(VAR_VALUE, "shutdown_duration", buf); + + if( shutdown_timer > 0 ) { + snprintf(buf, sizeof(buf), "shutdown timer in second (default: %d seconds)", shutdown_timer); + } + else { + snprintf(buf, sizeof(buf), "shutdown timer in second (default: none)"); + } + addvar(VAR_VALUE, "shutdown_timer", buf); + + /* Legacy MGE-XML conversion from 2000's, not needed in modern firmwares */ + addvar(VAR_FLAG, "do_convert_deci", "enable legacy convert_deci() for certain measurements 10x too large"); } void upsdrv_initups(void) @@ -271,6 +557,24 @@ void upsdrv_initups(void) } } + val = getval("shutdown_duration"); + if (val) { + shutdown_duration = atoi(val); + + if (shutdown_duration < 0) { + fatalx(EXIT_FAILURE, "shutdown duration must be greater than or equal to 0"); + } + } + + val = getval("shutdown_timer"); + if (val) { + shutdown_timer = atoi(val); + + if (shutdown_timer < 0) { + fatalx(EXIT_FAILURE, "shutdown timer must be greater than or equal to 0"); + } + } + if (nut_debug_level > 5) { ne_debug_init(stderr, NE_DBG_HTTP | NE_DBG_HTTPBODY); } @@ -286,7 +590,7 @@ void upsdrv_initups(void) if (uri.scheme == NULL) { uri.scheme = strdup("http"); } - + if (uri.host == NULL) { uri.host = strdup(device_path); } @@ -298,7 +602,7 @@ void upsdrv_initups(void) upsdebugx(1, "using %s://%s port %d", uri.scheme, uri.host, uri.port); session = ne_session_create(uri.scheme, uri.host, uri.port); - + /* timeout if we can't (re)connect to the UPS */ #ifdef HAVE_NE_SET_CONNECT_TIMEOUT ne_set_connect_timeout(session, timeout); @@ -349,6 +653,7 @@ void upsdrv_cleanup(void) free(subdriver->summary); free(subdriver->getobject); free(subdriver->setobject); + free(product_page); if (sock) { ne_sock_close(sock); @@ -367,37 +672,44 @@ void upsdrv_cleanup(void) static int netxml_get_page(const char *page) { - int ret; + int ret = NE_ERROR; ne_request *request; ne_xml_parser *parser; - upsdebugx(2, "%s: %s", __func__, page); + upsdebugx(2, "%s: %s", __func__, (page != NULL)?page:"(null)"); - request = ne_request_create(session, "GET", page); + if (page != NULL) { + request = ne_request_create(session, "GET", page); - parser = ne_xml_create(); + parser = ne_xml_create(); - ne_xml_push_handler(parser, subdriver->startelm_cb, subdriver->cdata_cb, subdriver->endelm_cb, NULL); + ne_xml_push_handler(parser, subdriver->startelm_cb, subdriver->cdata_cb, subdriver->endelm_cb, NULL); - ret = netxml_dispatch_request(request, parser); + ret = netxml_dispatch_request(request, parser); - if (ret) { - upsdebugx(2, "%s: %s", __func__, ne_get_error(session)); + if (ret) { + upsdebugx(2, "%s: %s", __func__, ne_get_error(session)); + } + + ne_xml_destroy(parser); + ne_request_destroy(request); } - - ne_xml_destroy(parser); - ne_request_destroy(request); - return ret; } static int netxml_alarm_subscribe(const char *page) { - int ret, port = -1, secret = -1; + ssize_t ret; + int secret = -1; + unsigned int port = 0; char buf[LARGEBUF], *s; ne_request *request; ne_sock_addr *addr; const ne_inet_addr *ai; + char resp_buf[LARGEBUF]; + + /* Clear response buffer */ + memset(resp_buf, 0, sizeof(resp_buf)); upsdebugx(2, "%s: %s", __func__, page); @@ -416,18 +728,23 @@ static int netxml_alarm_subscribe(const char *page) netxml_get_page(subdriver->configure); - snprintf(buf, sizeof(buf), "\n"); + snprintf(buf, sizeof(buf), "\n"); snprintfcat(buf, sizeof(buf), "\n"); snprintfcat(buf, sizeof(buf), "%s v%s\n", progname, DRIVER_VERSION); snprintfcat(buf, sizeof(buf), "connected socket\n"); snprintfcat(buf, sizeof(buf), "%s\n", dstate_getinfo("driver.hostname")); snprintfcat(buf, sizeof(buf), "\n"); - snprintfcat(buf, sizeof(buf), "%s\n", dstate_getinfo("driver.delay.shutdown")); - snprintfcat(buf, sizeof(buf), "%s\n", dstate_getinfo("driver.timer.shutdown")); - snprintfcat(buf, sizeof(buf), "CENTRALIZED\n"); + snprintfcat(buf, sizeof(buf), "%d\n", shutdown_duration); + if( shutdown_timer > 0 ) { + snprintfcat(buf, sizeof(buf), "%d\r\n", shutdown_timer); + } + else { + snprintfcat(buf, sizeof(buf), "NONE\n"); + } + snprintfcat(buf, sizeof(buf), "LOCAL\n"); snprintfcat(buf, sizeof(buf), "1\n"); snprintfcat(buf, sizeof(buf), "\n"); -/* snprintfcat(buf, sizeof(buf), "NUT driver\n"); */ + snprintfcat(buf, sizeof(buf), "\n"); snprintfcat(buf, sizeof(buf), "\n"); /* now send subscription message setting all the proper flags */ @@ -448,7 +765,7 @@ static int netxml_alarm_subscribe(const char *page) break; } - ret = ne_read_response_block(request, buf, sizeof buf); + ret = ne_read_response_block(request, resp_buf, sizeof(resp_buf)); if (ret == NE_OK) { ret = ne_end_request(request); @@ -460,19 +777,38 @@ static int netxml_alarm_subscribe(const char *page) /* due to different formats used by the various NMCs, we need to\ break up the reply in lines and parse each one separately */ - for (s = strtok(buf, "\r\n"); s != NULL; s = strtok(NULL, "\r\n")) { + for (s = strtok(resp_buf, "\r\n"); s != NULL; s = strtok(NULL, "\r\n")) { + long long int tmp_port = -1, tmp_secret = -1; upsdebugx(2, "%s: parsing %s", __func__, s); - if (!strncasecmp(s, "", 6) && (sscanf(s+6, "%u", &port) != 1)) { + if (!strncasecmp(s, "", 6) && (sscanf(s+6, "%lli", &tmp_port) != 1)) { return NE_RETRY; } - if (!strncasecmp(s, "", 8) && (sscanf(s+8, "%u", &secret) != 1)) { + /* FIXME? Does a port==0 make sense here? Or should the test below be for port<1? + * Legacy code until a fix here used sscanf() above to get a '%u' value... + */ + if (tmp_port < 0 || tmp_port > UINT_MAX) { + upsdebugx(2, "%s: parsing initial subcription failed, bad port value", __func__); return NE_RETRY; } + + if (!strncasecmp(s, "", 8) && (sscanf(s+8, "%lli", &tmp_secret) != 1)) { + return NE_RETRY; + } + + if (tmp_secret < 0 || tmp_secret > UINT_MAX) { + upsdebugx(2, "%s: parsing initial subcription failed, bad secret value", __func__); + return NE_RETRY; + } + + /* Range of valid values constrained above */ + port = (unsigned int)tmp_port; + secret = (int)tmp_secret; + } - if ((port == -1) || (secret == -1)) { + if ((port < 1) || (secret == -1)) { upsdebugx(2, "%s: parsing initial subcription failed", __func__); return NE_RETRY; } @@ -515,8 +851,8 @@ static int netxml_alarm_subscribe(const char *page) return NE_RETRY; } - snprintf(buf, sizeof(buf), "\n", secret); - ret = ne_sock_fullwrite(sock, buf, strlen(buf)); + snprintf(buf, sizeof(buf), "", secret); + ret = ne_sock_fullwrite(sock, buf, strlen(buf) + 1); if (ret != NE_OK) { upsdebugx(2, "%s: send failed: %s", __func__, ne_sock_error(sock)); @@ -576,6 +912,8 @@ static int netxml_dispatch_request(ne_request *request, ne_xml_parser *parser) /* Supply the 'login' and 'password' when authentication is required */ static int netxml_authenticate(void *userdata, const char *realm, int attempt, char *username, char *password) { + NUT_UNUSED_VARIABLE(userdata); + char *val; upsdebugx(2, "%s: realm = [%s], attempt = %d", __func__, realm, attempt); @@ -682,5 +1020,777 @@ static void netxml_status_set(void) if (STATUS_BIT(SHUTDOWNIMM)) { status_set("FSD"); /* shutdown imminent */ } + if (STATUS_BIT(CAL)) { + status_set("CAL"); /* calibrating */ + } } + +/* + * *_OBJECT interface implementation + */ + +static object_query_t *object_query_create( + object_query_type_t type, + object_post_mode_t mode) +{ + object_query_t *handle = (object_query_t *)calloc(1, + sizeof(object_query_t)); + + if (NULL == handle) + return NULL; + + handle->type = type; + handle->mode = mode; + + return handle; +} + + +static size_t object_query_size(object_query_t *handle) { + assert(NULL != handle); + + return handle->cnt; +} + + +/** + * \brief SET_OBJECT request list entry destructor + * + * \param req SET_OBJECT request list entry + */ +static void set_object_req_destroy(set_object_req_t *req) { + assert(NULL != req); + + if (NULL != req->name) + free(req->name); + + if (NULL != req->value) + free(req->value); +} + + +/** + * \brief SET_OBJECT response list entry destructor + * + * \param resp SET_OBJECT response list entry + */ +static void set_object_resp_destroy(set_object_resp_t *resp) { + assert(NULL != resp); + + if (NULL != resp->name) + free(resp->name); + + if (NULL != resp->unit) + free(resp->unit); + + if (NULL != resp->access) + free(resp->access); + + if (NULL != resp->value) + free(resp->value); +} + + +/** + * \brief *_OBJECT query entry destructor + * + * \param handle SET_OBJECT query handle + * \param entry SET_OBJECT query entry + */ +static void object_entry_destroy(object_query_t *handle, object_entry_t *entry) { + assert(NULL != handle); + assert(NULL != entry); + + /* Sanity checks */ + assert(0 < handle->cnt); + + /* Relink list */ + if (entry == handle->head) { + handle->head = entry->next; + } + else { + assert(NULL != entry->prev); + + entry->prev->next = entry->next; + } + + if (entry == handle->tail) { + handle->tail = entry->prev; + } + else { + assert(NULL != entry->next); + + entry->next->prev = entry->prev; + } + + --handle->cnt; + + /* Destroy payload */ + switch (handle->type) { + case SET_OBJECT_REQUEST: + set_object_req_destroy(&entry->payld.req); + + break; + + case SET_OBJECT_RESPONSE: + set_object_resp_destroy(&entry->payld.resp); + + break; + } + + /* Destroy entry */ + free(entry); +} + + +static void object_query_destroy(object_query_t *handle) { + assert(NULL != handle); + + /* Destroy entries */ + while (handle->cnt) + object_entry_destroy(handle, handle->head); + + /* Destroy handle */ + free(handle); +} + + +/** + * \brief Add *_OBJECT list entry (at list end) + * + * \param handle Entry list handle + * \param entry Entry + */ +static void object_add_entry(object_query_t *handle, object_entry_t *entry) { + assert(NULL != handle); + assert(NULL != entry); + + /* Sanity checks */ + assert(SET_OBJECT_REQUEST == handle->type); + + /* Add entry at end of bi-directional list */ + if (handle->cnt) { + assert(NULL != handle->tail); + assert(NULL == handle->tail->next); + + handle->tail->next = entry; + entry->prev = handle->tail; + } + + /* Add the very first entry */ + else { + handle->head = entry; + entry->prev = NULL; + } + + handle->tail = entry; + entry->next = NULL; + + ++handle->cnt; +} + + +static object_entry_t *set_object_add( + object_query_t *handle, + const char *name, + const char *value) +{ + char *name_cpy; + char *value_cpy; + + assert(NULL != name); + assert(NULL != value); + + object_entry_t *entry = (object_entry_t *)calloc(1, + sizeof(object_entry_t)); + + if (NULL == entry) + return NULL; + + /* Copy payload data */ + name_cpy = strdup(name); + value_cpy = strdup(value); + + /* Cleanup in case of memory error */ + if (NULL == name_cpy || NULL == value_cpy) { + if (NULL != name_cpy) + free(name_cpy); + + if (NULL != value_cpy) + free(value_cpy); + + free(entry); + + return NULL; + } + + /* Set payload */ + entry->payld.req.name = name_cpy; + entry->payld.req.value = value_cpy; + + /* Enlist */ + object_add_entry(handle, entry); + + return entry; +} + + +/** + * \brief Common SET_OBJECT entries serialiser + * + * \param buff Buffer + * \param entry SET_OBJECT request entry + * + * \return OBJECT_OK on success + * \return OBJECT_ERROR otherwise + */ +static object_query_status_t set_object_serialise_entries(ne_buffer *buff, object_entry_t *entry) { + object_query_status_t status = OBJECT_OK; + + assert(NULL != buff); + + ne_buffer_zappend(buff, "\r\n"); + ne_buffer_zappend(buff, "\r\n"); + + for (; NULL != entry; entry = entry->next) { + const char *vname = vname_nut2mge_xml(entry->payld.req.name); + + /* Serialise one object */ + if (NULL != vname) { + ne_buffer_zappend(buff, " "); + ne_buffer_zappend(buff, entry->payld.req.value); + ne_buffer_zappend(buff, "\r\n"); + } + + /* Var. name resolution error */ + else + status = OBJECT_ERROR; + } + + ne_buffer_zappend(buff, "\r\n"); + + return status; +} + + +static ne_buffer *set_object_serialise_raw(object_query_t *handle) { + assert(NULL != handle); + + /* Sanity checks */ + assert(SET_OBJECT_REQUEST == handle->type); + + /* Create buffer */ + ne_buffer *buff = ne_buffer_create(); + + /* neon API ref. states that the function always succeeds */ + assert(NULL != buff); + + /* Serialise all entries */ + set_object_serialise_entries(buff, handle->head); + + return buff; +} + + +static ne_buffer *set_object_serialise_form(object_query_t *handle) { + const char *vname = NULL; + + assert(NULL != handle); + + /* Sanity checks */ + assert(SET_OBJECT_REQUEST == handle->type); + + /* Create buffer */ + ne_buffer *buff = ne_buffer_create(); + + /* neon API ref. states that the function always succeeds */ + assert(NULL != buff); + + /* Simple request */ + if (1 == object_query_size(handle)) { + assert(NULL != handle->head); + + /* TODO: Simple req. doesn't seem to work + vname = vname_nut2mge_xml(handle->head->payld.req.name); + */ + } + if (NULL != vname) { + assert(NULL != handle->head); + + ne_buffer_zappend(buff, "objectName="); + ne_buffer_zappend(buff, vname); + ne_buffer_zappend(buff, "&objectValue="); + ne_buffer_zappend(buff, handle->head->payld.req.value); + } + + /* Multi set request (or empty request) */ + else { + /* Add request prologue */ + ne_buffer_zappend(buff, "--" FORM_POST_BOUNDARY "\r\n"); + ne_buffer_zappend(buff, "Content-Disposition: form-data; name=\"file\"; " + "filename=\"Configuration.xml\"\r\n"); + ne_buffer_zappend(buff, "Content-Type: application/octet-stream\r\n"); + ne_buffer_zappend(buff, "\r\n"); + + /* Serialise all entries */ + set_object_serialise_entries(buff, handle->head); + + /* Add request epilogue */ + ne_buffer_zappend(buff, "--" FORM_POST_BOUNDARY "--\r\n"); + } + + return buff; +} + + +/** + * \brief neon callback for SET_OBJECT RAW POST mode response element start + * + * \param userdata Obfuscated SET_OBJECT_RESPONSE query handle + * \param parent Element parent + * \param nspace Element namespace (empty) + * \param name Element name + * \param attrs Element attributes + * + * \return \c NE_XML_STATEROOT + distance of the element from root + */ +static int set_object_raw_resp_start_element( + void *userdata, + int parent, + const char *nspace, + const char *name, + const char **attrs) +{ + object_query_t *handle = (object_query_t *)userdata; + + assert(NULL != handle); + + /* Sanity checks */ + assert(SET_OBJECT_RESPONSE == handle->type); + + /* Check that namespace is empty */ + if (NULL != nspace && '\0' != *nspace) { + handle->status = OBJECT_PARSE_ERROR; + + return NE_XML_STATEROOT; + } + + /* OBJECT (as a SET_OBJECT child) */ + if (NE_XML_STATEROOT + 1 == parent && 0 == strcasecmp(name, "OBJECT")) { + size_t i; + + object_entry_t *entry = (object_entry_t *)calloc(1, + sizeof(object_entry_t)); + + /* Memory error */ + if (NULL == entry) { + handle->status = OBJECT_ERROR; + + return NE_XML_STATEROOT; + } + + /* Set attributes */ + for (i = 0; NULL != attrs[i] && NULL != attrs[i + 1]; i += 2) { + char **attr = NULL; + const char *aval = NULL; + + /* Skip unset attribute name and/or value (useless) */ + if (NULL == attrs[i] || NULL == attrs[i + 1]) + continue; + + /* Obviously, the following holds, now */ + assert(NULL != attrs[i]); + assert(NULL != attrs[i + 1]); + + /* name */ + if (0 == strcasecmp(attrs[i], "name")) { + attr = &entry->payld.resp.name; + aval = vname_mge_xml2nut(attrs[i + 1]); + } + + /* unit */ + else if (0 == strcasecmp(attrs[i], "unit")) { + attr = &entry->payld.resp.unit; + aval = attrs[i + 1]; + } + + /* access */ + else if (0 == strcasecmp(attrs[i], "access")) { + attr = &entry->payld.resp.access; + aval = attrs[i + 1]; + } + + /* Set known attribute */ + if (NULL != attr) { + /* Copy value */ + if (NULL != aval) { + *attr = strdup(aval); + + if (NULL == *attr) + handle->status = OBJECT_ERROR; + } + + /* Value resolution error */ + else + handle->status = OBJECT_ERROR; + } + } + + object_add_entry(handle, entry); + + return NE_XML_STATEROOT + 2; /* signal to cdata callback */ + } + + /* SET_OBJECT (as the root child) */ + if (NE_XML_STATEROOT == parent && 0 == strcasecmp(name, "SET_OBJECT")) + return NE_XML_STATEROOT + 1; + + /* Unknown element (as a SET_OBJECT child) */ + if (NE_XML_STATEROOT + 1 == parent) + return NE_XML_STATEROOT + 1; + + /* Ignore any other root children */ + return NE_XML_STATEROOT; +} + + +/** + * \brief neon callback for SET_OBJECT RAW POST mode response data start + * + * The callback is used to set OBJECT element value. + * This is done for state \c NE_XML_STATEROOT + 2 + * (see \ref set_object_raw_resp_start_element). + * + * \param userdata Obfuscated SET_OBJECT_RESPONSE query handle + * \param state Element distance from root + * \param cdata Character data + * \param len Character data length + * + * \return state + */ +static int set_object_raw_resp_cdata( + void *userdata, + int state, + const char *cdata, + size_t len) +{ + object_query_t *handle = (object_query_t *)userdata; + + assert(NULL != handle); + + /* Sanity checks */ + assert(SET_OBJECT_RESPONSE == handle->type); + + /* Ignore any element except OBJECT */ + if (NE_XML_STATEROOT + 2 != state) + return state; + + if (OBJECT_OK == handle->status) { + char *value; + + /* Set last object value */ + assert(NULL != handle->tail); + assert(NULL != handle->tail->payld.resp.name); + + value = vvalue_mge_xml2nut(handle->tail->payld.resp.name, cdata, len); + + handle->tail->payld.resp.value = value; + + if (NULL == handle->tail->payld.resp.value) + handle->status = OBJECT_ERROR; + } + + return state; +} + + +/** + * \brief neon callback for SET_OBJECT RAW POST mode response element start + * + * \param userdata Obfuscated SET_OBJECT_RESPONSE query handle + * \param state Element distance from root + * \param nspace Element namespace (empty) + * \param name Element name + * + * \return \c NE_XML_STATEROOT + distance of the element from root + */ +static int set_object_raw_resp_end_element( + void *userdata, + int state, + const char *nspace, + const char *name) +{ + NUT_UNUSED_VARIABLE(userdata); + NUT_UNUSED_VARIABLE(nspace); + + /* OBJECT (as a SET_OBJECT child) */ + if (NE_XML_STATEROOT + 2 == state) { + assert(0 == strcasecmp(name, "OBJECT")); + + return NE_XML_STATEROOT + 1; + } + + /* + * Otherwise, state is either NE_XML_STATEROOT or NE_XML_STATEROOT + 1 + * In any case, we return NE_XML_STATEROOT + */ + return NE_XML_STATEROOT; +} + + +static object_query_t *set_object_deserialise_raw(ne_buffer *buff) { + int ne_status; + + assert(NULL != buff); + + /* Create SET_OBJECT query response */ + object_query_t *handle = object_query_create(SET_OBJECT_RESPONSE, RAW_POST); + + if (NULL == handle) + return NULL; + + /* Create XML parser */ + ne_xml_parser *parser = ne_xml_create(); + + /* neon API ref. states that the function always succeeds */ + assert(NULL != parser); + + /* Set element & data handlers */ + ne_xml_push_handler( + parser, + set_object_raw_resp_start_element, + set_object_raw_resp_cdata, + set_object_raw_resp_end_element, + handle); + + /* Parse the response */ + ne_status = ne_xml_parse(parser, buff->data, buff->used); + + if (NE_OK != ne_status) + handle->status = OBJECT_PARSE_ERROR; + + /* Destroy parser */ + ne_xml_destroy(parser); + + return handle; +} + + +/** + * \brief Send HTTP request over a session + * + * The function creates HTTP request, sends it and reads-out the response. + * + * \param[in] argsession HTTP session + * \param[in] method Request method + * \param[in] arguri Request URI + * \param[in] ct Request content type (optional, \c NULL accepted) + * \param[in] req_body Request body (optional, \c NULL is accepted) + * \param[out] resp_body Response body (optional, \c NULL is accepted) + * + * \return HTTP status code if response was sent, 0 on send error + */ +static int send_http_request( + ne_session *argsession, + const char *method, + const char *arguri, + const char *ct, + ne_buffer *req_body, + ne_buffer *resp_body) +{ + int resp_code = 0; + + ne_request *req = NULL; + + /* Create request */ + req = ne_request_create(argsession, method, arguri); + + /* Neon claims that request creation is always successful */ + assert(NULL != req); + + do { /* Pragmatic do ... while (0) loop allowing breaks on error */ + const ne_status *req_st; + + /* Set Content-Type */ + if (NULL != ct) + ne_add_request_header(req, "Content-Type", ct); + + /* Set request body */ + if (NULL != req_body) + /* BEWARE: The terminating '\0' byte is "used", too */ + ne_set_request_body_buffer(req, + req_body->data, req_body->used - 1); + + /* Send request */ + int status = ne_begin_request(req); + + if (NE_OK != status) { + break; + } + + /* Read response */ + assert(NE_OK == status); + + for (;;) { + char buff[512]; + + ssize_t read; + + read = ne_read_response_block(req, buff, sizeof(buff)); + + /* Read failure */ + if (0 > read) { + status = NE_ERROR; + + break; + } + + if (0 == read) + break; + + if (NULL != resp_body) + ne_buffer_append(resp_body, buff, (size_t)read); + } + + if (NE_OK != status) { + break; + } + + /* Request served */ + ne_end_request(req); + + /* Get response code */ + req_st = ne_get_status(req); + + assert(NULL != req_st); + + resp_code = req_st->code; + + } while (0); /* end of do ... while (0) pragmatic loop */ + + if (NULL != req) + ne_request_destroy(req); + + return resp_code; +} + + +static object_query_t *set_object_raw(object_query_t *req) { + int resp_code; + object_query_t *resp = NULL; + ne_buffer *req_body = NULL; + ne_buffer *resp_body = NULL; + + assert(NULL != req); + + /* Sanity check */ + assert(SET_OBJECT_REQUEST == req->type); + assert(RAW_POST == req->mode); + + /* Serialise request POST data */ + req_body = set_object_serialise_raw(req); + + /* Send request */ + resp_body = ne_buffer_create(); + + assert(NULL != resp_body); + + resp_code = send_http_request(session, + "POST", "/set_obj.htm", NULL, req_body, resp_body); + + /* + * Repeat in case of 401 - Unauthorised + * + * Note that this is a WA of NMC sending Connection: close + * header in the 401 response, in which case neon closes + * connection (quite rightfully). + */ + if (401 == resp_code) + resp_code = send_http_request(session, + "POST", "/set_obj.htm", NULL, req_body, resp_body); + + /* Deserialise response */ + if (200 == resp_code) + resp = set_object_deserialise_raw(resp_body); + + /* Cleanup */ + if (NULL != req_body) + ne_buffer_destroy(req_body); + + if (NULL != resp_body) + ne_buffer_destroy(resp_body); + + return resp; +} + + +static object_query_t *set_object_form(object_query_t *req) { + int resp_code; + ne_buffer *req_body = NULL; + + const char *ct = "multipart/form-data; boundary=" FORM_POST_BOUNDARY; + + /* TODO: Single request doesn't seem to work + if (1 == object_query_size(req)) + ct = "application/x-form-urlencoded"; + */ + + assert(NULL != req); + + /* Sanity check */ + assert(SET_OBJECT_REQUEST == req->type); + assert(FORM_POST == req->mode); + + /* Serialise request POST data */ + req_body = set_object_serialise_form(req); + + /* Send request (response is ignored by the proto. spec v3) */ + resp_code = send_http_request(session, + "POST", "/Forms/set_obj_2", ct, req_body, NULL); + + /* + * Repeat in case of 401 - Unauthorised + * + * Note that this is a WA of NMC sending Connection: close + * header in the 401 response, in which case neon closes + * connection (quite rightfully). + */ + if (401 == resp_code) { + resp_code = send_http_request(session, + "POST", "/Forms/set_obj_2", ct, req_body, NULL); + } + + /* Cleanup */ + if (NULL != req_body) + ne_buffer_destroy(req_body); + + return NULL; +} + + +static object_query_t *set_object(object_query_t *req) { + object_query_t *resp = NULL; + + assert(NULL != req); + + /* Sanity checks */ + assert(SET_OBJECT_REQUEST == req->type); + + /* Select implementation by POST request mode */ + switch (req->mode) { + case RAW_POST: + resp = set_object_raw(req); + + break; + + case FORM_POST: + resp = set_object_form(req); + + break; + } + + return resp; +} diff --git a/drivers/netxml-ups.h b/drivers/netxml-ups.h index 4729b7a..555dc02 100644 --- a/drivers/netxml-ups.h +++ b/drivers/netxml-ups.h @@ -1,4 +1,4 @@ -/* netxml-ups.h Driver data/defines for network XML UPS units +/* netxml-ups.h Driver data/defines for network XML UPS units Copyright (C) 2008-2009 Arjen de Korte @@ -67,9 +67,11 @@ typedef enum { CHARGERFAIL, /* battery charger failure */ VRANGE, /* voltage out of range */ FRANGE, /* frequency out of range */ - FUSEFAULT /* fuse fault */ + FUSEFAULT, /* fuse fault */ + CAL /* calibrating */ } status_bit_t; extern uint32_t ups_status; +extern int shutdown_duration; #endif /* NETXML_UPS_H */ diff --git a/drivers/nut-ipmi.h b/drivers/nut-ipmi.h index 0cd0deb..669eb92 100644 --- a/drivers/nut-ipmi.h +++ b/drivers/nut-ipmi.h @@ -29,6 +29,14 @@ typedef enum { PSU_POWER_FAILURE /* = status OFF */ } psu_status_t; +#ifdef HAVE_FREEIPMI_11X_12X + /* args 8, 9, 10, 11 of ipmi_fru_multirecord_power_supply_information() */ + typedef int input_voltage_range_t; +#else + /* args 8, 9, 10, 11 of ipmi_fru_parse_multirecord_power_supply_information() */ + typedef unsigned int input_voltage_range_t; +#endif + /* Abstract structure to store information */ typedef struct IPMIDevice_s { int ipmi_id; /* FRU ID */ @@ -38,8 +46,8 @@ typedef struct IPMIDevice_s { char* part; /* Part Number */ char* date; /* Manufacturing Date/Time */ int overall_capacity; /* realpower.nominal? */ - int input_minvoltage; - int input_maxvoltage; + input_voltage_range_t input_minvoltage; + input_voltage_range_t input_maxvoltage; int input_minfreq; int input_maxfreq; int voltage; /* psu.voltage or device.voltage */ @@ -56,8 +64,7 @@ typedef struct IPMIDevice_s { /* Generic functions, to implement in the backends */ int nut_ipmi_open(int ipmi_id, IPMIDevice_t *ipmi_dev); void nut_ipmi_close(void); -int nut_ipmi_monitoring_init(); +int nut_ipmi_monitoring_init(void); int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev); #endif /* NUT_IPMI_H */ - diff --git a/drivers/nut-ipmipsu.c b/drivers/nut-ipmipsu.c index 18cb355..c0dd658 100644 --- a/drivers/nut-ipmipsu.c +++ b/drivers/nut-ipmipsu.c @@ -1,6 +1,7 @@ /* nut-ipmipsu.c - Driver for IPMI Power Supply Units (PSU) * - * Copyright (C) 2011 - Arnaud Quette + * Copyright (C) + * 2011 - 2012 Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,7 +27,7 @@ #include "nut-ipmi.h" #define DRIVER_NAME "IPMI PSU driver" -#define DRIVER_VERSION "0.06" +#define DRIVER_VERSION "0.31" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -46,11 +47,11 @@ upsdrv_info_t upsdrv_info = { /* Abstract structure to allow different IPMI implementation * We currently use FreeIPMI, but OpenIPMI and others are serious - * candidates! */ -IPMIDevice_t ipmi_dev; + * candidates! */ +static IPMIDevice_t ipmi_dev; /* Currently used to store FRU ID, but will probably evolve... */ -int ipmi_id = -1; +static int ipmi_id = -1; void upsdrv_initinfo(void) { @@ -62,9 +63,6 @@ void upsdrv_initinfo(void) ipmi_dev.manufacturer ? ipmi_dev.manufacturer : "unknown", ipmi_dev.product ? ipmi_dev.product : "unknown"); - dstate_setinfo("driver.version.data", "%s", DRIVER_NAME); - dstate_setinfo("driver.version.internal", DRIVER_VERSION); - dstate_setinfo ("device.type", "psu"); /* Publish information from the IPMI structure */ @@ -89,10 +87,17 @@ void upsdrv_initinfo(void) if (ipmi_dev.overall_capacity != -1) dstate_setinfo("ups.realpower.nominal", "%i", ipmi_dev.overall_capacity); + /* FIXME: Did older FreeIPMI with "unsigned int" voltage ranges + * have a way to report invalid readings? + */ +#ifdef HAVE_FREEIPMI_11X_12X if (ipmi_dev.input_minvoltage != -1) +#endif dstate_setinfo("input.voltage.minimum", "%i", ipmi_dev.input_minvoltage); +#ifdef HAVE_FREEIPMI_11X_12X if (ipmi_dev.input_maxvoltage != -1) +#endif dstate_setinfo("input.voltage.maximum", "%i", ipmi_dev.input_maxvoltage); if (ipmi_dev.input_minfreq != -1) @@ -138,6 +143,9 @@ void upsdrv_updateinfo(void) */ } +void upsdrv_shutdown(void) + __attribute__((noreturn)); + void upsdrv_shutdown(void) { fatalx(EXIT_FAILURE, "shutdown not supported"); @@ -183,20 +191,23 @@ void upsdrv_makevartable(void) "Authentication type to use during lan session activation"); addvar(VAR_VALUE, "type", "Type of the device to match ('psu' for \"Power Supply\")"); - + addvar(VAR_VALUE, "serial", "Serial number to match a specific device"); - addvar(VAR_VALUE, "fruid", "FRU identifier to match a specific device"); - addvar(VAR_VALUE, "sensorid", "Sensor identifier to match a specific device"); */ + addvar(VAR_VALUE, "fruid", "FRU identifier to match a specific device"); */ } void upsdrv_initups(void) { upsdebugx(1, "upsdrv_initups..."); - /* port can be expressed using: - * "id?" for device (FRU) ID 0x? - * "psu?" for PSU number ? - */ + /* port can be expressed in various forms: + * - inband: + * "id?" for device (FRU) ID 0x? + * "psu?" for PSU number ? + * - out of band + * "id?@host" + * "host" => requires serial or ... + */ if (!strncmp( device_path, "id", 2)) { ipmi_id = atoi(device_path+2); diff --git a/drivers/nut-libfreeipmi.c b/drivers/nut-libfreeipmi.c index 0e3415d..b37f58f 100644 --- a/drivers/nut-libfreeipmi.c +++ b/drivers/nut-libfreeipmi.c @@ -1,7 +1,7 @@ /* nut-libfreeipmi.c - NUT IPMI backend, using FreeIPMI * * Copyright (C) - * 2011 - Arnaud Quette + * 2011 - 2012 Arnaud Quette * 2011 - Albert Chu * * Based on the sample codes 'ipmi-fru-example.c', 'frulib.c' and @@ -39,14 +39,19 @@ */ +#include "config.h" /* must be first */ + #include #include #include "timehead.h" +#include "common.h" #include #include +#if HAVE_FREEIPMI_MONITORING #include -#include "common.h" +#endif #include "nut-ipmi.h" +#include "nut_stdint.h" #include "dstate.h" /* FreeIPMI defines */ @@ -56,12 +61,50 @@ #define IPMI_FRU_CUSTOM_FIELDS 64 /* FreeIPMI contexts and configuration*/ -ipmi_ctx_t ipmi_ctx = NULL; -ipmi_fru_parse_ctx_t fru_parse_ctx = NULL; -ipmi_sdr_cache_ctx_t sdr_cache_ctx = NULL; -ipmi_sdr_parse_ctx_t sdr_parse_ctx = NULL; -ipmi_monitoring_ctx_t mon_ctx = NULL; -struct ipmi_monitoring_ipmi_config ipmi_config; +static ipmi_ctx_t ipmi_ctx = NULL; +static ipmi_monitoring_ctx_t mon_ctx = NULL; +/* static struct ipmi_monitoring_ipmi_config ipmi_config; */ + +/* SDR management API has changed with 1.1.X and later */ +#ifdef HAVE_FREEIPMI_11X_12X + static ipmi_sdr_ctx_t sdr_ctx = NULL; + static ipmi_fru_ctx_t fru_ctx = NULL; + #define SDR_PARSE_CTX sdr_ctx + #define NUT_IPMI_SDR_CACHE_DEFAULTS IPMI_SDR_CACHE_CREATE_FLAGS_DEFAULT +#else + static ipmi_sdr_cache_ctx_t sdr_ctx = NULL; + static ipmi_sdr_parse_ctx_t sdr_parse_ctx = NULL; + #define SDR_PARSE_CTX sdr_parse_ctx + static ipmi_fru_parse_ctx_t fru_ctx = NULL; + /* Functions remapping */ + #define ipmi_sdr_ctx_create ipmi_sdr_cache_ctx_create + #define ipmi_sdr_ctx_destroy ipmi_sdr_cache_ctx_destroy + #define ipmi_sdr_ctx_errnum ipmi_sdr_cache_ctx_errnum + #define ipmi_sdr_ctx_errormsg ipmi_sdr_cache_ctx_errormsg + #define ipmi_fru_ctx_create ipmi_fru_parse_ctx_create + #define ipmi_fru_ctx_destroy ipmi_fru_parse_ctx_destroy + #define ipmi_fru_ctx_set_flags ipmi_fru_parse_ctx_set_flags + #define ipmi_fru_ctx_strerror ipmi_fru_parse_ctx_strerror + #define ipmi_fru_ctx_errnum ipmi_fru_parse_ctx_errnum + #define ipmi_fru_open_device_id ipmi_fru_parse_open_device_id + #define ipmi_fru_close_device_id ipmi_fru_parse_close_device_id + #define ipmi_fru_ctx_errormsg ipmi_fru_parse_ctx_errormsg + #define ipmi_fru_read_data_area ipmi_fru_parse_read_data_area + #define ipmi_fru_next ipmi_fru_parse_next + #define ipmi_fru_type_length_field_to_string ipmi_fru_parse_type_length_field_to_string + #define ipmi_fru_multirecord_power_supply_information ipmi_fru_parse_multirecord_power_supply_information + #define ipmi_fru_board_info_area ipmi_fru_parse_board_info_area + #define ipmi_fru_field_t ipmi_fru_parse_field_t + /* Constants */ + #define IPMI_SDR_MAX_RECORD_LENGTH IPMI_SDR_CACHE_MAX_SDR_RECORD_LENGTH + #define IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST IPMI_SDR_CACHE_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST + #define IPMI_FRU_AREA_SIZE_MAX IPMI_FRU_PARSE_AREA_SIZE_MAX + #define IPMI_FRU_FLAGS_SKIP_CHECKSUM_CHECKS IPMI_FRU_PARSE_FLAGS_SKIP_CHECKSUM_CHECKS + #define IPMI_FRU_AREA_TYPE_BOARD_INFO_AREA IPMI_FRU_PARSE_AREA_TYPE_BOARD_INFO_AREA + #define IPMI_FRU_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION + #define IPMI_FRU_AREA_STRING_MAX IPMI_FRU_PARSE_AREA_STRING_MAX + #define NUT_IPMI_SDR_CACHE_DEFAULTS IPMI_SDR_CACHE_CREATE_FLAGS_DEFAULT, IPMI_SDR_CACHE_VALIDATION_FLAGS_DEFAULT +#endif /* HAVE_FREEIPMI_11X_12X */ /* FIXME: freeipmi auto selects a cache based on the hostname you are * connecting too, but this is probably fine for you @@ -70,9 +113,9 @@ struct ipmi_monitoring_ipmi_config ipmi_config; /* Support functions */ static const char* libfreeipmi_getfield (uint8_t language_code, - ipmi_fru_parse_field_t *field); + ipmi_fru_field_t *field); -static void libfreeipmi_cleanup(); +static void libfreeipmi_cleanup(void); static int libfreeipmi_get_psu_info (const void *areabuf, uint8_t area_length, IPMIDevice_t *ipmi_dev); @@ -89,12 +132,22 @@ static int libfreeipmi_get_sensors_info (IPMIDevice_t *ipmi_dev); int nut_ipmi_open(int ipmi_id, IPMIDevice_t *ipmi_dev) { int ret = -1; - uint8_t areabuf[IPMI_FRU_PARSE_AREA_SIZE_MAX+1]; + uint8_t areabuf[IPMI_FRU_AREA_SIZE_MAX+1]; unsigned int area_type = 0; unsigned int area_length = 0; upsdebugx(1, "nut-libfreeipmi: nutipmi_open()..."); + /* FIXME? Check arg types for ipmi_fru_open_device_id() in configure? + * At this time it is uint8_t for libfreeipmi implementation of IPMI. + */ + if (ipmi_id > (int)UINT8_MAX) { + libfreeipmi_cleanup(); + fatal_with_errno(EXIT_FAILURE, + "nut_ipmi_open: ipmi_id %d is too large for libfreeipmi", + ipmi_id); + } + /* Initialize the FreeIPMI library. */ if (!(ipmi_ctx = ipmi_ctx_create ())) { @@ -126,26 +179,26 @@ int nut_ipmi_open(int ipmi_id, IPMIDevice_t *ipmi_dev) upsdebugx(1, "FreeIPMI initialized..."); /* Parse FRU information */ - if (!(fru_parse_ctx = ipmi_fru_parse_ctx_create (ipmi_ctx))) + if (!(fru_ctx = ipmi_fru_ctx_create (ipmi_ctx))) { libfreeipmi_cleanup(); - fatal_with_errno(EXIT_FAILURE, "ipmi_fru_parse_ctx_create()"); + fatal_with_errno(EXIT_FAILURE, "ipmi_fru_ctx_create()"); } - + /* lots of motherboards calculate checksums incorrectly */ - if (ipmi_fru_parse_ctx_set_flags (fru_parse_ctx, IPMI_FRU_PARSE_FLAGS_SKIP_CHECKSUM_CHECKS) < 0) + if (ipmi_fru_ctx_set_flags (fru_ctx, IPMI_FRU_FLAGS_SKIP_CHECKSUM_CHECKS) < 0) { libfreeipmi_cleanup(); - fatalx(EXIT_FAILURE, "ipmi_fru_parse_ctx_set_flags: %s\n", - ipmi_fru_parse_ctx_strerror (ipmi_fru_parse_ctx_errnum (fru_parse_ctx))); + fatalx(EXIT_FAILURE, "ipmi_fru_ctx_set_flags: %s\n", + ipmi_fru_ctx_strerror (ipmi_fru_ctx_errnum (fru_ctx))); } /* Now open the requested (local) PSU */ - if (ipmi_fru_parse_open_device_id (fru_parse_ctx, ipmi_id) < 0) + if (ipmi_fru_open_device_id (fru_ctx, (uint8_t)ipmi_id) < 0) { libfreeipmi_cleanup(); - fatalx(EXIT_FAILURE, "ipmi_fru_parse_open_device_id: %s\n", - ipmi_fru_parse_ctx_errormsg (fru_parse_ctx)); + fatalx(EXIT_FAILURE, "ipmi_fru_open_device_id: %s\n", + ipmi_fru_ctx_errormsg (fru_ctx)); } /* Set IPMI identifier */ @@ -156,38 +209,46 @@ int nut_ipmi_open(int ipmi_id, IPMIDevice_t *ipmi_dev) /* clear fields */ area_type = 0; area_length = 0; - memset (areabuf, '\0', IPMI_FRU_PARSE_AREA_SIZE_MAX + 1); + memset (areabuf, '\0', IPMI_FRU_AREA_SIZE_MAX + 1); /* parse FRU buffer */ - if (ipmi_fru_parse_read_data_area (fru_parse_ctx, + if (ipmi_fru_read_data_area (fru_ctx, &area_type, &area_length, areabuf, - IPMI_FRU_PARSE_AREA_SIZE_MAX) < 0) + IPMI_FRU_AREA_SIZE_MAX) < 0) { libfreeipmi_cleanup(); - fatal_with_errno(EXIT_FAILURE, - "ipmi_fru_parse_open_device_id: %s\n", - ipmi_fru_parse_ctx_errormsg (fru_parse_ctx)); + fatal_with_errno(EXIT_FAILURE, + "ipmi_fru_read_data_area: %s\n", + ipmi_fru_ctx_errormsg (fru_ctx)); } if (area_length) { + + if (area_length > (int)UINT8_MAX) { + libfreeipmi_cleanup(); + fatal_with_errno(EXIT_FAILURE, + "nut_ipmi_open: got area_length %d is too large for libfreeipmi", + area_length); + } + switch (area_type) { /* get generic board information */ - case IPMI_FRU_PARSE_AREA_TYPE_BOARD_INFO_AREA: + case IPMI_FRU_AREA_TYPE_BOARD_INFO_AREA: - if(libfreeipmi_get_board_info (areabuf, area_length, + if(libfreeipmi_get_board_info (areabuf, (uint8_t)area_length, ipmi_dev) < 0) { upsdebugx(1, "Can't retrieve board information"); } break; /* get specific PSU information */ - case IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION: + case IPMI_FRU_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION: - if(libfreeipmi_get_psu_info (areabuf, area_length, ipmi_dev) < 0) + if(libfreeipmi_get_psu_info (areabuf, (uint8_t)area_length, ipmi_dev) < 0) { upsdebugx(1, "Can't retrieve PSU information"); } @@ -197,13 +258,13 @@ int nut_ipmi_open(int ipmi_id, IPMIDevice_t *ipmi_dev) break; } } - } while ((ret = ipmi_fru_parse_next (fru_parse_ctx)) == 1); + } while ((ret = ipmi_fru_next (fru_ctx)) == 1); /* check for errors */ if (ret < 0) { libfreeipmi_cleanup(); - fatal_with_errno(EXIT_FAILURE, "ipmi_fru_parse_next: %s", - ipmi_fru_parse_ctx_errormsg (fru_parse_ctx)); + fatal_with_errno(EXIT_FAILURE, "ipmi_fru_next: %s", + ipmi_fru_ctx_errormsg (fru_ctx)); } else { /* Get all related sensors information */ @@ -224,25 +285,25 @@ void nut_ipmi_close(void) } static const char* libfreeipmi_getfield (uint8_t language_code, - ipmi_fru_parse_field_t *field) + ipmi_fru_field_t *field) { - static char strbuf[IPMI_FRU_PARSE_AREA_STRING_MAX + 1]; - unsigned int strbuflen = IPMI_FRU_PARSE_AREA_STRING_MAX; + static char strbuf[IPMI_FRU_AREA_STRING_MAX + 1]; + unsigned int strbuflen = IPMI_FRU_AREA_STRING_MAX; if (!field->type_length_field_length) return NULL; - memset (strbuf, '\0', IPMI_FRU_PARSE_AREA_STRING_MAX + 1); + memset (strbuf, '\0', IPMI_FRU_AREA_STRING_MAX + 1); - if (ipmi_fru_parse_type_length_field_to_string (fru_parse_ctx, + if (ipmi_fru_type_length_field_to_string (fru_ctx, field->type_length_field, field->type_length_field_length, language_code, strbuf, &strbuflen) < 0) { - upsdebugx (2, "ipmi_fru_parse_type_length_field_to_string: %s", - ipmi_fru_parse_ctx_errormsg (fru_parse_ctx)); + upsdebugx (2, "ipmi_fru_type_length_field_to_string: %s", + ipmi_fru_ctx_errormsg (fru_ctx)); return NULL; } @@ -271,18 +332,20 @@ static float libfreeipmi_get_voltage (uint8_t voltage_code) static void libfreeipmi_cleanup() { /* cleanup */ - if (fru_parse_ctx) { - ipmi_fru_parse_close_device_id (fru_parse_ctx); - ipmi_fru_parse_ctx_destroy (fru_parse_ctx); + if (fru_ctx) { + ipmi_fru_close_device_id (fru_ctx); + ipmi_fru_ctx_destroy (fru_ctx); } - if (sdr_cache_ctx) { - ipmi_sdr_cache_ctx_destroy (sdr_cache_ctx); + if (sdr_ctx) { + ipmi_sdr_ctx_destroy (sdr_ctx); } +#ifndef HAVE_FREEIPMI_11X_12X if (sdr_parse_ctx) { ipmi_sdr_parse_ctx_destroy (sdr_parse_ctx); } +#endif if (ipmi_ctx) { ipmi_ctx_close (ipmi_ctx); @@ -302,18 +365,18 @@ static int libfreeipmi_get_psu_info (const void *areabuf, { /* FIXME: directly use ipmi_dev fields */ unsigned int overall_capacity; - unsigned int low_end_input_voltage_range_1; - unsigned int high_end_input_voltage_range_1; + input_voltage_range_t low_end_input_voltage_range_1; + input_voltage_range_t high_end_input_voltage_range_1; unsigned int low_end_input_frequency_range; unsigned int high_end_input_frequency_range; - unsigned int voltage_1; + unsigned int voltage_1; /* code for conversion into a float */ /* FIXME: check for the interest and capability to use these data */ unsigned int peak_va; unsigned int inrush_current; unsigned int inrush_interval; - unsigned int low_end_input_voltage_range_2; - unsigned int high_end_input_voltage_range_2; + input_voltage_range_t low_end_input_voltage_range_2; + input_voltage_range_t high_end_input_voltage_range_2; unsigned int ac_dropout_tolerance; unsigned int predictive_fail_support; unsigned int power_factor_correction; @@ -328,7 +391,7 @@ static int libfreeipmi_get_psu_info (const void *areabuf, upsdebugx(1, "entering libfreeipmi_get_psu_info()"); - if (ipmi_fru_parse_multirecord_power_supply_information (fru_parse_ctx, + if (ipmi_fru_multirecord_power_supply_information (fru_ctx, areabuf, area_length, &overall_capacity, @@ -354,20 +417,42 @@ static int libfreeipmi_get_psu_info (const void *areabuf, &total_combined_wattage, &predictive_fail_tachometer_lower_threshold) < 0) { - fatalx(EXIT_FAILURE, "ipmi_fru_parse_multirecord_power_supply_information: %s", - ipmi_fru_parse_ctx_errormsg (fru_parse_ctx)); + fatalx(EXIT_FAILURE, "ipmi_fru_multirecord_power_supply_information: %s", + ipmi_fru_ctx_errormsg (fru_ctx)); } - ipmi_dev->overall_capacity = overall_capacity; + if (overall_capacity > (int)INT_MAX) + { + fatalx(EXIT_FAILURE, "ipmi_fru_multirecord_power_supply_information: overall_capacity exceeds expected range: %u", + overall_capacity); + } + ipmi_dev->overall_capacity = (int)overall_capacity; /* Voltages are in mV! */ ipmi_dev->input_minvoltage = low_end_input_voltage_range_1 / 1000; ipmi_dev->input_maxvoltage = high_end_input_voltage_range_1 / 1000; - ipmi_dev->input_minfreq = low_end_input_frequency_range; - ipmi_dev->input_maxfreq = high_end_input_frequency_range; + if (low_end_input_frequency_range > (int)INT_MAX) + { + fatalx(EXIT_FAILURE, "ipmi_fru_multirecord_power_supply_information: low_end_input_frequency_range exceeds expected range: %u", + low_end_input_frequency_range); + } + if (high_end_input_frequency_range > (int)INT_MAX) + { + fatalx(EXIT_FAILURE, "ipmi_fru_multirecord_power_supply_information: high_end_input_frequency_range exceeds expected range: %u", + high_end_input_frequency_range); + } + ipmi_dev->input_minfreq = (int)low_end_input_frequency_range; + ipmi_dev->input_maxfreq = (int)high_end_input_frequency_range; - ipmi_dev->voltage = libfreeipmi_get_voltage(voltage_1); + if (voltage_1 > (int)UINT8_MAX) + { + fatalx(EXIT_FAILURE, "ipmi_fru_multirecord_power_supply_information: voltage_1 code exceeds expected range: %u", + voltage_1); + } + ipmi_dev->voltage = libfreeipmi_get_voltage((uint8_t)voltage_1); + + upsdebugx(1, "libfreeipmi_get_psu_info() retrieved successfully"); return (0); } @@ -378,12 +463,12 @@ static int libfreeipmi_get_board_info (const void *areabuf, { uint8_t language_code; uint32_t mfg_date_time; - ipmi_fru_parse_field_t board_manufacturer; - ipmi_fru_parse_field_t board_product_name; - ipmi_fru_parse_field_t board_serial_number; - ipmi_fru_parse_field_t board_part_number; - ipmi_fru_parse_field_t board_fru_file_id; - ipmi_fru_parse_field_t board_custom_fields[IPMI_FRU_CUSTOM_FIELDS]; + ipmi_fru_field_t board_manufacturer; + ipmi_fru_field_t board_product_name; + ipmi_fru_field_t board_serial_number; + ipmi_fru_field_t board_part_number; + ipmi_fru_field_t board_fru_file_id; + ipmi_fru_field_t board_custom_fields[IPMI_FRU_CUSTOM_FIELDS]; const char *string = NULL; time_t timetmp; struct tm mfg_date_time_tm; @@ -392,15 +477,15 @@ static int libfreeipmi_get_board_info (const void *areabuf, upsdebugx(1, "entering libfreeipmi_get_board_info()"); /* clear fields */ - memset (&board_manufacturer, '\0', sizeof (ipmi_fru_parse_field_t)); - memset (&board_product_name, '\0', sizeof (ipmi_fru_parse_field_t)); - memset (&board_serial_number, '\0', sizeof (ipmi_fru_parse_field_t)); - memset (&board_fru_file_id, '\0', sizeof (ipmi_fru_parse_field_t)); + memset (&board_manufacturer, '\0', sizeof (ipmi_fru_field_t)); + memset (&board_product_name, '\0', sizeof (ipmi_fru_field_t)); + memset (&board_serial_number, '\0', sizeof (ipmi_fru_field_t)); + memset (&board_fru_file_id, '\0', sizeof (ipmi_fru_field_t)); memset (&board_custom_fields[0], '\0', - sizeof (ipmi_fru_parse_field_t) * IPMI_FRU_CUSTOM_FIELDS); + sizeof (ipmi_fru_field_t) * IPMI_FRU_CUSTOM_FIELDS); /* parse FRU buffer */ - if (ipmi_fru_parse_board_info_area (fru_parse_ctx, + if (ipmi_fru_board_info_area (fru_ctx, areabuf, area_length, &language_code, @@ -414,11 +499,10 @@ static int libfreeipmi_get_board_info (const void *areabuf, IPMI_FRU_CUSTOM_FIELDS) < 0) { libfreeipmi_cleanup(); - fatalx(EXIT_FAILURE, "ipmi_fru_parse_board_info_area: %s", - ipmi_fru_parse_ctx_errormsg (fru_parse_ctx)); + fatalx(EXIT_FAILURE, "ipmi_fru_board_info_area: %s", + ipmi_fru_ctx_errormsg (fru_ctx)); } - if (IPMI_FRU_LANGUAGE_CODE_VALID (language_code)) { upsdebugx (5, "FRU Board Language: %s", ipmi_fru_language_codes[language_code]); } @@ -430,7 +514,39 @@ static int libfreeipmi_get_board_info (const void *areabuf, * 'struct tm', thus passing 'struct tm' between functions could * have issues. So we need to memset */ memset (&mfg_date_time_tm, '\0', sizeof (struct tm)); - timetmp = mfg_date_time; + + /* Without a standard TIME_MAX, signedness may suffer; + * but we can at least check the number should fit */ +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + /* Stay ahead of possible redefinitions... */ + if (sizeof(mfg_date_time) > sizeof(timetmp)) + { + libfreeipmi_cleanup(); + fatalx(EXIT_FAILURE, "libfreeipmi_get_board_info: mfg_date_time type too large to process into a time_t"); + } + /* NOTE: Code until the end of method would also be "unreachable" + * for compilers or static analyzers that care about this, if the + * sizeof() check above fails on some architecture; build warnings + * should expose that so we look for a fix - so do not just blindly + * move the closing pragmas to end of method ;) + */ +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic pop +#endif + + timetmp = (time_t)mfg_date_time; localtime_r (&timetmp, &mfg_date_time_tm); memset (mfg_date_time_buf, '\0', IPMI_FRU_STR_BUFLEN + 1); strftime (mfg_date_time_buf, IPMI_FRU_STR_BUFLEN, "%D - %T", &mfg_date_time_tm); @@ -467,14 +583,14 @@ static int libfreeipmi_get_board_info (const void *areabuf, * Return -1 on error, or the number of sensors found otherwise */ static int libfreeipmi_get_sensors_info (IPMIDevice_t *ipmi_dev) { - uint8_t sdr_record[IPMI_SDR_CACHE_MAX_SDR_RECORD_LENGTH]; + uint8_t sdr_record[IPMI_SDR_MAX_RECORD_LENGTH]; uint8_t record_type, logical_physical_fru_device, logical_fru_device_device_slave_address; uint8_t tmp_entity_id, tmp_entity_instance; int sdr_record_len; uint16_t record_count; int found_device_id = 0; uint16_t record_id; - uint8_t entity_id, entity_instance; + uint8_t entity_id = 0, entity_instance = 0; int i; if (ipmi_ctx == NULL) @@ -484,90 +600,94 @@ static int libfreeipmi_get_sensors_info (IPMIDevice_t *ipmi_dev) ipmi_dev->sensors_count = 0; memset(ipmi_dev->sensors_id_list, 0, sizeof(ipmi_dev->sensors_id_list)); - if (!(sdr_cache_ctx = ipmi_sdr_cache_ctx_create ())) + if (!(sdr_ctx = ipmi_sdr_ctx_create ())) { libfreeipmi_cleanup(); - fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_cache_ctx_create()"); + fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_ctx_create()"); } +#ifndef HAVE_FREEIPMI_11X_12X if (!(sdr_parse_ctx = ipmi_sdr_parse_ctx_create ())) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_parse_ctx_create()"); } +#endif - if (ipmi_sdr_cache_open (sdr_cache_ctx, ipmi_ctx, CACHE_LOCATION) < 0) + if (ipmi_sdr_cache_open (sdr_ctx, ipmi_ctx, CACHE_LOCATION) < 0) { - if (ipmi_sdr_cache_ctx_errnum (sdr_cache_ctx) != IPMI_SDR_CACHE_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) + if (ipmi_sdr_ctx_errnum (sdr_ctx) != IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_cache_open: %s", - ipmi_sdr_cache_ctx_errormsg (sdr_cache_ctx)); + ipmi_sdr_ctx_errormsg (sdr_ctx)); } } - if (ipmi_sdr_cache_ctx_errnum (sdr_cache_ctx) == IPMI_SDR_CACHE_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) + if (ipmi_sdr_ctx_errnum (sdr_ctx) == IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) { - if (ipmi_sdr_cache_create (sdr_cache_ctx, + if (ipmi_sdr_cache_create (sdr_ctx, ipmi_ctx, CACHE_LOCATION, - IPMI_SDR_CACHE_CREATE_FLAGS_DEFAULT, - IPMI_SDR_CACHE_VALIDATION_FLAGS_DEFAULT, + NUT_IPMI_SDR_CACHE_DEFAULTS, NULL, NULL) < 0) { libfreeipmi_cleanup(); fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_cache_create: %s", - ipmi_sdr_cache_ctx_errormsg (sdr_cache_ctx)); + ipmi_sdr_ctx_errormsg (sdr_ctx)); } - - if (ipmi_sdr_cache_open (sdr_cache_ctx, - ipmi_ctx, CACHE_LOCATION) < 0) + if (ipmi_sdr_cache_open (sdr_ctx, ipmi_ctx, CACHE_LOCATION) < 0) { - if (ipmi_sdr_cache_ctx_errnum (sdr_cache_ctx) != IPMI_SDR_CACHE_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) + if (ipmi_sdr_ctx_errnum (sdr_ctx) != IPMI_SDR_ERR_CACHE_READ_CACHE_DOES_NOT_EXIST) { - libfreeipmi_cleanup(); - fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_cache_open: %s", - ipmi_sdr_cache_ctx_errormsg (sdr_cache_ctx)); + libfreeipmi_cleanup(); + fatal_with_errno(EXIT_FAILURE, "ipmi_sdr_cache_open: %s", + ipmi_sdr_ctx_errormsg (sdr_ctx)); } } } - if (ipmi_sdr_cache_record_count (sdr_cache_ctx, &record_count) < 0) - { + if (ipmi_sdr_cache_record_count (sdr_ctx, &record_count) < 0) { fprintf (stderr, - "ipmi_sdr_cache_record_count: %s", - ipmi_sdr_cache_ctx_errormsg (sdr_cache_ctx)); + "ipmi_sdr_cache_record_count: %s\n", + ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } - for (i = 0; i < record_count; i++, ipmi_sdr_cache_next (sdr_cache_ctx)) + upsdebugx(3, "Found %i records in SDR cache", record_count); + + for (i = 0; i < record_count; i++, ipmi_sdr_cache_next (sdr_ctx)) { - memset (sdr_record, '\0', IPMI_SDR_CACHE_MAX_SDR_RECORD_LENGTH); - if ((sdr_record_len = ipmi_sdr_cache_record_read (sdr_cache_ctx, + memset (sdr_record, '\0', IPMI_SDR_MAX_RECORD_LENGTH); + + if ((sdr_record_len = ipmi_sdr_cache_record_read (sdr_ctx, sdr_record, - IPMI_SDR_CACHE_MAX_SDR_RECORD_LENGTH)) < 0) + IPMI_SDR_MAX_RECORD_LENGTH)) < 0) { - fprintf (stderr, "ipmi_sdr_cache_record_read: %s", - ipmi_sdr_cache_ctx_errormsg (sdr_cache_ctx)); + fprintf (stderr, "ipmi_sdr_cache_record_read: %s\n", + ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } - - if (ipmi_sdr_parse_record_id_and_type (sdr_parse_ctx, + if (ipmi_sdr_parse_record_id_and_type (SDR_PARSE_CTX, sdr_record, - sdr_record_len, + (unsigned int)sdr_record_len, NULL, &record_type) < 0) { - fprintf (stderr, "ipmi_sdr_parse_record_id_and_type: %s", - ipmi_sdr_parse_ctx_errormsg (sdr_parse_ctx)); + fprintf (stderr, "ipmi_sdr_parse_record_id_and_type: %s\n", + ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } - if (record_type != IPMI_SDR_FORMAT_FRU_DEVICE_LOCATOR_RECORD) - continue; + upsdebugx (5, "Checking record %i (/%i)", i, record_count); - if (ipmi_sdr_parse_fru_device_locator_parameters (sdr_parse_ctx, + if (record_type != IPMI_SDR_FORMAT_FRU_DEVICE_LOCATOR_RECORD) { + upsdebugx(1, "=======> not device locator (%i)!!", record_type); + continue; + } + + if (ipmi_sdr_parse_fru_device_locator_parameters (SDR_PARSE_CTX, sdr_record, - sdr_record_len, + (unsigned int)sdr_record_len, NULL, &logical_fru_device_device_slave_address, NULL, @@ -575,25 +695,28 @@ static int libfreeipmi_get_sensors_info (IPMIDevice_t *ipmi_dev) &logical_physical_fru_device, NULL) < 0) { - fprintf (stderr, "ipmi_sdr_parse_fru_device_locator_parameters: %s", - ipmi_sdr_parse_ctx_errormsg (sdr_parse_ctx)); + fprintf (stderr, "ipmi_sdr_parse_fru_device_locator_parameters: %s\n", + ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } + upsdebugx(2, "Checking device %i/%i", logical_physical_fru_device, + logical_fru_device_device_slave_address); + if (logical_physical_fru_device && logical_fru_device_device_slave_address == ipmi_dev->ipmi_id) { found_device_id++; - if (ipmi_sdr_parse_fru_entity_id_and_instance (sdr_parse_ctx, + if (ipmi_sdr_parse_fru_entity_id_and_instance (SDR_PARSE_CTX, sdr_record, - sdr_record_len, + (unsigned int)sdr_record_len, &entity_id, &entity_instance) < 0) { fprintf (stderr, - "ipmi_sdr_parse_fru_entity_id_and_instance: %s", - ipmi_sdr_parse_ctx_errormsg (sdr_parse_ctx)); + "ipmi_sdr_parse_fru_entity_id_and_instance: %s\n", + ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } break; @@ -602,43 +725,43 @@ static int libfreeipmi_get_sensors_info (IPMIDevice_t *ipmi_dev) if (!found_device_id) { - fprintf (stderr, "Couldn't find device id %d", ipmi_dev->ipmi_id); + fprintf (stderr, "Couldn't find device id %d\n", ipmi_dev->ipmi_id); goto cleanup; } else upsdebugx(1, "Found device id %d", ipmi_dev->ipmi_id); - if (ipmi_sdr_cache_first (sdr_cache_ctx) < 0) + if (ipmi_sdr_cache_first (sdr_ctx) < 0) { - fprintf (stderr, "ipmi_sdr_cache_first: %s", - ipmi_sdr_cache_ctx_errormsg (sdr_cache_ctx)); + fprintf (stderr, "ipmi_sdr_cache_first: %s\n", + ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } - for (i = 0; i < record_count; i++, ipmi_sdr_cache_next (sdr_cache_ctx)) + for (i = 0; i < record_count; i++, ipmi_sdr_cache_next (sdr_ctx)) { /* uint8_t sdr_record[IPMI_SDR_CACHE_MAX_SDR_RECORD_LENGTH]; uint8_t record_type, tmp_entity_id, tmp_entity_instance; int sdr_record_len; */ - memset (sdr_record, '\0', IPMI_SDR_CACHE_MAX_SDR_RECORD_LENGTH); - if ((sdr_record_len = ipmi_sdr_cache_record_read (sdr_cache_ctx, + memset (sdr_record, '\0', IPMI_SDR_MAX_RECORD_LENGTH); + if ((sdr_record_len = ipmi_sdr_cache_record_read (sdr_ctx, sdr_record, - IPMI_SDR_CACHE_MAX_SDR_RECORD_LENGTH)) < 0) + IPMI_SDR_MAX_RECORD_LENGTH)) < 0) { - fprintf (stderr, "ipmi_sdr_cache_record_read: %s", - ipmi_sdr_cache_ctx_errormsg (sdr_cache_ctx)); + fprintf (stderr, "ipmi_sdr_cache_record_read: %s\n", + ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } - if (ipmi_sdr_parse_record_id_and_type (sdr_parse_ctx, + if (ipmi_sdr_parse_record_id_and_type (SDR_PARSE_CTX, sdr_record, - sdr_record_len, + (unsigned int)sdr_record_len, &record_id, &record_type) < 0) { - fprintf (stderr, "ipmi_sdr_parse_record_id_and_type: %s", - ipmi_sdr_parse_ctx_errormsg (sdr_parse_ctx)); + fprintf (stderr, "ipmi_sdr_parse_record_id_and_type: %s\n", + ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } @@ -650,15 +773,15 @@ static int libfreeipmi_get_sensors_info (IPMIDevice_t *ipmi_dev) continue; } - if (ipmi_sdr_parse_entity_id_instance_type (sdr_parse_ctx, + if (ipmi_sdr_parse_entity_id_instance_type (SDR_PARSE_CTX, sdr_record, - sdr_record_len, + (unsigned int)sdr_record_len, &tmp_entity_id, &tmp_entity_instance, NULL) < 0) { - fprintf (stderr, "ipmi_sdr_parse_entity_instance_type: %s", - ipmi_sdr_parse_ctx_errormsg (sdr_parse_ctx)); + fprintf (stderr, "ipmi_sdr_parse_entity_instance_type: %s\n", + ipmi_sdr_ctx_errormsg (sdr_ctx)); goto cleanup; } @@ -677,15 +800,21 @@ static int libfreeipmi_get_sensors_info (IPMIDevice_t *ipmi_dev) cleanup: /* Cleanup */ - if (sdr_cache_ctx) { - ipmi_sdr_cache_ctx_destroy (sdr_cache_ctx); + if (sdr_ctx) { + ipmi_sdr_ctx_destroy (sdr_ctx); } +#ifndef HAVE_FREEIPMI_11X_12X if (sdr_parse_ctx) { ipmi_sdr_parse_ctx_destroy (sdr_parse_ctx); } +#endif /* HAVE_FREEIPMI_11X_12X */ - return ipmi_dev->sensors_count; + if (ipmi_dev->sensors_count > INT_MAX) { + upsdebugx(1, "%s: Found %i sensors which is too many", + __func__, ipmi_dev->sensors_count); + } + return (int)ipmi_dev->sensors_count; } @@ -775,6 +904,8 @@ int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev) return -1; } + upsdebugx (1, "nut_ipmi_get_sensors_status: %i sensors to check", sensor_count); + for (i = 0; i < sensor_count; i++, ipmi_monitoring_sensor_iterator_next (mon_ctx)) { int record_id, sensor_type; @@ -797,6 +928,8 @@ int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev) continue; } + upsdebugx (1, "checking sensor #%i, type %i", record_id, sensor_type); + /* should we consider this for ALARM? * IPMI_MONITORING_STATE_NOMINAL * IPMI_MONITORING_STATE_WARNING @@ -804,7 +937,7 @@ int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev) * if ((sensor_state = ipmi_monitoring_sensor_read_sensor_state (mon_ctx)) < 0) * ... */ - if ((sensor_reading = ipmi_monitoring_sensor_read_sensor_reading (mon_ctx)) < 0) + if ((sensor_reading = ipmi_monitoring_sensor_read_sensor_reading (mon_ctx)) == NULL) { upsdebugx (1, "ipmi_monitoring_sensor_read_sensor_reading() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); @@ -824,7 +957,7 @@ int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev) continue; } - if ((sensor_bitmask_strings = ipmi_monitoring_sensor_read_sensor_bitmask_strings (mon_ctx)) < 0) + if ((sensor_bitmask_strings = ipmi_monitoring_sensor_read_sensor_bitmask_strings (mon_ctx)) == NULL) { upsdebugx (1, "ipmi_monitoring_sensor_read_sensor_bitmask_strings() error: %s", ipmi_monitoring_ctx_errormsg (mon_ctx)); @@ -838,16 +971,19 @@ int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev) ipmi_dev->temperature = *((double *)sensor_reading); upsdebugx (3, "Temperature: %.2f", *((double *)sensor_reading)); dstate_setinfo("ambient.temperature", "%.2f", *((double *)sensor_reading)); + retval = 0; break; case IPMI_MONITORING_SENSOR_TYPE_VOLTAGE: ipmi_dev->voltage = *((double *)sensor_reading); upsdebugx (3, "Voltage: %.2f", *((double *)sensor_reading)); dstate_setinfo("input.voltage", "%.2f", *((double *)sensor_reading)); + retval = 0; break; case IPMI_MONITORING_SENSOR_TYPE_CURRENT: ipmi_dev->input_current = *((double *)sensor_reading); upsdebugx (3, "Current: %.2f", *((double *)sensor_reading)); dstate_setinfo("input.current", "%.2f", *((double *)sensor_reading)); + retval = 0; break; case IPMI_MONITORING_SENSOR_TYPE_POWER_SUPPLY: @@ -898,6 +1034,7 @@ int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev) str_count++; } break; + /* Not sure of the values of these, so get as much as possible... */ case IPMI_MONITORING_SENSOR_TYPE_POWER_UNIT: upsdebugx (3, "Power Unit: status string"); @@ -928,7 +1065,7 @@ int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev) break; } } - + /* Process status if needed */ if (psu_status != PSU_STATUS_UNKNOWN) { @@ -938,6 +1075,7 @@ int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev) { case PSU_PRESENT: status_set("OL"); + retval = 0; break; case PSU_ABSENT: status_set("OFF"); @@ -946,12 +1084,19 @@ int nut_ipmi_get_sensors_status(IPMIDevice_t *ipmi_dev) break; case PSU_POWER_FAILURE: status_set("OFF"); + retval = 0; break; } - + status_commit(); } #endif /* HAVE_FREEIPMI_MONITORING */ return retval; } + +/* +--chassis-control=CONTROL + Control the chassis. This command provides power-up, power-down, and reset control. Supported values: POWER-DOWN, POWER-UP, POWER-CYCLE, HARD-RESET, DIAGNOS‐ + TIC-INTERRUPT, SOFT-SHUTDOWN. +*/ diff --git a/drivers/nut_libusb.h b/drivers/nut_libusb.h new file mode 100644 index 0000000..20473d3 --- /dev/null +++ b/drivers/nut_libusb.h @@ -0,0 +1,111 @@ +/*! + * @file nut_libusb.h + * @brief HID Library - Generic USB backend for Generic HID Access (using MGE HIDParser) + * + * @author Copyright (C) + * 2003 - 2016 Arnaud Quette + * 2005 Peter Selinger + * 2021 Jim Klimov + * + * This program is sponsored by MGE UPS SYSTEMS - opensource.mgeups.com + * + * The logic of this file is ripped from mge-shut driver (also from + * Arnaud Quette), which is a "HID over serial link" UPS driver for + * Network UPS Tools + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * -------------------------------------------------------------------------- */ + +#ifndef NUT_LIBUSB_H_SEEN +#define NUT_LIBUSB_H_SEEN 1 + +#include "main.h" /* for subdrv_info_t */ +#include "usb-common.h" /* for USBDevice_t and USBDeviceMatcher_t, + * and for libusb headers and 0.1/1.0 mapping */ + +/* Used in drivers/libusb*.c sources: */ +#define LIBUSB_DEFAULT_INTERFACE 0 +#define LIBUSB_DEFAULT_DESC_INDEX 0 +#define LIBUSB_DEFAULT_HID_EP_IN 1 +#define LIBUSB_DEFAULT_HID_EP_OUT 1 + +extern upsdrv_info_t comm_upsdrv_info; + +/*! + * usb_communication_subdriver_s: structure to describe the communication routines + * @name: can be either "shut" for Serial HID UPS Transfer (from MGE) or "usb" + */ +typedef struct usb_communication_subdriver_s { + const char *name; /* name of this subdriver */ + const char *version; /* version of this subdriver */ + + int (*open)(usb_dev_handle **sdevp, /* try to open the next available */ + USBDevice_t *curDevice, /* device matching USBDeviceMatcher_t */ + USBDeviceMatcher_t *matcher, + int (*callback)(usb_dev_handle *udev, USBDevice_t *hd, + usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen)); + + void (*close)(usb_dev_handle *sdev); + + int (*get_report)(usb_dev_handle *sdev, usb_ctrl_repindex ReportId, + usb_ctrl_charbuf raw_buf, usb_ctrl_charbufsize ReportSize); + + int (*set_report)(usb_dev_handle *sdev, usb_ctrl_repindex ReportId, + usb_ctrl_charbuf raw_buf, usb_ctrl_charbufsize ReportSize); + + int (*get_string)(usb_dev_handle *sdev, + usb_ctrl_strindex StringIdx, char *buf, usb_ctrl_charbufsize buflen); + + int (*get_interrupt)(usb_dev_handle *sdev, + usb_ctrl_charbuf buf, usb_ctrl_charbufsize bufsize, + usb_ctrl_timeout_msec timeout); + + /* Used for Powervar UPS or similar cases to make sure + * we use the right interface in the Composite device. + * In a few cases our libusb*.c sets the value for specific + * VID/PID combinations, in others the subdrivers do so. + * FIXME: The numeric value here seems to fit and + * gets used in several contexts, it may be cleaner + * to separate them eventually. Usages in NUT were + * seen as following libusb API (1.0 and 0.1) args, + * along with some hints from libusb sources/headers: + * libusb-1.0: + * libusb_claim_interface - "int interface_number" + * libusb_detach_kernel_driver - "int interface_number" + * libusb_get_config_descriptor - "uint8_t config_index" ("the index of the configuration you wish to retrieve") + * libusb_control_transfer - "uint16_t wIndex" ("the index field for the setup packet"; "should be given in host-endian byte order") + * libusb-0.1: // The parameters mirror the types of the same name in the USB specification + * usb_claim_interface - "int interface" + * usb_control_msg - "int index" + * (_np = non-portable => only on some systems, like FreeBSD) + * usb_detach_kernel_driver_np - "int interface" + */ + usb_ctrl_repindex hid_rep_index; /* number of the interface we use in a composite USB device; see comments above */ + + /* All devices use HID descriptor at index 0. + * However, some UPS like newer Eaton units have + * a light HID descriptor at index 0, and + * the full version is at index 1 (in which + * case, bcdDevice == 0x0202) + */ + usb_ctrl_descindex hid_desc_index; /* HID descriptor is at this index (non-trivial for composite USB devices); see comments above */ + usb_ctrl_endpoint hid_ep_in; /* Input interrupt endpoint. Default is 1 */ + usb_ctrl_endpoint hid_ep_out; /* Output interrupt endpoint. Default is 1 */ +} usb_communication_subdriver_t; + +extern usb_communication_subdriver_t usb_subdriver; + +#endif /* NUT_LIBUSB_H_SEEN */ diff --git a/drivers/nutdrv_atcl_usb.c b/drivers/nutdrv_atcl_usb.c new file mode 100644 index 0000000..4d51a4b --- /dev/null +++ b/drivers/nutdrv_atcl_usb.c @@ -0,0 +1,675 @@ +/* + * nutdrv_atcl_usb.c - driver for generic-brand "ATCL FOR UPS" + * + * Copyright (C) 2013-2014 Charles Lepple + * Copyright (C) 2016 Eaton + * + * Loosely based on richcomm_usb.c, + * Copyright (C) 2007 Peter van Valderen + * Dirk Teurlings + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "main.h" +#include "usb-common.h" + +/* driver version */ +#define DRIVER_NAME "'ATCL FOR UPS' USB driver" +#define DRIVER_VERSION "1.16" + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Charles Lepple ", + DRV_EXPERIMENTAL, + { NULL } +}; + +#define STATUS_ENDPOINT (USB_ENDPOINT_IN | 1) +#define SHUTDOWN_ENDPOINT (USB_ENDPOINT_OUT | 2) +#define STATUS_PACKETSIZE 8 +#define SHUTDOWN_PACKETSIZE 8 + +/* Probably can reduce this, since the pcap file shows mostly 1050-ish ms response times */ +#define ATCL_USB_TIMEOUT USB_TIMEOUT + +/* limit the amount of spew that goes in the syslog when we lose the UPS (from nut_usb.h) */ +#define USB_ERR_LIMIT 10 /* start limiting after 10 in a row */ +#define USB_ERR_RATE 10 /* then only print every 10th error */ + +#define USB_VENDOR_STRING "ATCL FOR UPS" + +static usb_device_id_t atcl_usb_id[] = { + /* ATCL FOR UPS */ + { USB_DEVICE(0x0001, 0x0000), NULL }, + + /* Terminating entry */ + { 0, 0, NULL } +}; + +static usb_dev_handle *udev = NULL; +static USBDevice_t usbdevice; +static unsigned int comm_failures = 0; + +static int device_match_func(USBDevice_t *device, void *privdata) +{ + char *requested_vendor; + NUT_UNUSED_VARIABLE(privdata); + + switch (is_usb_device_supported(atcl_usb_id, device)) + { + case SUPPORTED: + if(!device->Vendor) { + upsdebugx(1, "Couldn't retrieve USB string descriptor for vendor. Check permissions?"); + requested_vendor = getval("vendor"); + if(requested_vendor) { + if(!strcmp("NULL", requested_vendor)) { + upsdebugx(3, "Matched device with NULL vendor string."); + return 1; + } + } + upsdebugx(1, "To keep trying (in case your device does not have a vendor string), use vendor=NULL"); + return 0; + } + + if(!strcmp(device->Vendor, USB_VENDOR_STRING)) { + upsdebugx(4, "Matched expected vendor='%s'.", USB_VENDOR_STRING); + return 1; + } + /* Didn't match, but the user provided an alternate vendor ID: */ + requested_vendor = getval("vendor"); + if(requested_vendor) { + if(!strcmp(device->Vendor, requested_vendor)) { + upsdebugx(3, "Matched device with vendor='%s'.", requested_vendor); + return 1; + } else { + upsdebugx(2, "idVendor=%04x and idProduct=%04x, " + "but provided vendor '%s' does not match device: '%s'.", + device->VendorID, device->ProductID, requested_vendor, device->Vendor); + return 0; + } + } + + /* TODO: automatic way of suggesting other drivers? */ + upsdebugx(2, "idVendor=%04x and idProduct=%04x, " + "but device vendor string '%s' does not match expected string '%s'. " + "Have you tried the nutdrv_qx driver?", + device->VendorID, device->ProductID, device->Vendor, USB_VENDOR_STRING); + return 0; + + case POSSIBLY_SUPPORTED: + case NOT_SUPPORTED: + default: + return 0; + } +} + +static USBDeviceMatcher_t device_matcher = { + &device_match_func, + NULL, + NULL +}; + +static int query_ups(char *reply) +{ + int ret; + + ret = usb_interrupt_read(udev, STATUS_ENDPOINT, (usb_ctrl_charbuf)reply, STATUS_PACKETSIZE, ATCL_USB_TIMEOUT); + + if (ret <= 0) { + upsdebugx(2, "status interrupt read: %s", ret ? nut_usb_strerror(ret) : "timeout"); + return ret; + } + + assert ((uintmax_t)ret < (uintmax_t)SIZE_MAX); + upsdebug_hex(3, "read", reply, (size_t)ret); + return ret; +} + +static void usb_comm_fail(const char *fmt, ...) +{ + int ret; + char why[SMALLBUF]; + va_list ap; + + /* this means we're probably here because select was interrupted */ + if (exit_flag != 0) { + return; /* ignored, since we're about to exit anyway */ + } + + comm_failures++; + + if ((comm_failures == USB_ERR_LIMIT) || ((comm_failures % USB_ERR_RATE) == 0)) { + upslogx(LOG_WARNING, "Warning: excessive comm failures, limiting error reporting"); + } + + /* once it's past the limit, only log once every USB_ERR_LIMIT calls */ + if ((comm_failures > USB_ERR_LIMIT) && ((comm_failures % USB_ERR_LIMIT) != 0)) { + return; + } + + /* generic message if the caller hasn't elaborated */ + if (!fmt) { + upslogx(LOG_WARNING, "Communications with UPS lost - check cabling"); + return; + } + + va_start(ap, fmt); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + ret = vsnprintf(why, sizeof(why), fmt, ap); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + va_end(ap); + + if ((ret < 1) || (ret >= (int) sizeof(why))) { + upslogx(LOG_WARNING, "usb_comm_fail: vsnprintf needed more than %d bytes", (int)sizeof(why)); + } + + upslogx(LOG_WARNING, "Communications with UPS lost: %s", why); +} + +static void usb_comm_good(void) +{ + if (comm_failures == 0) { + return; + } + + upslogx(LOG_NOTICE, "Communications with UPS re-established"); + comm_failures = 0; +} + +/* + * Callback that is called by usb_device_open() that handles USB device + * settings prior to accepting the devide. At the very least claim the + * device here. Detaching the kernel driver will be handled by the + * caller, don't do this here. Return < 0 on error, 0 or higher on + * success. + */ +static int driver_callback(usb_dev_handle *handle, USBDevice_t *device) +{ + int ret; + NUT_UNUSED_VARIABLE(device); + + if ((ret = usb_set_configuration(handle, 1)) < 0) { + upslogx(LOG_WARNING, "Can't set USB configuration: %s", nut_usb_strerror(ret)); + return -1; + } + + if ((ret = usb_claim_interface(handle, 0)) < 0) { + upslogx(LOG_WARNING, "Can't claim USB interface: %s", nut_usb_strerror(ret)); + return -1; + } + + /* TODO: HID SET_IDLE to 0 (not necessary?) */ + + return 1; +} + +static int usb_device_close(usb_dev_handle *handle) +{ + int ret = 0; + + if (!handle) { + return 0; + } + + /* usb_release_interface() sometimes blocks and goes + * into uninterruptible sleep. So don't do it. + */ + /* usb_release_interface(handle, 0); */ + +#if WITH_LIBUSB_1_0 + libusb_close(handle); + libusb_exit(NULL); +#else + ret = usb_close(handle); +#endif + + return ret; +} + +static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDeviceMatcher_t *matcher, + int (*callback)(usb_dev_handle *handle, USBDevice_t *device)) +{ + int ret = 0; + uint8_t iManufacturer = 0, iProduct = 0, iSerialNumber = 0; + + /* libusb base init */ +#if WITH_LIBUSB_1_0 + if (libusb_init(NULL) < 0) { + libusb_exit(NULL); + fatal_with_errno(EXIT_FAILURE, "Failed to init libusb 1.0"); + } +#else /* => WITH_LIBUSB_0_1 */ + usb_init(); + usb_find_busses(); + usb_find_devices(); +#endif /* WITH_LIBUSB_1_0 */ + +#ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ + /* Causes a double free corruption in linux if device is detached! */ + /* usb_device_close(*handlep); */ + if (*handlep) + usb_close(*handlep); +#endif + +#if WITH_LIBUSB_1_0 + libusb_device **devlist; + ssize_t devcount = 0; + libusb_device_handle *handle; + struct libusb_device_descriptor dev_desc; + uint8_t bus; + int i; + + devcount = libusb_get_device_list(NULL, &devlist); + if (devcount <= 0) + fatal_with_errno(EXIT_FAILURE, "No USB device found"); + + for (i = 0; i < devcount; i++) { + + USBDeviceMatcher_t *m; + libusb_device *dev = devlist[i]; + libusb_get_device_descriptor(dev, &dev_desc); + ret = libusb_open(dev, &handle); + *handlep = handle; +#else /* => WITH_LIBUSB_0_1 */ + struct usb_bus *bus; + for (bus = usb_busses; bus; bus = bus->next) { + + struct usb_device *dev; + usb_dev_handle *handle; + + for (dev = bus->devices; dev; dev = dev->next) { + + int i; + USBDeviceMatcher_t *m; + + upsdebugx(3, "Checking USB device [%04x:%04x] (%s/%s)", + dev->descriptor.idVendor, + dev->descriptor.idProduct, + bus->dirname, dev->filename); + + /* supported vendors are now checked by the supplied matcher */ + + /* open the device */ + *handlep = handle = usb_open(dev); +#endif /* WITH_LIBUSB_1_0 */ + + if (!handle) { + upsdebugx(4, "Failed to open USB device, skipping: %s", nut_usb_strerror(ret)); + continue; + } + + /* collect the identifying information of this + device. Note that this is safe, because + there's no need to claim an interface for + this (and therefore we do not yet need to + detach any kernel drivers). */ + + free(device->Vendor); + free(device->Product); + free(device->Serial); + free(device->Bus); + + memset(device, 0, sizeof(*device)); + +#if WITH_LIBUSB_1_0 + device->VendorID = dev_desc.idVendor; + device->ProductID = dev_desc.idProduct; + bus = libusb_get_bus_number(dev); + device->Bus = (char *)malloc(4); + if (device->Bus == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + sprintf(device->Bus, "%03d", bus); + iManufacturer = dev_desc.iManufacturer; + iProduct = dev_desc.iProduct; + iSerialNumber = dev_desc.iSerialNumber; +#else /* => WITH_LIBUSB_0_1 */ + device->VendorID = dev->descriptor.idVendor; + device->ProductID = dev->descriptor.idProduct; + device->Bus = xstrdup(bus->dirname); + iManufacturer = dev->descriptor.iManufacturer; + iProduct = dev->descriptor.iProduct; + iSerialNumber = dev->descriptor.iSerialNumber; +#endif /* WITH_LIBUSB_1_0 */ + + if (iManufacturer) { + char buf[SMALLBUF]; + ret = usb_get_string_simple(handle, iManufacturer, + (usb_ctrl_charbuf)buf, sizeof(buf)); + if (ret > 0) { + device->Vendor = strdup(buf); + if (device->Vendor == NULL) { +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + } + } + + if (iProduct) { + char buf[SMALLBUF]; + ret = usb_get_string_simple(handle, iProduct, + (usb_ctrl_charbuf)buf, sizeof(buf)); + if (ret > 0) { + device->Product = strdup(buf); + if (device->Product == NULL) { +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + } + } + + if (iSerialNumber) { + char buf[SMALLBUF]; + ret = usb_get_string_simple(handle, iSerialNumber, + (usb_ctrl_charbuf)buf, sizeof(buf)); + if (ret > 0) { + device->Serial = strdup(buf); + if (device->Serial == NULL) { +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + } + } + + upsdebugx(4, "- VendorID : %04x", device->VendorID); + upsdebugx(4, "- ProductID : %04x", device->ProductID); + upsdebugx(4, "- Manufacturer : %s", device->Vendor ? device->Vendor : "unknown"); + upsdebugx(4, "- Product : %s", device->Product ? device->Product : "unknown"); + upsdebugx(4, "- Serial Number: %s", device->Serial ? device->Serial : "unknown"); + upsdebugx(4, "- Bus : %s", device->Bus ? device->Bus : "unknown"); + + for (m = matcher; m; m = m->next) { + + switch (m->match_function(device, m->privdata)) + { + case 0: + upsdebugx(4, "Device does not match - skipping"); + goto next_device; + case -1: +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ + fatal_with_errno(EXIT_FAILURE, "matcher"); +#ifndef HAVE___ATTRIBUTE__NORETURN +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunreachable-code" +# endif + goto next_device; +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic pop +# endif +#endif + case -2: + upsdebugx(4, "matcher: unspecified error"); + goto next_device; + } + } +#ifdef HAVE_LIBUSB_SET_AUTO_DETACH_KERNEL_DRIVER + /* First, try the auto-detach kernel driver method + * This function is not available on FreeBSD 10.1-10.3 */ + if ((ret = libusb_set_auto_detach_kernel_driver (udev, 1)) < 0) + upsdebugx(2, "failed to auto detach kernel driver from USB device: %s", + nut_usb_strerror((enum libusb_error)ret)); + else + upsdebugx(2, "auto detached kernel driver from USB device"); +#endif /* HAVE_LIBUSB_SET_AUTO_DETACH_KERNEL_DRIVER */ + + for (i = 0; i < 3; i++) { + + ret = callback(handle, device); + if (ret >= 0) { + upsdebugx(3, "USB device [%04x:%04x] opened", device->VendorID, device->ProductID); +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ + return ret; + } + +#if WITH_LIBUSB_0_1 && (defined HAVE_USB_DETACH_KERNEL_DRIVER_NP) + /* this method requires at least libusb 0.1.8: + * it forces device claiming by unbinding + * attached driver... From libhid */ + if ((ret = usb_detach_kernel_driver_np(handle, 0)) < 0) { + upsdebugx(1, + "failed to detach kernel driver from USB device: %s", + nut_usb_strerror(ret)); + } else { + upsdebugx(4, "detached kernel driver from USB device..."); + } +#else +# ifdef HAVE_LIBUSB_DETACH_KERNEL_DRIVER + if ((ret = libusb_detach_kernel_driver(udev, 0)) < 0) { + upsdebugx(4, + "failed to detach kernel driver from USB device: %s", + nut_usb_strerror(ret)); + } else { + upsdebugx(4, "detached kernel driver from USB device..."); + } +# else +# ifdef HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP + if ((ret = libusb_detach_kernel_driver_np(udev, 0)) < 0) { + upsdebugx(4, + "failed to detach kernel driver from USB device: %s", + nut_usb_strerror(ret)); + } else { + upsdebugx(4, "detached kernel driver from USB device..."); + } +# endif /* HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP */ +# endif /* HAVE_LIBUSB_DETACH_KERNEL_DRIVER */ +#endif /* HAVE_USB_DETACH_KERNEL_DRIVER_NP or HAVE_LIBUSB_DETACH_KERNEL_DRIVER or HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP */ + } + +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ + fatalx(EXIT_FAILURE, + "USB device [%04x:%04x] matches, but driver callback failed: %s", + device->VendorID, device->ProductID, + nut_usb_strerror(ret)); + + next_device: + usb_close(handle); +#if (!WITH_LIBUSB_1_0) /* => WITH_LIBUSB_0_1 */ + } +#endif /* WITH_LIBUSB_1_0 */ + } + + *handlep = NULL; +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ + upsdebugx(3, "No matching USB device found"); + + return -1; +} + +/* + * Initialise the UPS + */ +void upsdrv_initups(void) +{ + int i; + + upsdebugx(1, "Searching for USB device..."); + + for (i = 0; usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback) < 0; i++) { + + if ((i < 3) && (sleep(5) == 0)) { + usb_comm_fail("Can't open USB device, retrying ..."); + continue; + } + + fatalx(EXIT_FAILURE, + "Unable to find ATCL FOR UPS\n\n" + + "Things to try:\n" + " - Connect UPS device to USB bus\n" + " - Run this driver as another user (upsdrvctl -u or 'user=...' in ups.conf).\n" + " See upsdrvctl(8) and ups.conf(5).\n\n" + + "Fatal error: unusable configuration"); + } + +} + +void upsdrv_cleanup(void) +{ + usb_device_close(udev); + + free(usbdevice.Vendor); + free(usbdevice.Product); + free(usbdevice.Serial); + free(usbdevice.Bus); +} + +void upsdrv_initinfo(void) +{ + dstate_setinfo("ups.mfr", "%s", usbdevice.Vendor ? usbdevice.Vendor : "unknown"); + dstate_setinfo("ups.model", "%s", usbdevice.Product ? usbdevice.Product : "unknown"); + if(usbdevice.Serial && usbdevice.Product && strcmp(usbdevice.Serial, usbdevice.Product)) { + /* Only set "ups.serial" if it isn't the same as "ups.model": */ + dstate_setinfo("ups.serial", "%s", usbdevice.Serial); + } + + dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID); + dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID); +} + +void upsdrv_updateinfo(void) +{ + char reply[STATUS_PACKETSIZE]; + int ret; + + if (!udev) { + ret = usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback); + + if (ret < 0) { + return; + } + } + + ret = query_ups(reply); + + if (ret != STATUS_PACKETSIZE) { + usb_comm_fail("Query to UPS failed"); + dstate_datastale(); + + usb_device_close(udev); + udev = NULL; + + return; + } + + usb_comm_good(); + dstate_dataok(); + + status_init(); + + switch(reply[0]) { + case 3: + upsdebugx(2, "reply[0] = 0x%02x -> OL", reply[0]); + status_set("OL"); + break; + case 2: + upsdebugx(2, "reply[0] = 0x%02x -> LB", reply[0]); + status_set("LB"); + goto fallthrough_LB_means_OB; + /* Note: the comment below existed for years, so wondering + * if this device CAN set independently LB and OB? */ + /* fall through */ + case 1: + fallthrough_LB_means_OB: + upsdebugx(2, "reply[0] = 0x%02x -> OB", reply[0]); + status_set("OB"); + break; + default: + upslogx(LOG_ERR, "Unknown status: 0x%02x", reply[0]); + } + if(strnlen(reply + 1, 7) != 0) { + upslogx(LOG_NOTICE, "Status bytes 1-7 are not all zero"); + } + + status_commit(); +} + +/* If the UPS is on battery, it should shut down about 30 seconds after + * receiving this packet. + */ +void upsdrv_shutdown(void) +{ + /* Not "const" because this mismatches arg type of usb_interrupt_write() */ + char shutdown_packet[SHUTDOWN_PACKETSIZE] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + int ret; + + upslogx(LOG_DEBUG, + "%s: attempting to call usb_interrupt_write(01 00 00 00 00 00 00 00)", + __func__); + + ret = usb_interrupt_write(udev, + SHUTDOWN_ENDPOINT, (usb_ctrl_charbuf)shutdown_packet, + SHUTDOWN_PACKETSIZE, ATCL_USB_TIMEOUT); + + if (ret <= 0) { + upslogx(LOG_NOTICE, + "%s: first usb_interrupt_write() failed: %s", + __func__, + ret ? nut_usb_strerror(ret) : "timeout"); + } + + /* Totally guessing from the .pcap file here. TODO: configurable delay? */ + usleep(170*1000); + + ret = usb_interrupt_write(udev, + SHUTDOWN_ENDPOINT, (usb_ctrl_charbuf)shutdown_packet, + SHUTDOWN_PACKETSIZE, ATCL_USB_TIMEOUT); + + if (ret <= 0) { + upslogx(LOG_ERR, + "%s: second usb_interrupt_write() failed: %s", + __func__, + ret ? nut_usb_strerror(ret) : "timeout"); + } + +} + +void upsdrv_help(void) +{ +} + +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "vendor", "USB vendor string (or NULL if none)"); +} diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c new file mode 100644 index 0000000..337b42a --- /dev/null +++ b/drivers/nutdrv_qx.c @@ -0,0 +1,4105 @@ +/* nutdrv_qx.c - Driver for USB and serial UPS units with Q* protocols + * + * Copyright (C) + * 2013 Daniele Pezzini + * 2016 Eaton + * Based on: + * usbhid-ups.c - Copyright (C) + * 2003-2012 Arnaud Quette + * 2005 John Stamp + * 2005-2006 Peter Selinger + * 2007-2009 Arjen de Korte + * blazer.c - Copyright (C) + * 2008-2009 Arjen de Korte + * 2012 Arnaud Quette + * blazer_ser.c - Copyright (C) + * 2008 Arjen de Korte + * blazer_usb.c - Copyright (C) + * 2003-2009 Arjen de Korte + * 2011-2012 Arnaud Quette + * Masterguard additions + * 2020-2021 Edgar Fuß, Mathematisches Institut der Universität Bonn + * Armac (Richcomm-variant) additions + * 2021 Tomasz Fortuna + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#define DRIVER_VERSION "0.32" + +#include "config.h" +#include "main.h" +#include "attribute.h" +#include "nut_float.h" +#include "nut_stdint.h" + +/* note: QX_USB/QX_SERIAL set through Makefile */ +#ifdef QX_USB + #include "nut_libusb.h" /* also includes "usb-common.h" */ + + #ifdef QX_SERIAL + #define DRIVER_NAME "Generic Q* USB/Serial driver" + #else + #define DRIVER_NAME "Generic Q* USB driver" + #endif /* QX_SERIAL */ +#else + #define DRIVER_NAME "Generic Q* Serial driver" +#endif /* QX_USB */ + +#ifdef QX_SERIAL + #include "serial.h" + #define SER_WAIT_SEC 1 /* 3 seconds for Best UPS */ +#endif /* QX_SERIAL */ + +#include "nutdrv_qx.h" + +/* == Subdrivers == */ +/* Include all known subdrivers */ +#include "nutdrv_qx_bestups.h" +#include "nutdrv_qx_hunnox.h" +#include "nutdrv_qx_mecer.h" +#include "nutdrv_qx_megatec.h" +#include "nutdrv_qx_megatec-old.h" +#include "nutdrv_qx_mustek.h" +#include "nutdrv_qx_q1.h" +#include "nutdrv_qx_voltronic.h" +#include "nutdrv_qx_voltronic-qs.h" +#include "nutdrv_qx_voltronic-qs-hex.h" +#include "nutdrv_qx_zinto.h" +#include "nutdrv_qx_masterguard.h" +#include "nutdrv_qx_ablerex.h" + +/* Reference list of available non-USB subdrivers */ +static subdriver_t *subdriver_list[] = { + &voltronic_subdriver, + &voltronic_qs_subdriver, + &voltronic_qs_hex_subdriver, + &mustek_subdriver, + &megatec_old_subdriver, + &bestups_subdriver, + &mecer_subdriver, + &megatec_subdriver, + &zinto_subdriver, + &masterguard_subdriver, + &hunnox_subdriver, + &ablerex_subdriver, + /* Fallback Q1 subdriver */ + &q1_subdriver, + NULL +}; + + +/* == Driver description structure == */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Daniele Pezzini " \ + "Arnaud Quette " \ + "John Stamp " \ + "Peter Selinger " \ + "Arjen de Korte " \ + "Edgar Fuß ", + DRV_BETA, +#ifdef QX_USB + { &comm_upsdrv_info, NULL } +#else + { NULL } +#endif /* QX_USB */ +}; + +/* == Data walk modes == */ +typedef enum { + QX_WALKMODE_INIT = 0, + QX_WALKMODE_QUICK_UPDATE, + QX_WALKMODE_FULL_UPDATE +} walkmode_t; + + +/* == Global vars == */ +/* Pointer to the active subdriver object (changed in subdriver_matcher() function) */ +static subdriver_t *subdriver = NULL; + +static long pollfreq = DEFAULT_POLLFREQ; +static unsigned int ups_status = 0; +static bool_t data_has_changed = FALSE; /* for SEMI_STATIC data polling */ + +static time_t lastpoll; /* Timestamp the last polling */ + +#if defined(QX_USB) && !defined(TESTING) +static int hunnox_step = 0; +#endif /* QX_USB && !TESTING */ + +#if defined(QX_USB) && defined(QX_SERIAL) +static int is_usb = 0; /* Whether the device is connected through USB (1) or serial (0) */ +#endif /* QX_USB && QX_SERIAL */ + +static struct { + char command[SMALLBUF]; /* Command sent to the UPS to get answer/to execute an instant command */ + char answer[SMALLBUF]; /* Answer from the UPS, filled at runtime */ +} previous_item = { "", "" }; /* Hold the values of the item processed just before the actual one */ + + +/* == Support functions == */ +static int subdriver_matcher(void); +static ssize_t qx_command(const char *cmd, char *buf, size_t buflen); +static int qx_process_answer(item_t *item, const size_t len); /* returns just 0 or -1 */ +static bool_t qx_ups_walk(walkmode_t mode); +static void ups_status_set(void); +static void ups_alarm_set(void); +static void qx_set_var(item_t *item); + + +/* == Struct & data for status processing == */ +typedef struct { + const char *status_str; /* UPS status string */ + const unsigned int status_mask; /* UPS status mask */ +} status_lkp_t; + +static status_lkp_t status_info[] = { + /* Map status strings to bit masks */ + { "OL", STATUS(OL) }, + { "LB", STATUS(LB) }, + { "RB", STATUS(RB) }, + { "CHRG", STATUS(CHRG) }, + { "DISCHRG", STATUS(DISCHRG) }, + { "BYPASS", STATUS(BYPASS) }, + { "CAL", STATUS(CAL) }, + { "OFF", STATUS(OFF) }, + { "OVER", STATUS(OVER) }, + { "TRIM", STATUS(TRIM) }, + { "BOOST", STATUS(BOOST) }, + { "FSD", STATUS(FSD) }, + { NULL, 0 }, +}; + + +/* == battery.{charge,runtime} guesstimation == */ +/* Support functions */ +static int qx_battery(void); +static int qx_load(void); +static void qx_initbattery(void); + +/* Battery data */ +static struct { + double packs; /* Battery voltage multiplier */ + struct { + double act; /* Actual runtime on battery */ + double nom; /* Nominal runtime on battery (full load) */ + double est; /* Estimated runtime remaining (full load) */ + double exp; /* Load exponent */ + } runt; + struct { + double act; /* Actual battery voltage */ + double high; /* Battery float voltage */ + double nom; /* Nominal battery voltage */ + double low; /* Battery low voltage */ + } volt; + struct { + double act; /* Actual battery charge */ + long time; /* Recharge time from empty to full */ + } chrg; +} batt = { 1, { -1, -1, 0, 0 }, { -1, -1, -1, -1 }, { -1, 43200 } }; + +/* Load data */ +static struct { + double act; /* Actual load (reported by the UPS) */ + double low; /* Idle load */ + double eff; /* Effective load */ +} load = { 0, 0.1, 1 }; + +static time_t battery_lastpoll = 0; + +/* Fill batt.volt.act and guesstimate the battery charge + * if it isn't already available. */ +static int qx_battery(void) +{ + const char *val = dstate_getinfo("battery.voltage"); + + if (!val) { + upsdebugx(2, "%s: unable to get battery.voltage", __func__); + return -1; + } + + batt.volt.act = batt.packs * strtod(val, NULL); + + if (d_equal(batt.chrg.act, -1) && batt.volt.low > 0 && batt.volt.high > batt.volt.low) { + + batt.chrg.act = 100 * (batt.volt.act - batt.volt.low) / (batt.volt.high - batt.volt.low); + + if (batt.chrg.act < 0) { + batt.chrg.act = 0; + } + + if (batt.chrg.act > 100) { + batt.chrg.act = 100; + } + + dstate_setinfo("battery.charge", "%.0f", batt.chrg.act); + + } + + return 0; +} + +/* Load for battery.{charge,runtime} from runtimecal */ +static int qx_load(void) +{ + const char *val = dstate_getinfo("ups.load"); + + if (!val) { + upsdebugx(2, "%s: unable to get ups.load", __func__); + return -1; + } + + load.act = strtod(val, NULL); + + load.eff = pow(load.act / 100, batt.runt.exp); + + if (load.eff < load.low) { + load.eff = load.low; + } + + return 0; +} + +/* Guesstimation: init */ +static void qx_initbattery(void) +{ + if (!dstate_getinfo("battery.charge") || !dstate_getinfo("battery.runtime")) { + + const char *val; + + val = dstate_getinfo("battery.voltage.high"); + if (val) { + batt.volt.high = strtod(val, NULL); + } + + val = dstate_getinfo("battery.voltage.low"); + if (val) { + batt.volt.low = strtod(val, NULL); + } + + val = dstate_getinfo("battery.voltage.nominal"); + if (val) { + batt.volt.nom = strtod(val, NULL); + } + + /* If no values are available for both battery.voltage.{low,high} + * either from the UPS or provided by the user in ups.conf, + * try to guesstimate them, but announce it! */ + if ( (!d_equal(batt.volt.nom, -1)) && (d_equal(batt.volt.low, -1) || d_equal(batt.volt.high, -1))) { + + upslogx(LOG_INFO, "No values for battery high/low voltages"); + + /* Basic formula, which should cover most cases */ + batt.volt.low = 104 * batt.volt.nom / 120; + batt.volt.high = 130 * batt.volt.nom / 120; + + /* Publish these data too */ + dstate_setinfo("battery.voltage.low", "%.2f", batt.volt.low); + dstate_setinfo("battery.voltage.high", "%.2f", batt.volt.high); + + upslogx(LOG_INFO, "Using 'guesstimation' (low: %f, high: %f)!", + batt.volt.low, batt.volt.high); + + } + + val = dstate_getinfo("battery.packs"); + if (val && (strspn(val, "0123456789 .") == strlen(val))) { + batt.packs = strtod(val, NULL); + } else { + + /* qx_battery -> batt.volt.act */ + if (!qx_battery() && (!d_equal(batt.volt.nom, -1))) { + + const double packs[] = { 120, 100, 80, 60, 48, 36, 30, 24, 18, 12, 8, 6, 4, 3, 2, 1, 0.5, -1 }; + int i; + + /* The battery voltage will quickly return to + * at least the nominal value after discharging them. + * For overlapping battery.voltage.low/high ranges + * therefore choose the one with the highest multiplier. */ + for (i = 0; packs[i] > 0; i++) { + + if (packs[i] * batt.volt.act > 1.2 * batt.volt.nom) { + continue; + } + + if (packs[i] * batt.volt.act < 0.8 * batt.volt.nom) { + upslogx(LOG_INFO, + "Can't autodetect number of battery packs [%.0f/%.2f]", + batt.volt.nom, batt.volt.act); + break; + } + + batt.packs = packs[i]; + break; + + } + + } else { + upslogx(LOG_INFO, + "Can't autodetect number of battery packs [%.0f/%.2f]", + batt.volt.nom, batt.volt.act); + } + + } + + /* Update batt.{chrg,volt}.act */ + qx_battery(); + + val = getval("runtimecal"); + if (val) { + + double rh, lh, rl, ll; + + time(&battery_lastpoll); + + if (sscanf(val, "%lf,%lf,%lf,%lf", &rh, &lh, &rl, &ll) < 4) { + fatalx(EXIT_FAILURE, "Insufficient parameters for runtimecal"); + } + + if ((rl < rh) || (rh <= 0)) { + fatalx(EXIT_FAILURE, "Parameter out of range (runtime)"); + } + + if ((lh > 100) || (ll > lh) || (ll <= 0)) { + fatalx(EXIT_FAILURE, "Parameter out of range (load)"); + } + + batt.runt.exp = log(rl / rh) / log(lh / ll); + upsdebugx(2, "%s: battery runtime exponent: %.3f", + __func__, batt.runt.exp); + + batt.runt.nom = rh * pow(lh / 100, batt.runt.exp); + upsdebugx(2, "%s: battery runtime nominal: %.1f", + __func__, batt.runt.nom); + + } else { + + upslogx(LOG_INFO, "Battery runtime will not be calculated " + "(runtimecal not set)"); + return; + + } + + val = dstate_getinfo("battery.charge"); + if (!val && (!d_equal(batt.volt.nom, -1))) { + batt.volt.low = batt.volt.nom; + batt.volt.high = 1.15 * batt.volt.nom; + + if (qx_battery()) + fatalx(EXIT_FAILURE, "Initial battery charge undetermined"); + + val = dstate_getinfo("battery.charge"); + } + + if (val) { + batt.runt.est = batt.runt.nom * strtod(val, NULL) / 100; + upsdebugx(2, "%s: battery runtime estimate: %.1f", + __func__, batt.runt.est); + } else { + fatalx(EXIT_FAILURE, "Initial battery charge undetermined"); + } + + val = getval("chargetime"); + if (val) { + batt.chrg.time = strtol(val, NULL, 10); + + if (batt.chrg.time <= 0) { + fatalx(EXIT_FAILURE, "Charge time out of range [1..s]"); + } + + upsdebugx(2, "%s: battery charge time: %ld", + __func__, batt.chrg.time); + } else { + upslogx(LOG_INFO, + "No charge time specified, " + "using built in default [%ld seconds]", + batt.chrg.time); + } + + val = getval("idleload"); + if (val) { + load.low = strtod(val, NULL) / 100; + + if ((load.low <= 0) || (load.low > 1)) { + fatalx(EXIT_FAILURE, "Idle load out of range [0..100]"); + } + + upsdebugx(2, + "%s: minimum load used (idle): %.3f", + __func__, load.low); + } else { + upslogx(LOG_INFO, + "No idle load specified, using built in default [%.1f %%]", + 100 * load.low); + } + } +} + + +/* == USB communication subdrivers == */ +#if defined(QX_USB) && !defined(TESTING) +static usb_communication_subdriver_t *usb = &usb_subdriver; +static usb_dev_handle *udev = NULL; +static USBDevice_t usbdevice; +static USBDeviceMatcher_t *reopen_matcher = NULL; +static USBDeviceMatcher_t *regex_matcher = NULL; +static int langid_fix = -1; + +static int (*subdriver_command)(const char *cmd, char *buf, size_t buflen) = NULL; + +/* Cypress communication subdriver */ +static int cypress_command(const char *cmd, char *buf, size_t buflen) +{ + char tmp[SMALLBUF]; + int ret = 0; + size_t i; + + if (buflen > INT_MAX) { + upsdebugx(3, "%s: requested to read too much (%zu), " + "reducing buflen to (INT_MAX-1)", + __func__, buflen); + buflen = (INT_MAX - 1); + } + + /* Send command */ + memset(tmp, 0, sizeof(tmp)); + snprintf(tmp, sizeof(tmp), "%s", cmd); + + for (i = 0; i < strlen(tmp); i += (size_t)ret) { + + /* Write data in 8-byte chunks */ + /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); */ + ret = usb_control_msg(udev, + USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, + 0x09, 0x200, 0, + (usb_ctrl_charbuf)&tmp[i], 8, 5000); + + if (ret <= 0) { + upsdebugx(3, "send: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", + ret); + return ret; + } + + } + + upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp); + + /* Read reply */ + memset(buf, 0, buflen); + + for (i = 0; (i <= buflen-8) && (memchr(buf, '\r', buflen) == NULL); i += (size_t)ret) { + + /* Read data in 8-byte chunks */ + /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */ + ret = usb_interrupt_read(udev, + 0x81, + (usb_ctrl_charbuf)&buf[i], 8, 1000); + + /* Any errors here mean that we are unable to read a reply + * (which will happen after successfully writing a command + * to the UPS) */ + if (ret <= 0) { + upsdebugx(3, "read: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", + ret); + return ret; + } + + snprintf(tmp, sizeof(tmp), "read [% 3d]", (int)i); + upsdebug_hex(5, tmp, &buf[i], (size_t)ret); + + } + + upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); + + if (i > INT_MAX) { + upsdebugx(3, "%s: read too much (%zu)", __func__, i); + return -1; + } + return (int)i; +} + +/* SGS communication subdriver */ +static int sgs_command(const char *cmd, char *buf, size_t buflen) +{ + char tmp[SMALLBUF]; + int ret = 0; + size_t cmdlen, i; + + if (buflen > INT_MAX) { + upsdebugx(3, "%s: requested to read too much (%zu), " + "reducing buflen to (INT_MAX-1)", + __func__, buflen); + buflen = (INT_MAX - 1); + } + + /* Send command */ + cmdlen = strlen(cmd); + + for (i = 0; i < cmdlen; i += (size_t)ret) { + + memset(tmp, 0, sizeof(tmp)); + + /* i and cmdlen are size_t nominally, but diff is not large */ + ret = (int)((cmdlen - i) < 7 ? (cmdlen - i) : 7); + + /* ret is between 0 and 7 */ + tmp[0] = (char)ret; + memcpy(&tmp[1], &cmd[i], (unsigned char)ret); + + /* Write data in 8-byte chunks */ + ret = usb_control_msg(udev, + USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0x09, 0x200, 0, + (usb_ctrl_charbuf)tmp, 8, 5000); + + if (ret <= 0) { + upsdebugx(3, "send: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", + ret); + return ret; + } + + ret--; + + } + + upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); + + /* Read reply */ + memset(buf, 0, buflen); + + for (i = 0; i <= buflen - 8; i += (size_t)ret) { + + memset(tmp, 0, sizeof(tmp)); + + /* Read data in 8-byte chunks */ + ret = usb_interrupt_read(udev, + 0x81, + (usb_ctrl_charbuf)tmp, 8, 1000); + + /* No error!!! */ + /* if (ret == -110) */ + if (ret == ERROR_TIMEOUT) + break; + + /* Any errors here mean that we are unable to read a reply + * (which will happen after successfully writing a command + * to the UPS) */ + if (ret <= 0) { + upsdebugx(3, "read: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", + ret); + return ret; + } + + /* Every call to read returns 8 bytes + * -> actually returned bytes: */ + ret = tmp[0] <= 7 ? tmp[0] : 7; + + if (ret > 0) + memcpy(&buf[i], &tmp[1], (unsigned char)ret); + + snprintf(tmp, sizeof(tmp), "read [% 3d]", (int)i); + upsdebug_hex(5, tmp, &buf[i], (size_t)ret); + + } + + /* If the reply lacks the expected terminating CR, add it (if there's enough space) */ + if (i && memchr(buf, '\r', i) == NULL) { + upsdebugx(4, "%s: the reply lacks the expected terminating CR.", __func__); + if (i < buflen - 1) { + upsdebugx(4, "%s: adding missing terminating CR.", __func__); + buf[i++] = '\r'; + buf[i] = 0; + } + } + + upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); + + if (i > INT_MAX) { + upsdebugx(3, "%s: read too much (%zu)", __func__, i); + return -1; + } + return (int)i; +} + +/* Phoenix communication subdriver */ +static int phoenix_command(const char *cmd, char *buf, size_t buflen) +{ + char tmp[SMALLBUF]; + int ret; + size_t i; + + if (buflen > INT_MAX) { + upsdebugx(3, "%s: requested to read too much (%zu), " + "reducing buflen to (INT_MAX-1)", + __func__, buflen); + buflen = (INT_MAX - 1); + } + + for (i = 0; i < 8; i++) { + + /* Read data in 8-byte chunks */ + /* ret = usb->get_interrupt(udev, (unsigned char *)tmp, 8, 1000); */ + ret = usb_interrupt_read(udev, + 0x81, + (usb_ctrl_charbuf)tmp, 8, 1000); + + /* This USB to serial implementation is crappy. + * In order to read correct replies we need to flush the + * output buffers of the converter until we get no more + * data (e.g. it times out). */ + switch (ret) + { + case ERROR_PIPE: /* Broken pipe */ + usb_clear_halt(udev, 0x81); + break; + + case ERROR_TIMEOUT: /* Connection timed out */ + break; + } + + if (ret < 0) { + upsdebugx(3, "flush: %s (%d)", + nut_usb_strerror(ret), ret); + break; + } + + upsdebug_hex(4, "dump", tmp, (size_t)ret); + + } + + /* Send command */ + memset(tmp, 0, sizeof(tmp)); + snprintf(tmp, sizeof(tmp), "%s", cmd); + + for (i = 0; i < strlen(tmp); i += (size_t)ret) { + + /* Write data in 8-byte chunks */ + /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); */ + ret = usb_control_msg(udev, + USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, + 0x09, 0x200, 0, (usb_ctrl_charbuf)&tmp[i], 8, 1000); + + if (ret <= 0) { + upsdebugx(3, "send: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", ret); + return ret; + } + + } + + upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp); + + /* Read reply */ + memset(buf, 0, buflen); + + for (i = 0; (i <= buflen-8) && (memchr(buf, '\r', buflen) == NULL); i += (size_t)ret) { + + /* Read data in 8-byte chunks */ + /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */ + ret = usb_interrupt_read(udev, + 0x81, + (usb_ctrl_charbuf)&buf[i], 8, 1000); + + /* Any errors here mean that we are unable to read a reply + * (which will happen after successfully writing a command + * to the UPS) */ + if (ret <= 0) { + upsdebugx(3, "read: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", ret); + return ret; + } + + snprintf(tmp, sizeof(tmp), "read [% 3d]", (int)i); + upsdebug_hex(5, tmp, &buf[i], (size_t)ret); + + } + + upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); + + if (i > INT_MAX) { + upsdebugx(3, "%s: read too much (%zu)", __func__, i); + return -1; + } + return (int)i; +} + +/* Ippon communication subdriver */ +static int ippon_command(const char *cmd, char *buf, size_t buflen) +{ + char tmp[64]; + int ret; + size_t i, len; + + if (buflen > INT_MAX) { + upsdebugx(3, "%s: requested to read too much (%zu), " + "reducing buflen to (INT_MAX-1)", + __func__, buflen); + buflen = (INT_MAX - 1); + } + + /* Send command */ + snprintf(tmp, sizeof(tmp), "%s", cmd); + + for (i = 0; i < strlen(tmp); i += (size_t)ret) { + + /* Write data in 8-byte chunks */ + ret = usb_control_msg(udev, + USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, + 0x09, 0x2, 0, (usb_ctrl_charbuf)&tmp[i], 8, 1000); + + if (ret <= 0) { + upsdebugx(3, "send: %s (%d)", + (ret != ERROR_TIMEOUT) ? nut_usb_strerror(ret) : "Connection timed out", + ret); + return ret; + } + + } + + upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp); + + /* Read all 64 bytes of the reply in one large chunk */ + ret = usb_interrupt_read(udev, + 0x81, + (usb_ctrl_charbuf)tmp, sizeof(tmp), 1000); + + /* Any errors here mean that we are unable to read a reply + * (which will happen after successfully writing a command + * to the UPS) */ + if (ret <= 0) { + upsdebugx(3, "read: %s (%d)", + (ret != ERROR_TIMEOUT) ? nut_usb_strerror(ret) : "Connection timed out", + ret); + return ret; + } + + /* As Ippon will always return 64 bytes in response, + * we have to calculate and return length of actual + * response data here. + * Empty response will look like 0x00 0x0D, otherwise + * it will be data string terminated by 0x0D. */ + + for (i = 0, len = 0; i < (size_t)ret; i++) { + + if (tmp[i] != '\r') + continue; + + len = ++i; + break; + + } + + /* Just in case there wasn't any '\r', fallback to string length, if any */ + if (!len) + len = strlen(tmp); + + upsdebug_hex(5, "read", tmp, (size_t)len); + upsdebugx(3, "read: %.*s", (int)strcspn(tmp, "\r"), tmp); + + len = len < buflen ? len : buflen - 1; + + memset(buf, 0, buflen); + memcpy(buf, tmp, len); + + /* If the reply lacks the expected terminating CR, add it (if there's enough space) */ + if (len && memchr(buf, '\r', len) == NULL) { + upsdebugx(4, "%s: the reply lacks the expected terminating CR.", __func__); + if (len < buflen - 1) { + upsdebugx(4, "%s: adding missing terminating CR.", __func__); + buf[len++] = '\r'; + buf[len] = 0; + } + } + + if (len > INT_MAX) { + upsdebugx(3, "%s: read too much (%zu)", __func__, len); + return -1; + } + return (int)len; +} + +static int hunnox_protocol(int asking_for) +{ + char buf[1030]; + + int langid_fix_local = 0x0409; + + if (langid_fix != -1) { + langid_fix_local = langid_fix; + } + + switch (hunnox_step) { + case 0: + upsdebugx(3, "asking for: %02X", 0x00); + usb_get_string(udev, 0x00, + langid_fix_local, (usb_ctrl_charbuf)buf, 1026); + usb_get_string(udev, 0x00, + langid_fix_local, (usb_ctrl_charbuf)buf, 1026); + usb_get_string(udev, 0x01, + langid_fix_local, (usb_ctrl_charbuf)buf, 1026); + usleep(10000); + break; + case 1: + if (asking_for != 0x0d) { + upsdebugx(3, "asking for: %02X", 0x0d); + usb_get_string(udev, 0x0d, + langid_fix_local, (usb_ctrl_charbuf)buf, 102); + } + break; + case 2: + if (asking_for != 0x03) { + upsdebugx(3, "asking for: %02X", 0x03); + usb_get_string(udev, 0x03, + langid_fix_local, (usb_ctrl_charbuf)buf, 102); + } + break; + case 3: + if (asking_for != 0x0c) { + upsdebugx(3, "asking for: %02X", 0x0c); + usb_get_string(udev, 0x0c, + langid_fix_local, (usb_ctrl_charbuf)buf, 102); + } + break; + default: + hunnox_step = 0; + } + hunnox_step++; + if (hunnox_step > 3) { + hunnox_step = 1; + } + + return 0; +} + +/* Krauler communication subdriver */ +static int krauler_command(const char *cmd, char *buf, size_t buflen) +{ + /* Still not implemented: + * 0x6 T (don't know how to pass the parameter) + * 0x68 and 0x69 both cause shutdown after an undefined interval */ + const struct { + const char *str; /* Megatec command */ + const int index; /* Krauler string index for this command */ + const char prefix; /* Character to replace the first byte in reply */ + } command[] = { + { "Q1\r", 0x03, '(' }, + { "F\r", 0x0d, '#' }, + { "I\r", 0x0c, '#' }, + { "T\r", 0x04, '\r' }, + { "TL\r", 0x05, '\r' }, + { "Q\r", 0x07, '\r' }, + { "C\r", 0x0b, '\r' }, + { "CT\r", 0x0b, '\r' }, + { NULL, 0, '\0' } + }; + + int i; + + upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); + + if (buflen > INT_MAX) { + upsdebugx(3, "%s: requested to read too much (%zu), " + "reducing buflen to (INT_MAX-1)", + __func__, buflen); + buflen = (INT_MAX - 1); + } + + for (i = 0; command[i].str; i++) { + + int retry; + + if (strcmp(cmd, command[i].str)) { + continue; + } + + for (retry = 0; retry < 10; retry++) { + + int ret; + + if (langid_fix != -1) { + /* Apply langid_fix value */ + ret = usb_get_string(udev, + command[i].index, langid_fix, + (usb_ctrl_charbuf)buf, buflen); + } else { + ret = usb_get_string_simple(udev, + command[i].index, + (usb_ctrl_charbuf)buf, buflen); + } + + if (ret <= 0) { + upsdebugx(3, "read: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", ret); + return ret; + } + + /* This may serve in the future */ + upsdebugx(1, "received %d (%d)", ret, buf[0]); + + if (langid_fix != -1) { + /* Limit this check, at least for now */ + /* Invalid receive size - message corrupted */ + if (ret != buf[0]) { + upsdebugx(1, "size mismatch: %d / %d", ret, buf[0]); + continue; + } + + /* Simple unicode -> ASCII inplace conversion + * FIXME: this code is at least shared with mge-shut/libshut + * Create a common function? */ + unsigned int di, si, size = (unsigned int)buf[0]; + for (di = 0, si = 2; si < size; si += 2) { + + if (di >= (buflen - 1)) + break; + + if (buf[si + 1]) /* high byte */ + buf[di++] = '?'; + else + buf[di++] = buf[si]; + + } + + /* Note: effective range of di should be unsigned char */ + buf[di] = 0; + ret = (int)di; + } + + /* If the reply lacks the expected terminating CR, add it (if there's enough space) */ + if (ret && memchr(buf, '\r', ret) == NULL) { + upsdebugx(4, "%s: the reply lacks the expected terminating CR.", __func__); + if ((size_t)ret < buflen - 1) { + upsdebugx(4, "%s: adding missing terminating CR.", __func__); + buf[ret++] = '\r'; + buf[ret] = 0; + } + } + + /* "UPS No Ack" has a special meaning */ + if ( + strcspn(buf, "\r") == 10 && + !strncasecmp(buf, "UPS No Ack", 10) + ) { + upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); + continue; + } + + /* Replace the first byte of what we received with the correct one */ + buf[0] = command[i].prefix; + + upsdebug_hex(5, "read", buf, (size_t)ret); + upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); + + return ret; + + } + + return 0; + + } + + /* Echo the unknown command back */ + upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd); + return snprintf(buf, buflen, "%s", cmd); +} + +/* Fabula communication subdriver */ +static int fabula_command(const char *cmd, char *buf, size_t buflen) +{ + const struct { + const char *str; /* Megatec command */ + const int index; /* Fabula string index for this command */ + } commands[] = { + { "Q1\r", 0x03, }, /* Status */ + { "F\r", 0x0d, }, /* Ratings */ + { "I\r", 0x0c, }, /* Vendor infos */ + { "Q\r", 0x07, }, /* Beeper toggle */ + { "C\r", 0x0a, }, /* Cancel shutdown/Load on [0x(0..F)A]*/ + { NULL, 0 } + }; + int i, ret, index = 0; + + upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); + + if (buflen > INT_MAX) { + upsdebugx(3, "%s: requested to read too much (%zu), " + "reducing buflen to (INT_MAX-1)", + __func__, buflen); + buflen = (INT_MAX - 1); + } + + for (i = 0; commands[i].str; i++) { + + if (strcmp(cmd, commands[i].str)) + continue; + + index = commands[i].index; + break; + + } + + if (!index) { + + int val2 = -1; + double val1 = -1; + + /* Shutdowns */ + if ( + sscanf(cmd, "S%lfR%d\r", &val1, &val2) == 2 || + sscanf(cmd, "S%lf\r", &val1) == 1 + ) { + + double delay; + + /* 0x(1+)0 -> shutdown.stayoff (SnR0000) + * 0x(1+)8 -> shutdown.return (Sn[Rm], m != 0) + * [delay before restart is always 10 seconds] + * +0x10 (16dec) = next megatec delay + * (min .5 = hex 0x1*; max 10 = hex 0xF*) -> n < 1 ? -> n += .1; n >= 1 ? -> n += 1 */ + + /* delay: [.5..10] (-> seconds: [30..600]) */ + delay = val1 < .5 ? .5 : val1 > 10 ? 10 : val1; + + if (delay < 1) + index = 16 + round((delay - .5) * 10) * 16; + else + index = 96 + (delay - 1) * 16; + + /* shutdown.return (Sn[Rm], m != 0) */ + if (val2) + index += 8; + + /* Unknown commands */ + } else { + + /* Echo the unknown command back */ + upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd); + return snprintf(buf, buflen, "%s", cmd); + + } + + } + + upsdebugx(4, "command index: 0x%02x", index); + + /* Send command/Read reply */ + ret = usb_get_string_simple(udev, index, (usb_ctrl_charbuf)buf, buflen); + + if (ret <= 0) { + upsdebugx(3, "read: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", ret); + return ret; + } + + /* If the reply lacks the expected terminating CR, add it (if there's enough space) */ + if (memchr(buf, '\r', ret) == NULL) { + upsdebugx(4, "%s: the reply lacks the expected terminating CR.", __func__); + if ((size_t)ret < buflen - 1) { + upsdebugx(4, "%s: adding missing terminating CR.", __func__); + buf[ret++] = '\r'; + buf[ret] = 0; + } + } + + upsdebug_hex(5, "read", buf, (size_t)ret); + upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); + + /* The UPS always replies "UPS No Ack" when a supported command + * is issued (either if it fails or if it succeeds).. */ + if ( + strcspn(buf, "\r") == 10 && + !strncasecmp(buf, "UPS No Ack", 10) + ) { + /* ..because of that, always return 0 (with buf empty, + * as if it was a timeout): queries will see it as a failure, + * instant commands ('megatec' protocol) as a success */ + memset(buf, 0, buflen); + return 0; + } + + return ret; +} + +/* Hunnox communication subdriver, based on Fabula code above so repeats + * much of it currently. Possible future optimization is to refactor shared + * code into new routines to be called from both (or more) methods.*/ +static int hunnox_command(const char *cmd, char *buf, size_t buflen) +{ + /* The hunnox_patch was an argument in initial implementation of PR #638 + * which added "hunnox" support; keeping it fixed here helps to visibly + * track the modifications compared to original fabula_command() e.g. to + * facilitate refactoring commented above, in the future. + */ +/* char hunnox_patch = 1; */ + const struct { + const char *str; /* Megatec command */ + const int index; /* Fabula string index for this command */ + } commands[] = { + { "Q1\r", 0x03, }, /* Status */ + { "F\r", 0x0d, }, /* Ratings */ + { "I\r", 0x0c, }, /* Vendor infos */ + { "Q\r", 0x07, }, /* Beeper toggle */ + { "C\r", 0x0a, }, /* Cancel shutdown/Load on [0x(0..F)A]*/ + { NULL, 0 } + }; + int i, ret, index = 0; + + upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); + + if (buflen > INT_MAX) { + upsdebugx(3, "%s: requested to read too much (%zu), " + "reducing buflen to (INT_MAX-1)", + __func__, buflen); + buflen = (INT_MAX - 1); + } + + for (i = 0; commands[i].str; i++) { + + if (strcmp(cmd, commands[i].str)) + continue; + + index = commands[i].index; + break; + + } + + if (!index) { + + int val2 = -1; + double val1 = -1; + + /* Shutdowns */ + if ( + sscanf(cmd, "S%lfR%d\r", &val1, &val2) == 2 || + sscanf(cmd, "S%lf\r", &val1) == 1 + ) { + + double delay; + + /* 0x(1+)0 -> shutdown.stayoff (SnR0000) + * 0x(1+)8 -> shutdown.return (Sn[Rm], m != 0) + * [delay before restart is always 10 seconds] + * +0x10 (16dec) = next megatec delay + * (min .5 = hex 0x1*; max 10 = hex 0xF*) -> n < 1 ? -> n += .1; n >= 1 ? -> n += 1 */ + + /* delay: [.5..10] (-> seconds: [30..600]) */ + delay = val1 < .5 ? .5 : val1 > 10 ? 10 : val1; + + if (delay < 1) + index = 16 + round((delay - .5) * 10) * 16; + else + index = 96 + (delay - 1) * 16; + + /* shutdown.return (Sn[Rm], m != 0) */ + if (val2) + index += 8; + + /* Unknown commands */ + } else { + + /* Echo the unknown command back */ + upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd); + return snprintf(buf, buflen, "%s", cmd); + + } + + } + + upsdebugx(4, "command index: 0x%02x", index); + +/* if (hunnox_patch) { */ + /* Enable lock-step protocol for Hunnox */ + if (hunnox_protocol(index) != 0) { + return 0; + } + + /* Seems that if we inform a large buffer, the USB locks. + * This value was captured from the Windows "official" client. + * Note this should not be a problem programmatically: it just + * means that the caller reserved a longer buffer that we need + * in practice to write a response into. + */ + if (buflen > 102) { + buflen = 102; + } +/* } */ + + /* Send command/Read reply */ + if (langid_fix != -1) { + ret = usb_get_string(udev, + index, langid_fix, (usb_ctrl_charbuf)buf, buflen); + } else { + ret = usb_get_string_simple(udev, + index, (usb_ctrl_charbuf)buf, buflen); + } + + if (ret <= 0) { + upsdebugx(3, "read: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", + ret); + return ret; + } + +/* if (hunnox_patch) { */ + if (langid_fix != -1) { + /* Limit this check, at least for now */ + /* Invalid receive size - message corrupted */ + if (ret != buf[0]) { + upsdebugx(1, "size mismatch: %d / %d", ret, buf[0]); + return 0; + } + + /* Simple unicode -> ASCII inplace conversion + * FIXME: this code is at least shared with mge-shut/libshut + * Create a common function? */ + unsigned int di, si, size = (unsigned int)buf[0]; + for (di = 0, si = 2; si < size; si += 2) { + if (di >= (buflen - 1)) + break; + + if (buf[si + 1]) /* high byte */ + buf[di++] = '?'; + else + buf[di++] = buf[si]; + } + + /* Note: effective range of di should be unsigned char */ + buf[di] = 0; + ret = (int)di; + } +/* } */ + + upsdebug_hex(5, "read", buf, (size_t)ret); + upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); + + /* The UPS always replies "UPS No Ack" when a supported command + * is issued (either if it fails or if it succeeds).. */ + if ( + strcspn(buf, "\r") == 10 && + !strncasecmp(buf, "UPS No Ack", 10) + ) { + /* ..because of that, always return 0 (with buf empty, + * as if it was a timeout): queries will see it as a failure, + * instant commands ('megatec' protocol) as a success */ + memset(buf, 0, buflen); + return 0; + } + + return ret; +} + +/* Fuji communication subdriver */ +static int fuji_command(const char *cmd, char *buf, size_t buflen) +{ + unsigned char tmp[8]; + char command[SMALLBUF] = "", + read[SMALLBUF] = ""; + int ret, val2; + unsigned char answer_len; + double val1; + size_t i; + const struct { + const char *command; /* Megatec command */ + const unsigned char answer_len; /* Expected length of the answer + * to the ongoing query */ + } query[] = { + { "Q1", 47 }, + { "F", 22 }, + { "I", 39 }, + { NULL, 0 } + }; + + if (buflen > INT_MAX) { + upsdebugx(3, "%s: requested to read too much (%zu), " + "reducing buflen to (INT_MAX-1)", + __func__, buflen); + buflen = (INT_MAX - 1); + } + + /* + * Queries (b1..b8) sent (as a 8-bytes interrupt) to the UPS + * adopt the following scheme: + * + * b1: 0x80 + * b2: 0x06 + * b3: + * b4: 0x03 + * b5..bn: + * bn+1..b7: [] + * b8: + * + * Where: + * Length (in Hex) of the command (without the trailing CR) + 1 + * Command/query (without the trailing CR) + * [] 0x00 padding to the 7th byte + * Expected length (in Hex) of the answer to the ongoing + * query (0 when no reply is expected, i.e. commands) + * + * Replies to queries (commands are followed by action without + * any reply) are sent from the UPS (in 8-byte chunks) with + * 0x00 padding after the trailing CR to full 8 bytes. + * + */ + + /* Send command */ + + /* Remove the CR */ + snprintf(command, sizeof(command), "%.*s", (int)strcspn(cmd, "\r"), cmd); + + /* Length of the command that will be sent to the UPS can be + * at most: 8 - 5 (0x80, 0x06, , 0x03, ) = 3. + * As a consequence also 'SnRm' commands (shutdown.{return,stayoff} + * and load.off) are not supported. + * So, map all the 'SnRm' shutdown.returns (m != 0) as the + * corresponding 'Sn' commands, meanwhile ignoring ups.delay.start + * and making the UPS turn on the load as soon as power is back. */ + if (sscanf(cmd, "S%lfR%d\r", &val1, &val2) == 2 && val2) { + upsdebugx(4, "%s: trimming '%s' to '%.*s'", __func__, command, 3, command); + command[3] = 0; + } + /* Too long command */ + if (strlen(command) > 3) { + /* Be 'megatec-y': echo the unsupported command back */ + upsdebugx(3, "%s: unsupported command %s", __func__, command); + return snprintf(buf, buflen, "%s", cmd); + } + + /* Expected length of the answer to the ongoing query + * (0 when no reply is expected, i.e. commands) */ + answer_len = 0; + for (i = 0; query[i].command; i++) { + + if (strcmp(command, query[i].command)) + continue; + + answer_len = query[i].answer_len; + break; + + } + + memset(tmp, 0, sizeof(tmp)); + + /* 0x80 */ + tmp[0] = 0x80; + /* 0x06 */ + tmp[1] = 0x06; + /* ; per above under 3 */ + tmp[2] = (unsigned char)strlen(command) + 1; + /* 0x03 */ + tmp[3] = 0x03; + /* */ + memcpy(&tmp[4], command, strlen(command)); + /* */ + tmp[7] = answer_len; + + upsdebug_hex(4, "command", (char *)tmp, 8); + + /* Write data */ + ret = usb_interrupt_write(udev, + USB_ENDPOINT_OUT | 2, + (const usb_ctrl_charbuf)tmp, + 8, USB_TIMEOUT); + + if (ret <= 0) { + upsdebugx(3, "send: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", ret); + return ret; + } + + upsdebugx(3, "send: %s", command); + + /* Read reply */ + + memset(buf, 0, buflen); + + for (i = 0; (i <= buflen - 8) && (memchr(buf, '\r', buflen) == NULL); i += (size_t)ret) { + + /* Read data in 8-byte chunks */ + ret = usb_interrupt_read(udev, + USB_ENDPOINT_IN | 1, + (usb_ctrl_charbuf)&buf[i], 8, 1000); + + /* Any errors here mean that we are unable to read a reply + * (which will happen after successfully writing a command + * to the UPS) */ + if (ret <= 0) { + upsdebugx(3, "read: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", ret); + return ret; + } + + snprintf(read, sizeof(read), "read [%3d]", (int)i); + upsdebug_hex(5, read, &buf[i], (size_t)ret); + + } + + upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); + + /* As Fuji units return the reply in 8-byte chunks always padded to the 8th byte with 0x00, we need to calculate and return the length of the actual response here. */ + return (int)strlen(buf); +} + +/* Phoenixtec (Masterguard) communication subdriver */ +static int phoenixtec_command(const char *cmd, char *buf, size_t buflen) +{ + int ret; + char *p, *e = NULL; + char *l[] = { "T", "TL", "S", "C", "CT", "M", "N", "O", "SRC", "FCLR", "SS", "TUD", "SSN", NULL }; /* commands that don't return an answer */ + char **lp; + size_t cmdlen = strlen(cmd); + + if (cmdlen > INT_MAX) { + upsdebugx(3, "%s: requested command is too long (%zu)", + __func__, cmdlen); + return 0; + } + + if (buflen > INT_MAX) { + upsdebugx(3, "%s: requested to read too much (%zu), " + "reducing buflen to (INT_MAX-1)", + __func__, buflen); + buflen = (INT_MAX - 1); + } + + if ((ret = usb_control_msg(udev, + USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + 0x0d, 0, 0, (usb_ctrl_charbuf)cmd, (int)cmdlen, 1000)) <= 0 + ) { + upsdebugx(3, "send: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", + ret); + *buf = '\0'; + return ret; + } + + for (lp = l; *lp != NULL; lp++) { + const char *q; + int b; + + p = *lp; q = cmd; b = 1; + while (*p != '\0') { + if (*p++ != *q++) { + b = 0; + break; + } + } + if (b && *q >= 'A' && *q <= 'Z') b = 0; /* "M" not to match "MSO" */ + if (b) { + upsdebugx(4, "command %s returns no answer", *lp); + *buf = '\0'; + return 0; + } + } + + for (p = buf; p < buf + buflen; p += ret) { + /* buflen constrained to INT_MAX above, so we can cast: */ + if ((ret = usb_interrupt_read(udev, + USB_ENDPOINT_IN | 1, + (usb_ctrl_charbuf)p, (int)(buf + buflen - p), 1000)) <= 0 + ) { + upsdebugx(3, "read: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", + ret); + *buf = '\0'; + return ret; + } + if ((e = memchr(p, '\r', (size_t)ret)) != NULL) break; + } + if (e != NULL && ++e < buf + buflen) { + *e = '\0'; + /* buflen constrained to INT_MAX above, so we can cast: */ + return (int)(e - buf); + } else { + upsdebugx(3, "read: buflen %zu too small", buflen); + *buf = '\0'; + return 0; + } +} + +/* SNR communication subdriver */ +static int snr_command(const char *cmd, char *buf, size_t buflen) +{ + /*ATTENTION: This subdriver uses short buffer with length 102 byte*/ + const struct { + const char *str; /* Megatec command */ + const int index; /* String index for this command */ + const char prefix; /* Character to replace the first byte in reply */ + } command[] = { + { "Q1\r", 0x03, '(' }, + { "F\r", 0x0d, '#' }, + { "I\r", 0x0c, '#' }, + { NULL, 0, '\0' } + }; + + int i; + + upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); + + if (buflen > INT_MAX) { + upsdebugx(3, "%s: requested to read too much (%zu), " + "reducing buflen to (INT_MAX-1)", + __func__, buflen); + buflen = (INT_MAX - 1); + } + + if (buflen < 102) { + upsdebugx(4, "size of buf less than 102 byte!"); + return 0; + } + + /* Prepare SNR-UPS for communication. + * Without the interrupt UPS returns zeros for some time, + * and afterwards NUT returns a communications error. + */ + usb_interrupt_read(udev, + 0x81, + (usb_ctrl_charbuf)buf, 102, 1000); + + for (i = 0; command[i].str; i++) { + + int retry; + + if (strcmp(cmd, command[i].str)) { + continue; + } + + for (retry = 0; retry < 10; retry++) { + + int ret; + + ret = usb_get_string(udev, + command[i].index, langid_fix, + (usb_ctrl_charbuf)buf, 102); + + if (ret <= 0) { + upsdebugx(3, "read: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", + ret); + return ret; + } + + /* This may serve in the future */ + upsdebugx(1, "received %d (%d)", ret, buf[0]); + + + if (ret != buf[0]) { + upsdebugx(1, "size mismatch: %d / %d", ret, buf[0]); + continue; + } + + /* Simple unicode -> ASCII inplace conversion + * FIXME: this code is at least shared with mge-shut/libshut + * Create a common function? */ + unsigned int di, si, size = (unsigned int)buf[0]; + for (di = 0, si = 2; si < size; si += 2) { + + if (di >= (buflen - 1)) + break; + + if (buf[si + 1]) /* high byte */ + buf[di++] = '?'; + else + buf[di++] = buf[si]; + + } + + /* Note: effective range of di should be unsigned char */ + buf[di] = 0; + ret = (int)di; + + /* "UPS No Ack" has a special meaning */ + if ( + strcspn(buf, "\r") == 10 && + !strncasecmp(buf, "UPS No Ack", 10) + ) { + upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); + continue; + } + + /* Replace the first byte of what we received with the correct one */ + buf[0] = command[i].prefix; + + upsdebug_hex(5, "read", buf, (size_t)ret); + upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); + + return ret; + + } + + return 0; + + } + + /* Echo the unknown command back */ + upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd); + return snprintf(buf, buflen, "%s", cmd); +} + +static int ablerex_command(const char *cmd, char *buf, size_t buflen) +{ + int iii; + int len; + int idx; + char tmp[64]; + char tmpryy[64]; + + upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); + + if (buflen > INT_MAX) { + upsdebugx(3, "%s: requested to read too much (%zu), reducing buflen to (INT_MAX-1)", + __func__, buflen); + buflen = (INT_MAX - 1); + } + + int retry; + + for (retry = 0; retry < 3; retry++) { + int ret; + + memset(buf, 0, buflen); + tmp[0] = 0x05; + tmp[1] = 0; + tmp[2] = 1 + (char)strcspn(cmd, "\r"); + + for (iii = 0 ; iii < tmp[2] ; iii++) + { + tmp[3+iii] = cmd[iii]; + } + + ret = usb_control_msg(udev, + 0x21, + 0x09, 0x305, 0, + (usb_ctrl_charbuf)tmp, 47, 1000); + + upsdebugx(3, "R11 read: %s", ret ? nut_usb_strerror(ret) : "timeout"); + + usleep(500000); + tmpryy[0] = 0x05; + ret = usb_control_msg(udev, + 0xA1, + 0x01, 0x305, 0, + (usb_ctrl_charbuf)tmpryy, 47, 1000); + upsdebugx(3, "R2 read%d: %.*s", ret, ret, tmpryy); + + len = 0; + for (idx = 0 ; idx < 47 ; idx++) + { + buf[idx] = tmpryy[idx]; + if (tmpryy[idx] == '\r') + { + len = idx; + break; + } + } + upsdebugx(3, "R3 read%d: %.*s", len, len, tmpryy); + + if (len > 0) { + len ++; + } + if (ret <= 0) { + upsdebugx(3, "read: %s", ret ? nut_usb_strerror(ret) : "timeout"); + return ret; + } + + upsdebugx(1, "received %d (%d)", ret, buf[0]); + + if ((!strcasecmp(cmd, "Q1\r")) && len != 47) continue; + if ((!strcasecmp(cmd, "I\r")) && len != 39) continue; + if ((!strcasecmp(cmd, "F\r")) && len != 22) continue; + if ((!strcasecmp(cmd, "Q5\r")) && len != 22) + { + buf[0] = '('; + for (idx = 1 ; idx < 47 ; idx++) + { + buf[idx] = 0; + } + upsdebugx(3, "read Q5 Fail..."); + return 22; + } + + upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf); + return len; + } + + return 0; +} + +static void *ablerex_subdriver_fun(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &ablerex_command; + return NULL; +} + +/* Armac communication subdriver + * + * This reproduces a communication protocol used by an old PowerManagerII + * software, which doesn't seem to be Armac specific. The banner is: "2004 + * Richcomm Technologies, Inc. Dec 27 2005 ver 1.1." Maybe other Richcomm UPSes + * would work with this - better than with the richcomm_usb driver. + */ +static int armac_command(const char *cmd, char *buf, size_t buflen) +{ + char tmpbuf[6]; + int ret = 0; + size_t i, bufpos; + const size_t cmdlen = strlen(cmd); + + /* UPS ignores (doesn't echo back) unsupported commands which makes + * the initialization long. List commands tested to be unsupported: + */ + const char *unsupported[] = { + "QGS\r", + "QS\r", + "QPI\r", + "M\r", + "D\r", + NULL + }; + + for (i = 0; unsupported[i] != NULL; i++) { + if (strcmp(cmd, unsupported[i]) == 0) { + upsdebugx(2, + "armac: unsupported cmd: %.*s", + (int)strcspn(cmd, "\r"), cmd); + return snprintf(buf, buflen, "%s", cmd); + } + } + upsdebugx(4, "armac command %.*s", (int)strcspn(cmd, "\r"), cmd); + + /* Send command to the UPS in 3-byte chunks. Most fit 1 chunk, except for eg. + * parameterized tests. */ + for (i = 0; i < cmdlen;) { + const size_t bytes_to_send = (cmdlen <= (i + 3)) ? (cmdlen - i) : 3; + memset(tmpbuf, 0, sizeof(tmpbuf)); + tmpbuf[0] = 0xa0 + bytes_to_send; + memcpy(tmpbuf + 1, cmd + i, bytes_to_send); + ret = usb_control_msg(udev, + USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, + 0x09, 0x200, 0, + (usb_ctrl_charbuf)tmpbuf, 4, 5000); + i += bytes_to_send; + } + + if (ret <= 0) { + upsdebugx(1, + "send control: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", + ret); + return ret; + } + + memset(buf, 0, buflen); + + bufpos = 0; + while (bufpos + 6 < buflen) { + size_t bytes_available; + + /* Read data in 6-byte chunks */ + ret = usb_interrupt_read(udev, + 0x81, + (usb_ctrl_charbuf)tmpbuf, 6, 1000); + + /* Any errors here mean that we are unable to read a reply + * (which will happen after successfully writing a command + * to the UPS) */ + if (ret != 6) { + upsdebugx(1, + "interrupt read error: %s (%d)", + ret ? nut_usb_strerror(ret) : "timeout", + ret); + return ret; + } + + upsdebugx(4, + "read: ret %d buf %02hhx: %02hhx %02hhx %02hhx %02hhx %02hhx >%c%c%c%c%c<", + ret, + tmpbuf[0], tmpbuf[1], tmpbuf[2], tmpbuf[3], tmpbuf[4], tmpbuf[5], + tmpbuf[1], tmpbuf[2], tmpbuf[3], tmpbuf[4], tmpbuf[5]); + + bytes_available = (unsigned char)tmpbuf[0] & 0x0f; + if (bytes_available == 0) { + /* End of transfer */ + break; + } + + memcpy(buf + bufpos, tmpbuf + 1, bytes_available); + bufpos += bytes_available; + + if (bytes_available <= 2) { + /* Slow down, let the UPS buffer more bytes */ + usleep(15000); + } + } + + if (bufpos + 6 >= buflen) { + upsdebugx(2, "Protocol error, too much data read."); + return -1; + } + + upsdebugx(3, "armac command %.*s response read: '%.*s'", + (int)strcspn(cmd, "\r"), cmd, + (int)strcspn(buf, "\r"), buf + ); + + return (int)bufpos; +} + + +static void *cypress_subdriver(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &cypress_command; + return NULL; +} + +static void *sgs_subdriver(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &sgs_command; + return NULL; +} + +static void *ippon_subdriver(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &ippon_command; + return NULL; +} + +static void *krauler_subdriver(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &krauler_command; + return NULL; +} + +static void *phoenix_subdriver(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &phoenix_command; + return NULL; +} + +static void *fabula_subdriver(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &fabula_command; + return NULL; +} + +static void *phoenixtec_subdriver(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &phoenixtec_command; + return NULL; +} + +/* Note: the "hunnox_subdriver" name is taken by the subdriver_t structure */ +static void *fabula_hunnox_subdriver(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &hunnox_command; + return NULL; +} + +static void *fuji_subdriver(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &fuji_command; + return NULL; +} + +static void *snr_subdriver(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &snr_command; + return NULL; +} + +static void *armac_subdriver(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &armac_command; + return NULL; +} + +/* USB device match structure */ +typedef struct { + const int vendorID; /* USB device's VendorID */ + const int productID; /* USB device's ProductID */ + const char *vendor; /* USB device's iManufacturer string */ + const char *product; /* USB device's iProduct string */ + void *(*fun)(USBDevice_t *); /* Handler for specific processing */ +} qx_usb_device_id_t; + +/* USB VendorID/ProductID/iManufacturer/iProduct match - note: rightmost comment is used for naming rules by tools/nut-usbinfo.pl */ +static qx_usb_device_id_t qx_usb_id[] = { + { USB_DEVICE(0x05b8, 0x0000), NULL, NULL, &cypress_subdriver }, /* Agiler UPS */ + { USB_DEVICE(0xffff, 0x0000), NULL, NULL, &ablerex_subdriver_fun }, /* Ablerex 625L USB (Note: earlier best-fit was "krauler_subdriver" before PR #1135) */ + { USB_DEVICE(0x1cb0, 0x0035), NULL, NULL, &krauler_subdriver }, /* Legrand Daker DK / DK Plus */ + { USB_DEVICE(0x0665, 0x5161), NULL, NULL, &cypress_subdriver }, /* Belkin F6C1200-UNV/Voltronic Power UPSes */ + { USB_DEVICE(0x06da, 0x0002), "Phoenixtec Power","USB Cable (V2.00)", &phoenixtec_subdriver },/* Masterguard A Series */ + { USB_DEVICE(0x06da, 0x0002), NULL, NULL, &cypress_subdriver }, /* Online Yunto YQ450 */ + { USB_DEVICE(0x06da, 0x0003), NULL, NULL, &ippon_subdriver }, /* Mustek Powermust */ + { USB_DEVICE(0x06da, 0x0004), NULL, NULL, &cypress_subdriver }, /* Phoenixtec Innova 3/1 T */ + { USB_DEVICE(0x06da, 0x0005), NULL, NULL, &cypress_subdriver }, /* Phoenixtec Innova RT */ + { USB_DEVICE(0x06da, 0x0201), NULL, NULL, &cypress_subdriver }, /* Phoenixtec Innova T */ + { USB_DEVICE(0x06da, 0x0601), NULL, NULL, &phoenix_subdriver }, /* Online Zinto A */ + { USB_DEVICE(0x0f03, 0x0001), NULL, NULL, &cypress_subdriver }, /* Unitek Alpha 1200Sx */ + { USB_DEVICE(0x14f0, 0x00c9), NULL, NULL, &phoenix_subdriver }, /* GE EP series */ + { USB_DEVICE(0x0483, 0x0035), NULL, NULL, &sgs_subdriver }, /* TS Shara UPSes; vendor ID 0x0483 is from ST Microelectronics - with product IDs delegated to different OEMs */ + { USB_DEVICE(0x0001, 0x0000), "MEC", "MEC0003", &fabula_subdriver }, /* Fideltronik/MEC LUPUS 500 USB */ + { USB_DEVICE(0x0001, 0x0000), NULL, "MEC0003", &fabula_hunnox_subdriver }, /* Hunnox HNX 850, reported to also help support Powercool and some other devices; closely related to fabula with tweaks */ + { USB_DEVICE(0x0001, 0x0000), "ATCL FOR UPS", "ATCL FOR UPS", &fuji_subdriver }, /* Fuji UPSes */ + { USB_DEVICE(0x0001, 0x0000), NULL, NULL, &krauler_subdriver }, /* Krauler UP-M500VA */ + { USB_DEVICE(0x0001, 0x0000), NULL, "MEC0003", &snr_subdriver }, /* SNR-UPS-LID-XXXX UPSes */ + { USB_DEVICE(0x0925, 0x1234), NULL, NULL, &armac_subdriver }, /* Armac UPS and maybe other richcomm-like or using old PowerManagerII software */ + /* End of list */ + { -1, -1, NULL, NULL, NULL } +}; + +static int qx_is_usb_device_supported(qx_usb_device_id_t *usb_device_id_list, USBDevice_t *device) +{ + int retval = NOT_SUPPORTED; + qx_usb_device_id_t *usbdev; + + for (usbdev = usb_device_id_list; usbdev->vendorID != -1; usbdev++) { + + if (usbdev->vendorID != device->VendorID) + continue; + + /* Flag as possibly supported if we see a known vendor */ + retval = POSSIBLY_SUPPORTED; + + if (usbdev->productID != device->ProductID) + continue; + + if (usbdev->vendor + && (!device->Vendor || strcasecmp(usbdev->vendor, device->Vendor)) + ) { + continue; + } + + if (usbdev->product + && (!device->Product || strcasecmp(usbdev->product, device->Product)) + ) { + continue; + } + + /* Call the specific handler, if it exists */ + if (usbdev->fun != NULL) + (*usbdev->fun)(device); + + return SUPPORTED; + + } + + return retval; +} + +static int device_match_func(USBDevice_t *hd, void *privdata) +{ + NUT_UNUSED_VARIABLE(privdata); + + if (subdriver_command) { + return 1; + } + + switch (qx_is_usb_device_supported(qx_usb_id, hd)) + { + case SUPPORTED: + return 1; + + case POSSIBLY_SUPPORTED: + case NOT_SUPPORTED: + default: + return 0; + } +} + +static USBDeviceMatcher_t device_matcher = { + &device_match_func, + NULL, + NULL +}; +#endif /* QX_USB && !TESTING */ + + +/* == Driver functions implementations == */ + +/* See header file for details. */ +int instcmd(const char *cmdname, const char *extradata) +{ + item_t *item; + char value[SMALLBUF]; + + if (!strcasecmp(cmdname, "beeper.off")) { + /* Compatibility mode for old command */ + upslogx(LOG_WARNING, + "The 'beeper.off' command has been renamed to 'beeper.disable'"); + return instcmd("beeper.disable", NULL); + } + + if (!strcasecmp(cmdname, "beeper.on")) { + /* Compatibility mode for old command */ + upslogx(LOG_WARNING, + "The 'beeper.on' command has been renamed to 'beeper.enable'"); + return instcmd("beeper.enable", NULL); + } + + upslogx(LOG_INFO, "%s(%s, %s)", + __func__, cmdname, + extradata ? extradata : "[NULL]"); + + /* Retrieve item by command name */ + item = find_nut_info(cmdname, QX_FLAG_CMD, QX_FLAG_SKIP); + + /* Check for fallback if not found */ + if (item == NULL) { + + if (!strcasecmp(cmdname, "load.on")) { + return instcmd("load.on.delay", "0"); + } + + if (!strcasecmp(cmdname, "load.off")) { + return instcmd("load.off.delay", "0"); + } + + if (!strcasecmp(cmdname, "shutdown.return")) { + + int ret; + + /* Ensure "ups.start.auto" is set to "yes", if supported */ + if (dstate_getinfo("ups.start.auto")) { + if (setvar("ups.start.auto", "yes") != STAT_SET_HANDLED) { + upslogx(LOG_ERR, "%s: FAILED", __func__); + return STAT_INSTCMD_FAILED; + } + } + + ret = instcmd("load.on.delay", dstate_getinfo("ups.delay.start")); + if (ret != STAT_INSTCMD_HANDLED) { + return ret; + } + + return instcmd("load.off.delay", + dstate_getinfo("ups.delay.shutdown")); + + } + + if (!strcasecmp(cmdname, "shutdown.stayoff")) { + + int ret; + + /* Ensure "ups.start.auto" is set to "no", if supported */ + if (dstate_getinfo("ups.start.auto")) { + if (setvar("ups.start.auto", "no") != STAT_SET_HANDLED) { + upslogx(LOG_ERR, "%s: FAILED", __func__); + return STAT_INSTCMD_FAILED; + } + } + + ret = instcmd("load.on.delay", "-1"); + if (ret != STAT_INSTCMD_HANDLED) { + return ret; + } + + return instcmd("load.off.delay", + dstate_getinfo("ups.delay.shutdown")); + + } + + upsdebugx(2, "%s: command %s unavailable", __func__, cmdname); + return STAT_INSTCMD_INVALID; + } + + /* If extradata is empty, use the default value + * from the QX to NUT table, if any */ + extradata = extradata ? extradata : item->dfl; + snprintf(value, sizeof(value), "%s", extradata ? extradata : ""); + + /* Preprocess command */ + if (item->preprocess != NULL + && item->preprocess(item, value, sizeof(value)) + ) { + /* Something went wrong */ + upslogx(LOG_ERR, "%s: FAILED", __func__); + return STAT_INSTCMD_FAILED; + } + + /* No preprocess function -> nothing to do with extradata */ + if (item->preprocess == NULL) + snprintf(value, sizeof(value), "%s", ""); + + /* Send the command, get the reply */ + if (qx_process(item, strlen(value) > 0 ? value : NULL)) { + /* Something went wrong */ + upslogx(LOG_ERR, "%s: FAILED", __func__); + return STAT_INSTCMD_FAILED; + } + + /* We got a reply from the UPS: + * either subdriver->accepted (-> command handled) + * or the command itself echoed back (-> command failed) + */ + if (strlen(item->value) > 0) { + + if (subdriver->accepted != NULL + && !strcasecmp(item->value, subdriver->accepted) + ) { + upslogx(LOG_INFO, "%s: SUCCEED", __func__); + /* Set the status so that SEMI_STATIC vars are polled */ + data_has_changed = TRUE; + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_ERR, "%s: FAILED", __func__); + return STAT_INSTCMD_FAILED; + + } + + /* No reply from the UPS -> command handled */ + upslogx(LOG_INFO, "%s: SUCCEED", __func__); + /* Set the status so that SEMI_STATIC vars are polled */ + data_has_changed = TRUE; + return STAT_INSTCMD_HANDLED; +} + +/* See header file for details. */ +int setvar(const char *varname, const char *val) +{ + item_t *item; + char value[SMALLBUF]; + st_tree_t *root = (st_tree_t *)dstate_getroot(); + int ok = 0; + + /* Retrieve variable */ + item = find_nut_info(varname, QX_FLAG_SETVAR, QX_FLAG_SKIP); + + if (item == NULL) { + upsdebugx(2, "%s: element %s unavailable", __func__, varname); + return STAT_SET_UNKNOWN; + } + + /* No NUT variable is available for this item, so we're handling + * a one-time setvar from ups.conf */ + if (item->qxflags & QX_FLAG_NONUT) { + + const char *userval; + + /* Nothing to do */ + if (!testvar(item->info_type)) { + upsdebugx(2, "%s: nothing to do... [%s]", + __func__, item->info_type); + return STAT_SET_HANDLED; + } + + userval = getval(item->info_type); + + upslogx(LOG_INFO, "%s(%s, %s)", + __func__, varname, + userval ? userval : "[NULL]"); + + snprintf(value, sizeof(value), "%s", userval ? userval : ""); + + /* This item is available in NUT */ + } else { + + upslogx(LOG_INFO, "%s(%s, %s)", + __func__, varname, + strlen(val) ? val : "[NULL]"); + + if (!strlen(val)) { + upslogx(LOG_ERR, "%s: value not given for %s", + __func__, item->info_type); + return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ + } + + snprintf(value, sizeof(value), "%s", val); + + /* Nothing to do */ + if (!strcasecmp(dstate_getinfo(item->info_type), value)) { + upslogx(LOG_INFO, "%s: nothing to do... [%s]", + __func__, item->info_type); + return STAT_SET_HANDLED; + } + + } + + /* Check if given value is in the range of accepted values (range) */ + if (item->qxflags & QX_FLAG_RANGE) { + + long valuetoset, min, max; + + if (strspn(value, "0123456789 .") != strlen(value)) { + upslogx(LOG_ERR, "%s: non numerical value [%s: %s]", + __func__, item->info_type, value); + return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ + } + + valuetoset = strtol(value, NULL, 10); + + /* No NUT var is available for this item, so + * take its range from qx2nut table */ + if (item->qxflags & QX_FLAG_NONUT) { + + info_rw_t *rvalue; + + if (!strlen(value)) { + upslogx(LOG_ERR, "%s: value not given for %s", + __func__, item->info_type); + return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ + } + + min = max = -1; + + /* Loop on all existing values */ + for (rvalue = item->info_rw; rvalue != NULL && strlen(rvalue->value) > 0; rvalue++) { + + if (rvalue->preprocess + && rvalue->preprocess(rvalue->value, sizeof(rvalue->value)) + ) { + continue; + } + + if (min < 0) { + min = strtol(rvalue->value, NULL, 10); + continue; + } + + max = strtol(rvalue->value, NULL, 10); + + /* valuetoset is in the range */ + if (min <= valuetoset && valuetoset <= max) { + ok = 1; + break; + } + + min = -1; + max = -1; + + } + + /* We have a NUT var for this item, so check given value + * against the already set range */ + } else { + + const range_t *range = state_getrangelist(root, item->info_type); + + /* Unable to find tree node for var */ + if (!range) { + upsdebugx(2, "%s: unable to find tree node for %s", + __func__, item->info_type); + return STAT_SET_UNKNOWN; + } + + while (range) { + + min = range->min; + max = range->max; + + /* valuetoset is in the range */ + if (min <= valuetoset && valuetoset <= max) { + ok = 1; + break; + } + range = range->next; + } + + } + + if (!ok) { + upslogx(LOG_ERR, "%s: value out of range [%s: %s]", + __func__, item->info_type, value); + return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ + } + + /* Check if given value is in the range of accepted values (enum) */ + } else if (item->qxflags & QX_FLAG_ENUM) { + + /* No NUT var is available for this item, so + * take its range from qx2nut table */ + if (item->qxflags & QX_FLAG_NONUT) { + + info_rw_t *envalue; + + if (!strlen(value)) { + upslogx(LOG_ERR, "%s: value not given for %s", + __func__, item->info_type); + return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ + } + + /* Loop on all existing values */ + for (envalue = item->info_rw; envalue != NULL && strlen(envalue->value) > 0; envalue++) { + + if (envalue->preprocess + && envalue->preprocess(envalue->value, sizeof(envalue->value)) + ) { + continue; + } + + if (strcasecmp(envalue->value, value)) + continue; + + /* value found */ + ok = 1; + break; + + } + + /* We have a NUT var for this item, so check given value + * against the already set range */ + } else { + + const enum_t *enumlist = state_getenumlist(root, item->info_type); + + /* Unable to find tree node for var */ + if (!enumlist) { + upsdebugx(2, "%s: unable to find tree node for %s", + __func__, item->info_type); + return STAT_SET_UNKNOWN; + } + + while (enumlist) { + + /* If this is not the right value, go on to the next */ + if (strcasecmp(enumlist->val, value)) { + enumlist = enumlist->next; + continue; + } + + /* value found in enumlist */ + ok = 1; + break; + } + + } + + if (!ok) { + upslogx(LOG_ERR, "%s: value out of range [%s: %s]", + __func__, item->info_type, value); + return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ + } + + /* Check if given value is not too long (string) */ + } else if (item->info_flags & ST_FLAG_STRING) { + + const long aux = state_getaux(root, item->info_type); + + /* Unable to find tree node for var */ + if (aux < 0) { + upsdebugx(2, "%s: unable to find tree node for %s", + __func__, item->info_type); + return STAT_SET_UNKNOWN; + } + + /* FIXME? Should this cast to "long"? + * An int-size string is quite a lot already, + * even on architectures with a moderate INTMAX + */ + if (aux < (int)strlen(value)) { + upslogx(LOG_ERR, "%s: value is too long [%s: %s]", + __func__, item->info_type, value); + return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ + } + + } + + /* Preprocess value: from NUT-compliant to UPS-compliant */ + if (item->preprocess != NULL + && item->preprocess(item, value, sizeof(value)) + ) { + /* Something went wrong */ + upslogx(LOG_ERR, "%s: FAILED", __func__); + return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ + } + + /* Handle server side variable */ + if (item->qxflags & QX_FLAG_ABSENT) { + upsdebugx(2, "%s: setting server side variable %s", + __func__, item->info_type); + dstate_setinfo(item->info_type, "%s", value); + upslogx(LOG_INFO, "%s: SUCCEED", __func__); + return STAT_SET_HANDLED; + } + + /* No preprocess function -> nothing to do with val */ + if (item->preprocess == NULL) + snprintf(value, sizeof(value), "%s", ""); + + /* Actual variable setting */ + if (qx_process(item, strlen(value) > 0 ? value : NULL)) { + /* Something went wrong */ + upslogx(LOG_ERR, "%s: FAILED", __func__); + return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ + } + + /* We got a reply from the UPS: + * either subdriver->accepted (-> command handled) + * or the command itself echoed back (-> command failed) */ + if (strlen(item->value) > 0) { + + if (subdriver->accepted != NULL + && !strcasecmp(item->value, subdriver->accepted) + ) { + upslogx(LOG_INFO, "%s: SUCCEED", __func__); + /* Set the status so that SEMI_STATIC vars are polled */ + data_has_changed = TRUE; + return STAT_SET_HANDLED; + } + + upslogx(LOG_ERR, "%s: FAILED", __func__); + return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */ + + } + + /* No reply from the UPS -> command handled */ + upslogx(LOG_INFO, "%s: SUCCEED", __func__); + /* Set the status so that SEMI_STATIC vars are polled */ + data_has_changed = TRUE; + return STAT_SET_HANDLED; +} + +/* Try to shutdown the UPS */ +void upsdrv_shutdown(void) + __attribute__((noreturn)); + +void upsdrv_shutdown(void) +{ + int retry; + item_t *item; + const char *val; + + upsdebugx(1, "%s...", __func__); + + /* Get user-defined delays */ + + /* Start delay */ + item = find_nut_info("ups.delay.start", 0, QX_FLAG_SKIP); + + /* Don't know what happened */ + if (!item) + fatalx(EXIT_FAILURE, "Unable to set start delay"); + + /* Set the default value */ + dstate_setinfo(item->info_type, "%s", item->dfl); + + /* Set var flags/range/enum */ + qx_set_var(item); + + /* Retrieve user defined delay settings */ + val = getval(QX_VAR_ONDELAY); + + if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) { + fatalx(EXIT_FAILURE, "Start delay '%s' out of range", val); + } + + /* Shutdown delay */ + item = find_nut_info("ups.delay.shutdown", 0, QX_FLAG_SKIP); + + /* Don't know what happened */ + if (!item) + fatalx(EXIT_FAILURE, "Unable to set shutdown delay"); + + /* Set the default value */ + dstate_setinfo(item->info_type, "%s", item->dfl); + + /* Set var flags/range/enum */ + qx_set_var(item); + + /* Retrieve user defined delay settings */ + val = getval(QX_VAR_OFFDELAY); + + if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) { + fatalx(EXIT_FAILURE, "Shutdown delay '%s' out of range", val); + } + + /* Stop pending shutdowns */ + if (find_nut_info("shutdown.stop", QX_FLAG_CMD, QX_FLAG_SKIP)) { + + for (retry = 1; retry <= MAXTRIES; retry++) { + + if (instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { + continue; + } + + break; + + } + + if (retry > MAXTRIES) { + upslogx(LOG_NOTICE, "No shutdown pending"); + } + + } + + /* Shutdown */ + for (retry = 1; retry <= MAXTRIES; retry++) { + + if (testvar("stayoff")) { + + if (instcmd("shutdown.stayoff", NULL) != STAT_INSTCMD_HANDLED) { + continue; + } + + } else { + + if (instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) { + continue; + } + + } + + fatalx(EXIT_SUCCESS, "Shutting down in %s seconds", + dstate_getinfo("ups.delay.shutdown")); + } + + fatalx(EXIT_FAILURE, "Shutdown failed!"); +} + +#ifdef QX_USB + #ifndef TESTING + static const struct { + const char *name; + int (*command)(const char *cmd, char *buf, size_t buflen); + } usbsubdriver[] = { + { "cypress", &cypress_command }, + { "phoenixtec", &phoenixtec_command }, + { "phoenix", &phoenix_command }, + { "ippon", &ippon_command }, + { "krauler", &krauler_command }, + { "fabula", &fabula_command }, + { "hunnox", &hunnox_command }, + { "fuji", &fuji_command }, + { "sgs", &sgs_command }, + { "snr", &snr_command }, + { "ablerex", &ablerex_command }, + { "armac", &armac_command }, + { NULL, NULL } + }; + #endif +#endif + + +void upsdrv_help(void) +{ +#ifdef QX_USB + #ifndef TESTING + printf("\nAcceptable values for 'subdriver' via -x or ups.conf in this driver: "); + size_t i; + + for (i = 0; usbsubdriver[i].name != NULL; i++) { + if (i>0) + printf(", "); + printf("%s", usbsubdriver[i].name); + } + printf("\n\n"); + #endif +#endif + + printf("Read The Fine Manual ('man 8 nutdrv_qx')\n"); +} + +/* Adding flags/vars */ +void upsdrv_makevartable(void) +{ + char temp[SMALLBUF]; + int i; + + upsdebugx(1, "%s...", __func__); + + snprintf(temp, sizeof(temp), + "Set shutdown delay, in seconds (default=%s)", DEFAULT_OFFDELAY); + addvar(VAR_VALUE, QX_VAR_OFFDELAY, temp); + + snprintf(temp, sizeof(temp), + "Set startup delay, in seconds (default=%s)", DEFAULT_ONDELAY); + addvar(VAR_VALUE, QX_VAR_ONDELAY, temp); + + addvar(VAR_FLAG, "stayoff", + "If invoked the UPS won't return after a shutdown when FSD arises"); + + snprintf(temp, sizeof(temp), + "Set polling frequency, in seconds, to reduce data flow (default=%d)", + DEFAULT_POLLFREQ); + addvar(VAR_VALUE, QX_VAR_POLLFREQ, temp); + + addvar(VAR_VALUE, "protocol", + "Preselect communication protocol (skip autodetection)"); + + /* battery.{charge,runtime} guesstimation */ + addvar(VAR_VALUE, "runtimecal", + "Parameters used for runtime calculation"); + addvar(VAR_VALUE, "chargetime", + "Nominal charge time for UPS battery"); + addvar(VAR_VALUE, "idleload", + "Minimum load to be used for runtime calculation"); + +#ifdef QX_USB + addvar(VAR_VALUE, "subdriver", "Serial-over-USB subdriver selection"); + /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ + nut_usb_addvars(); + + addvar(VAR_VALUE, "langid_fix", + "Apply the language ID workaround to the krauler subdriver " + "(0x409 or 0x4095)"); + addvar(VAR_FLAG, "noscanlangid", "Don't autoscan valid range for langid"); +#endif /* QX_USB */ + +#ifdef QX_SERIAL + addvar(VAR_VALUE, "cablepower", "Set cable power for serial interface"); +#endif /* QX_SERIAL */ + + /* Subdrivers flags/vars */ + for (i = 0; subdriver_list[i] != NULL; i++) { + + if (subdriver_list[i]->makevartable != NULL) + subdriver_list[i]->makevartable(); + + } +} + +/* Update UPS status/infos */ +void upsdrv_updateinfo(void) +{ + time_t now; + static int retry = 0; + + upsdebugx(1, "%s...", __func__); + + time(&now); + + /* Clear status buffer before beginning */ + status_init(); + + /* Do a full update (polling) every pollfreq or upon data change + * (i.e. setvar/instcmd) */ + if ((now > (lastpoll + pollfreq)) || (data_has_changed == TRUE)) { + + upsdebugx(1, "Full update..."); + + /* Clear ups_status */ + ups_status = 0; + + alarm_init(); + + if (qx_ups_walk(QX_WALKMODE_FULL_UPDATE) == FALSE) { + + if (retry < MAXTRIES || retry == MAXTRIES) { + upsdebugx(1, + "Communications with the UPS lost: status read failed!"); + retry++; + } else { + dstate_datastale(); + } + + return; + } + + lastpoll = now; + data_has_changed = FALSE; + + ups_alarm_set(); + alarm_commit(); + + } else { + + upsdebugx(1, "Quick update..."); + + /* Quick poll data only to see if the UPS is still connected */ + if (qx_ups_walk(QX_WALKMODE_QUICK_UPDATE) == FALSE) { + + if (retry < MAXTRIES || retry == MAXTRIES) { + upsdebugx(1, + "Communications with the UPS lost: status read failed!"); + retry++; + } else { + dstate_datastale(); + } + + return; + } + + } + + ups_status_set(); + status_commit(); + + if (retry > MAXTRIES) { + upslogx(LOG_NOTICE, "Communications with the UPS re-established"); + } + + retry = 0; + + dstate_dataok(); +} + +/* Initialise data from UPS */ +void upsdrv_initinfo(void) +{ + char *val; + + upsdebugx(1, "%s...", __func__); + + dstate_setinfo("driver.version.data", "%s", subdriver->name); + + /* Initialise data */ + if (qx_ups_walk(QX_WALKMODE_INIT) == FALSE) { + fatalx(EXIT_FAILURE, "Can't initialise data from the UPS"); + } + + /* Init battery guesstimation */ + qx_initbattery(); + + if (dstate_getinfo("ups.delay.start")) { + + /* Retrieve user defined delay settings */ + val = getval(QX_VAR_ONDELAY); + + if (val && setvar("ups.delay.start", val) != STAT_SET_HANDLED) { + fatalx(EXIT_FAILURE, "Start delay '%s' out of range", val); + } + + } + + if (dstate_getinfo("ups.delay.shutdown")) { + + /* Retrieve user defined delay settings */ + val = getval(QX_VAR_OFFDELAY); + + if (val && setvar("ups.delay.shutdown", val) != STAT_SET_HANDLED) { + fatalx(EXIT_FAILURE, "Shutdown delay '%s' out of range", val); + } + + } + + if (!find_nut_info("load.off", QX_FLAG_CMD, QX_FLAG_SKIP) + && find_nut_info("load.off.delay", QX_FLAG_CMD, QX_FLAG_SKIP) + ) { + /* Adds default with a delay value of '0' (= immediate) */ + dstate_addcmd("load.off"); + } + + if (!find_nut_info("load.on", QX_FLAG_CMD, QX_FLAG_SKIP) + && find_nut_info("load.on.delay", QX_FLAG_CMD, QX_FLAG_SKIP) + ) { + /* Adds default with a delay value of '0' (= immediate) */ + dstate_addcmd("load.on"); + } + + /* Init polling frequency */ + val = getval(QX_VAR_POLLFREQ); + if (val) + pollfreq = strtol(val, NULL, 10); + + dstate_setinfo("driver.parameter.pollfreq", "%ld", pollfreq); + + time(&lastpoll); + + /* Install handlers */ + upsh.setvar = setvar; + upsh.instcmd = instcmd; + + /* Subdriver initinfo */ + if (subdriver->initinfo != NULL) + subdriver->initinfo(); +} + +/* Open the port and the like and choose the subdriver */ +void upsdrv_initups(void) +{ + upsdebugx(1, "%s...", __func__); + +#if defined(QX_SERIAL) && defined(QX_USB) + + /* Whether the device is connected through USB or serial */ + if ( + !strcasecmp(dstate_getinfo("driver.parameter.port"), "auto") || + getval("subdriver") || + getval("vendorid") || + getval("productid") || + getval("vendor") || + getval("product") || + getval("serial") || + getval("bus") || + getval("langid_fix") + ) { + /* USB */ + is_usb = 1; + } else { + /* Serial */ + is_usb = 0; + } + +#endif /* QX_SERIAL && QX_USB */ + +/* Serial */ +#ifdef QX_SERIAL + + #ifdef QX_USB + if (!is_usb) { + #endif /* QX_USB */ + + #ifndef TESTING + + const struct { + const char *val; + const int dtr; + const int rts; + } cablepower[] = { + { "normal", 1, 0 }, /* Default */ + { "reverse", 0, 1 }, + { "both", 1, 1 }, + { "none", 0, 0 }, + { NULL, 0, 0 } + }; + + int i; + const char *val; + struct termios tio; + + /* Open and lock the serial port and set the speed to 2400 baud. */ + upsfd = ser_open(device_path); + ser_set_speed(upsfd, device_path, B2400); + + if (tcgetattr(upsfd, &tio)) { + fatal_with_errno(EXIT_FAILURE, "tcgetattr"); + } + + /* Use canonical mode input processing (to read reply line) */ + tio.c_lflag |= ICANON; /* Canonical input (erase and kill processing) */ + + tio.c_cc[VEOF] = _POSIX_VDISABLE; + tio.c_cc[VEOL] = '\r'; + tio.c_cc[VERASE] = _POSIX_VDISABLE; + tio.c_cc[VINTR] = _POSIX_VDISABLE; + tio.c_cc[VKILL] = _POSIX_VDISABLE; + tio.c_cc[VQUIT] = _POSIX_VDISABLE; + tio.c_cc[VSUSP] = _POSIX_VDISABLE; + tio.c_cc[VSTART] = _POSIX_VDISABLE; + tio.c_cc[VSTOP] = _POSIX_VDISABLE; + + if (tcsetattr(upsfd, TCSANOW, &tio)) { + fatal_with_errno(EXIT_FAILURE, "tcsetattr"); + } + + val = getval("cablepower"); + for (i = 0; val && cablepower[i].val; i++) { + + if (!strcasecmp(val, cablepower[i].val)) { + break; + } + } + + if (!cablepower[i].val) { + fatalx(EXIT_FAILURE, "Value '%s' not valid for 'cablepower'", val); + } + + ser_set_dtr(upsfd, cablepower[i].dtr); + ser_set_rts(upsfd, cablepower[i].rts); + + /* Allow some time to settle for the cablepower */ + usleep(100000); + + #endif /* TESTING */ + + #ifdef QX_USB + } else { /* is_usb */ + #endif /* QX_USB */ + +#endif /* QX_SERIAL */ + +/* USB */ +#ifdef QX_USB + + warn_if_bad_usb_port_filename(device_path); + + #ifndef TESTING + int ret, langid; + char tbuf[255]; /* Some devices choke on size > 255 */ + char *regex_array[7]; + + char *subdrv = getval("subdriver"); + + regex_array[0] = getval("vendorid"); + regex_array[1] = getval("productid"); + regex_array[2] = getval("vendor"); + regex_array[3] = getval("product"); + regex_array[4] = getval("serial"); + regex_array[5] = getval("bus"); + regex_array[6] = getval("device"); + + /* Check for language ID workaround (#1) */ + if (getval("langid_fix")) { + /* Skip "0x" prefix and set back to hexadecimal */ + unsigned int u_langid_fix; + if ( (sscanf(getval("langid_fix") + 2, "%x", &u_langid_fix) != 1) + || (u_langid_fix > INT_MAX) + ) { + upslogx(LOG_NOTICE, "Error enabling language ID workaround"); + } else { + langid_fix = (int)u_langid_fix; + upsdebugx(2, + "Language ID workaround enabled (using '0x%x')", + langid_fix); + } + } + + /* Pick up the subdriver name if set explicitly */ + if (subdrv) { + + int i; + + if (!regex_array[0] || !regex_array[1]) { + fatalx(EXIT_FAILURE, + "When specifying a subdriver, " + "'vendorid' and 'productid' are mandatory."); + } + + for (i = 0; usbsubdriver[i].name; i++) { + + if (strcasecmp(subdrv, usbsubdriver[i].name)) { + continue; + } + + subdriver_command = usbsubdriver[i].command; + break; + } + + if (!subdriver_command) { + fatalx(EXIT_FAILURE, "Subdriver '%s' not found!", subdrv); + } + + } + + ret = USBNewRegexMatcher(®ex_matcher, + regex_array, + REG_ICASE | REG_EXTENDED); + switch (ret) + { + case -1: + fatal_with_errno(EXIT_FAILURE, "USBNewRegexMatcher"); + case 0: + break; /* All is well */ + default: + fatalx(EXIT_FAILURE, + "Invalid regular expression: %s", + regex_array[ret]); + } + + /* Link the matchers */ + regex_matcher->next = &device_matcher; + + ret = usb->open(&udev, &usbdevice, regex_matcher, NULL); + if (ret < 0) { + fatalx(EXIT_FAILURE, + "No supported devices found. " + "Please check your device availability with 'lsusb'\n" + "and make sure you have an up-to-date version of NUT. " + "If this does not help,\n" + "try running the driver with at least 'subdriver', " + "'vendorid' and 'productid'\n" + "options specified. Please refer to the man page " + "for details about these options\n" + "(man 8 nutdrv_qx).\n"); + } + + if (!subdriver_command) { + fatalx(EXIT_FAILURE, "No subdriver selected"); + } + + /* Create a new matcher for later reopening */ + ret = USBNewExactMatcher(&reopen_matcher, &usbdevice); + if (ret) { + fatal_with_errno(EXIT_FAILURE, "USBNewExactMatcher"); + } + + /* Link the matchers */ + reopen_matcher->next = regex_matcher; + + dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID); + dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID); + + /* Check for language ID workaround (#2) */ + if ((langid_fix != -1) && (!getval("noscanlangid"))) { + /* Future improvement: + * Asking for the zero'th index is special - it returns + * a string descriptor that contains all the language + * IDs supported by the device. + * Typically there aren't many - often only one. + * The language IDs are 16 bit numbers, and they start at + * the third byte in the descriptor. + * See USB 2.0 specification, section 9.6.7, for more + * information on this. + * This should allow automatic application of the workaround */ + ret = usb_get_string(udev, 0, 0, + (usb_ctrl_charbuf)tbuf, sizeof(tbuf)); + if (ret >= 4) { + langid = ((uint8_t)tbuf[2]) | (((uint8_t)tbuf[3]) << 8); + upsdebugx(1, + "First supported language ID: 0x%x " + "(please report to the NUT maintainer!)", + langid); + } + } + + #endif /* TESTING */ + + #ifdef QX_SERIAL + } /* is_usb */ + #endif /* QX_SERIAL */ + +#endif /* QX_USB */ + + /* Choose subdriver */ + if (!subdriver_matcher()) + fatalx(EXIT_FAILURE, "Device not supported!"); + + /* Subdriver initups */ + if (subdriver->initups != NULL) + subdriver->initups(); +} + +/* Close the ports and the like */ +void upsdrv_cleanup(void) +{ + upsdebugx(1, "%s...", __func__); + +#ifndef TESTING + +#ifdef QX_SERIAL + + #ifdef QX_USB + if (!is_usb) { + #endif /* QX_USB */ + + ser_set_dtr(upsfd, 0); + ser_close(upsfd, device_path); + + #ifdef QX_USB + } else { /* is_usb */ + #endif /* QX_USB */ + +#endif /* QX_SERIAL */ + +#ifdef QX_USB + + usb->close(udev); + USBFreeExactMatcher(reopen_matcher); + USBFreeRegexMatcher(regex_matcher); + free(usbdevice.Vendor); + free(usbdevice.Product); + free(usbdevice.Serial); + free(usbdevice.Bus); + free(usbdevice.Device); + + #ifdef QX_SERIAL + } /* is_usb */ + #endif /* QX_SERIAL */ + +#endif /* QX_USB */ + +#endif /* TESTING */ + +} + + +/* == Support functions == */ + +/* Generic command processing function: send a command and read a reply. + * Returns < 0 on error, 0 on timeout and the number of bytes read on success. */ +static ssize_t qx_command(const char *cmd, char *buf, size_t buflen) +{ +/* NOTE: Could not find in which ifdef-ed codepath, but clang complained + * about unused parameters here. Reference them just in case... + */ + NUT_UNUSED_VARIABLE(cmd); + NUT_UNUSED_VARIABLE(buf); + NUT_UNUSED_VARIABLE(buflen); + +#ifndef TESTING + + ssize_t ret = -1; + +# ifdef QX_USB + +# ifdef QX_SERIAL + /* Communication: USB */ + if (is_usb) { +# endif /* QX_SERIAL (&& QX_USB)*/ + + if (udev == NULL) { + ret = usb->open(&udev, &usbdevice, reopen_matcher, NULL); + + if (ret < 1) { + return ret; + } + } + + ret = (*subdriver_command)(cmd, buf, buflen); + + if (ret >= 0) { + return ret; + } + + switch (ret) + { + case ERROR_BUSY: /* Device or resource busy */ + fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver"); +#ifndef HAVE___ATTRIBUTE__NORETURN +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunreachable-code" +# endif + exit(EXIT_FAILURE); /* Should not get here in practice, but compiler is afraid we can fall through */ +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic pop +# endif +#endif + + #if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ + case -EPERM: /* Operation not permitted */ + fatal_with_errno(EXIT_FAILURE, "Permissions problem"); +#ifndef HAVE___ATTRIBUTE__NORETURN +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunreachable-code" +# endif + exit(EXIT_FAILURE); /* Should not get here in practice, but compiler is afraid we can fall through */ +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic pop +# endif +#endif + #endif /* WITH_LIBUSB_0_1 */ + + case ERROR_PIPE: /* Broken pipe */ + if (usb_clear_halt(udev, 0x81) == 0) { + upsdebugx(1, "Stall condition cleared"); + break; + } +#if (defined ETIME) && ETIME && WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ + goto fallthrough_case_ETIME; + case -ETIME: /* Timer expired */ + fallthrough_case_ETIME: +#endif /* ETIME && WITH_LIBUSB_0_1 */ + if (usb_reset(udev) == 0) { + upsdebugx(1, "Device reset handled"); + } + goto fallthrough_case_reconnect; + case ERROR_NO_DEVICE: /* No such device */ + case ERROR_ACCESS: /* Permission denied */ + case ERROR_IO: /* I/O error */ +#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ + case -ENXIO: /* No such device or address */ +#endif /* WITH_LIBUSB_0_1 */ + case ERROR_NOT_FOUND: /* No such file or directory */ + fallthrough_case_reconnect: + /* Uh oh, got to reconnect! */ + usb->close(udev); + udev = NULL; + break; + + case ERROR_TIMEOUT: /* Connection timed out */ + case ERROR_OVERFLOW: /* Value too large for defined data type */ +#if EPROTO && WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ + case -EPROTO: /* Protocol error */ +#endif + default: + break; + } + +# ifdef QX_SERIAL + /* Communication: serial */ + } else { /* !is_usb */ +# endif /* QX_SERIAL (&& QX_USB) */ + +# endif /* QX_USB (&& TESTING) */ + +# ifdef QX_SERIAL + + ser_flush_io(upsfd); + + ret = ser_send(upsfd, "%s", cmd); + + if (ret <= 0) { + upsdebugx(3, "send: %s (%zd)", + ret ? strerror(errno) : "timeout", ret); + return ret; + } + + upsdebugx(3, "send: '%.*s'", + (int)strcspn(cmd, "\r"), cmd); + + ret = ser_get_buf(upsfd, buf, buflen, SER_WAIT_SEC, 0); + + if (ret <= 0) { + upsdebugx(3, "read: %s (%zd)", + ret ? strerror(errno) : "timeout", ret); + return ret; + } + + upsdebug_hex(5, "read", buf, (size_t)ret); + upsdebugx(3, "read: '%.*s'", (int)strcspn(buf, "\r"), buf); + +# ifdef QX_USB + } /* !is_usb */ +# endif /* QX_USB (&& QX_SERIAL) */ + +# endif /* QX_SERIAL (&& TESTING) */ + + return ret; + +#else /* TESTING */ + + testing_t *testing = subdriver->testing; + int i; + + memset(buf, 0, buflen); + + upsdebugx(3, "send: '%.*s'", (int)strcspn(cmd, "\r"), cmd); + + for (i = 0; cmd && testing[i].cmd; i++) { + + if (strcasecmp(cmd, testing[i].cmd)) { + continue; + } + + upsdebugx(3, "read: '%.*s'", + (int)strcspn(testing[i].answer, "\r"), + testing[i].answer); + + /* If requested to do so and this is the case, try to preserve inner '\0's (treat answer as a sequence of bytes) */ + if (testing[i].answer_len > 0 && strlen(testing[i].answer) < (size_t)testing[i].answer_len) { + + size_t len; + + len = buflen <= (size_t)testing[i].answer_len ? buflen - 1 : (size_t)testing[i].answer_len; + len = len <= sizeof(testing[i].answer) ? len : sizeof(testing[i].answer); + + memcpy(buf, testing[i].answer, len); + upsdebug_hex(4, "read", buf, (int)len); + + return len; + + } + + return snprintf(buf, buflen, "%s", testing[i].answer); + + } + + /* If the driver expects some kind of reply in case of error.. */ + if (subdriver->rejected != NULL) { + + /* ..fulfill its expectations.. */ + upsdebugx(3, "read: '%.*s'", + (int)strcspn(subdriver->rejected, "\r"), + subdriver->rejected); + return snprintf(buf, buflen, "%s", subdriver->rejected); + + /* ..otherwise.. */ + } else { + + /* ..echo back the command */ + upsdebugx(3, "read: '%.*s'", (int)strcspn(cmd, "\r"), cmd); + return snprintf(buf, buflen, "%s", cmd); + + } + +#endif /* TESTING */ +} + +/* See header file for details. + * Interpretation is done in ups_status_set(). */ +void update_status(const char *value) +{ + status_lkp_t *status_item; + int clear = 0; + + upsdebugx(5, "%s: %s", __func__, value); + + if (*value == '!') { + value++; + clear = 1; + } + + for (status_item = status_info; status_item->status_str != NULL ; status_item++) { + + if (strcasecmp(status_item->status_str, value)) + continue; + + if (clear) { + ups_status &= ~status_item->status_mask; + } else { + ups_status |= status_item->status_mask; + } + + return; + } + + upsdebugx(5, "%s: Warning! %s not in list of known values", + __func__, value); +} + +/* Choose subdriver */ +static int subdriver_matcher(void) +{ + const char *protocol = getval("protocol"); + int i; + + /* Select the subdriver for this device */ + for (i = 0; subdriver_list[i] != NULL; i++) { + + int j; + + /* If protocol is set in ups.conf, use it */ + if (protocol) { + + char subdrv_name[SMALLBUF]; + + /* Get rid of subdriver version */ + snprintf(subdrv_name, sizeof(subdrv_name), "%.*s", + (int)strcspn(subdriver_list[i]->name, " "), + subdriver_list[i]->name); + + if (strcasecmp(subdrv_name, protocol)) { + upsdebugx(2, "Skipping protocol %s", + subdriver_list[i]->name); + continue; + } + + } + + /* Give every subdriver some tries */ + for (j = 0; j < MAXTRIES; j++) { + + subdriver = subdriver_list[i]; + + if (subdriver->claim()) { + break; + } + + subdriver = NULL; + + } + + if (subdriver != NULL) + break; + + } + + if (!subdriver) { + upslogx(LOG_ERR, "Device not supported!"); + return 0; + } + + upslogx(LOG_INFO, "Using protocol: %s", subdriver->name); + + return 1; +} + +/* Set vars boundaries */ +static void qx_set_var(item_t *item) +{ + if (!(item->qxflags & QX_FLAG_NONUT)) + dstate_setflags(item->info_type, item->info_flags); + + /* Set max length for strings, if needed */ + if (item->info_flags & ST_FLAG_STRING && !(item->qxflags & QX_FLAG_NONUT)) + dstate_setaux(item->info_type, strtol(item->info_rw[0].value, NULL, 10)); + + /* Set enum list */ + if (item->qxflags & QX_FLAG_ENUM) { + + info_rw_t *envalue; + char buf[LARGEBUF] = ""; + + /* Loop on all existing values */ + for (envalue = item->info_rw; envalue != NULL && strlen(envalue->value) > 0; envalue++) { + + if (envalue->preprocess + && envalue->preprocess(envalue->value, sizeof(envalue->value)) + ) { + continue; + } + + /* This item is not available yet in NUT, so publish these data in the logs */ + if (item->qxflags & QX_FLAG_NONUT) { + + snprintfcat(buf, sizeof(buf), " %s", envalue->value); + + /* This item is available in NUT, add its enum to the variable */ + } else { + + dstate_addenum(item->info_type, "%s", envalue->value); + + } + + } + + if (item->qxflags & QX_FLAG_NONUT) + upslogx(LOG_INFO, "%s, settable values:%s", + item->info_type, + strlen(buf) > 0 ? buf : " none"); + + } + + /* Set range */ + if (item->qxflags & QX_FLAG_RANGE) { + + info_rw_t *rvalue, *from = NULL, *to = NULL; + int ok = 0; + + /* Loop on all existing values */ + for (rvalue = item->info_rw; rvalue != NULL && strlen(rvalue->value) > 0; rvalue++) { + + if (rvalue->preprocess + && rvalue->preprocess(rvalue->value, sizeof(rvalue->value)) + ) { + continue; + } + + if (!from) { + from = rvalue; + continue; + } + + to = rvalue; + + /* This item is not available yet in NUT, so + * publish these data in the logs */ + if (item->qxflags & QX_FLAG_NONUT) { + + upslogx(LOG_INFO, "%s, settable range: %s..%s", + item->info_type, from->value, to->value); + ok++; + + /* This item is available in NUT, add its range to the variable */ + } else { + long lFrom = strtol(from->value, NULL, 10), + lTo = strtol(to->value, NULL, 10); + + if (lFrom > INT_MAX || lTo > INT_MAX) { + upslogx(LOG_INFO, + "%s, settable range exceeds INT_MAX: %ld..%ld", + item->info_type, lFrom, lTo); + } else { + dstate_addrange(item->info_type, (int)lFrom, (int)lTo); + } + } + + from = NULL; + to = NULL; + + } + + /* This item is not available yet in NUT and we weren't able to + * get its range; let people know it */ + if ((item->qxflags & QX_FLAG_NONUT) && !ok) + upslogx(LOG_INFO, "%s, settable range: none", item->info_type); + + } +} + +/* Walk UPS variables and set elements of the qx2nut array. */ +static bool_t qx_ups_walk(walkmode_t mode) +{ + item_t *item; + int retcode; + + /* Clear batt.{chrg,runt}.act for guesstimation */ + if (mode == QX_WALKMODE_FULL_UPDATE) { + batt.runt.act = -1; + batt.chrg.act = -1; + } + + /* Clear data from previous_item */ + memset(previous_item.command, 0, sizeof(previous_item.command)); + memset(previous_item.answer, 0, sizeof(previous_item.answer)); + + /* 3 modes: QX_WALKMODE_INIT, QX_WALKMODE_QUICK_UPDATE + * and QX_WALKMODE_FULL_UPDATE */ + + /* Device data walk */ + for (item = subdriver->qx2nut; item->info_type != NULL; item++) { + + /* Skip this item */ + if (item->qxflags & QX_FLAG_SKIP) + continue; + + upsdebugx(10, "%s: processing: %s", __func__, item->info_type); + + /* Filter data according to mode */ + switch (mode) + { + /* Device capabilities enumeration */ + case QX_WALKMODE_INIT: + + /* Special case for handling server side variables */ + if (item->qxflags & QX_FLAG_ABSENT) { + + /* Already set */ + if (dstate_getinfo(item->info_type)) + continue; + + dstate_setinfo(item->info_type, "%s", item->dfl); + + /* Set var flags/range/enum */ + qx_set_var(item); + + continue; + } + + /* Allow duplicates for these NUT variables */ + if (!strncmp(item->info_type, "ups.alarm", 9) + || !strncmp(item->info_type, "ups.status", 10) + ) { + break; + } + + /* This one doesn't exist yet */ + if (dstate_getinfo(item->info_type) == NULL) + break; + + continue; + + case QX_WALKMODE_QUICK_UPDATE: + + /* Quick update only deals with status and alarms! */ + if (!(item->qxflags & QX_FLAG_QUICK_POLL)) + continue; + + break; + + case QX_WALKMODE_FULL_UPDATE: + + /* These don't need polling after initinfo() */ + if (item->qxflags & (QX_FLAG_ABSENT | QX_FLAG_CMD | QX_FLAG_SETVAR | QX_FLAG_STATIC)) + continue; + + /* These need to be polled after user changes (setvar / instcmd) */ + if ((item->qxflags & QX_FLAG_SEMI_STATIC) + && (data_has_changed == FALSE) + ) { + continue; + } + + break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: + fatalx(EXIT_FAILURE, "%s: unknown update mode!", __func__); +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif + + } + + /* Instant commands */ + if (item->qxflags & QX_FLAG_CMD) { + dstate_addcmd(item->info_type); + continue; + } + + /* Setvars */ + if (item->qxflags & QX_FLAG_SETVAR) { + + if (item->qxflags & QX_FLAG_NONUT) { + setvar(item->info_type, NULL); + item->qxflags |= QX_FLAG_SKIP; + } + + continue; + + } + + /* Check whether the previous item uses the same command + * and then use its answer, if available.. */ + if (strlen(previous_item.command) > 0 + && strlen(previous_item.answer) > 0 + && !strcasecmp(previous_item.command, item->command) + ) { + + snprintf(item->answer, sizeof(item->answer), "%s", + previous_item.answer); + + /* Process the answer */ + retcode = qx_process_answer(item, strlen(item->answer)); + + /* ..otherwise: execute command to get answer from the UPS */ + } else { + + retcode = qx_process(item, NULL); + + } + + /* Record item as previous_item */ + snprintf(previous_item.command, sizeof(previous_item.command), "%s", + item->command); + snprintf(previous_item.answer, sizeof(previous_item.answer), "%s", + item->answer); + + if (retcode) { + + /* Clear data from the item */ + memset(item->answer, 0, sizeof(item->answer)); + memset(item->value, 0, sizeof(item->value)); + + if (item->qxflags & QX_FLAG_QUICK_POLL) + return FALSE; + + if (mode == QX_WALKMODE_INIT) + /* Skip this item from now on */ + item->qxflags |= QX_FLAG_SKIP; + + /* Don't know what happened, try again later... */ + continue; + + } + + /* Process the value we got back (set status bits + * and set the value of other parameters) */ + retcode = ups_infoval_set(item); + + /* Clear data from the item */ + memset(item->answer, 0, sizeof(item->answer)); + memset(item->value, 0, sizeof(item->value)); + + /* Uh-oh! Some error! */ + if (retcode == -1) { + + if (item->qxflags & QX_FLAG_QUICK_POLL) + return FALSE; + + continue; + + } + + /* Set var flags/range/enum (not for ups.{alarm.status}, + * hence the retcode check) */ + if (retcode && mode == QX_WALKMODE_INIT) { + qx_set_var(item); + } + + } + + /* Update battery guesstimation */ + if (mode == QX_WALKMODE_FULL_UPDATE + && (d_equal(batt.runt.act, -1) || d_equal(batt.chrg.act, -1)) + ) { + + if (getval("runtimecal")) { + + time_t battery_now; + + time(&battery_now); + + /* OL */ + if (ups_status & STATUS(OL)) { + + batt.runt.est += batt.runt.nom * difftime(battery_now, battery_lastpoll) / batt.chrg.time; + if (batt.runt.est > batt.runt.nom) { + batt.runt.est = batt.runt.nom; + } + + /* OB */ + } else { + + batt.runt.est -= load.eff * difftime(battery_now, battery_lastpoll); + if (batt.runt.est < 0) { + batt.runt.est = 0; + } + + } + + const char *val = dstate_getinfo("battery.voltage"); + + if (!val) { + upsdebugx(2, "%s: unable to get battery.voltage", __func__); + } else { + /* For age-corrected estimates below, + * see theory and experimental graphs at + * https://github.com/networkupstools/nut/pull/1027 + */ + + batt.volt.act = batt.packs * strtod(val, NULL); + + if (batt.volt.act > 0 && batt.volt.low > 0 && batt.volt.high > batt.volt.low) { + + double voltage_battery_charge = (batt.volt.act - batt.volt.low) / (batt.volt.high - batt.volt.low); + + if (voltage_battery_charge < 0) { + voltage_battery_charge = 0; + } + + if (voltage_battery_charge > 1) { + voltage_battery_charge = 1; + } + + /* Correct estimated runtime remaining for old batteries: + * this value replacement only happens if the actual + * voltage_battery_charge is smaller than expected by + * previous (load-based) estimation, thus adapting to a + * battery too old and otherwise behaving non-linearly + */ + if (voltage_battery_charge < (batt.runt.est / batt.runt.nom)) { + double estPrev = batt.runt.est; + batt.runt.est = voltage_battery_charge * batt.runt.nom; + upsdebugx(3, "%s: updating batt.runt.est from '%g' to '%g'", + __func__, estPrev, batt.runt.est); + } + + } + } + + if (d_equal(batt.chrg.act, -1)) + dstate_setinfo("battery.charge", "%.0f", + 100 * batt.runt.est / batt.runt.nom); + + if (d_equal(batt.runt.act, -1) && !qx_load()) + dstate_setinfo("battery.runtime", "%.0f", + batt.runt.est / load.eff); + + battery_lastpoll = battery_now; + + } else { + + qx_battery(); + + } + } + + return TRUE; +} + +/* Convert the local status information to NUT format and set NUT alarms. */ +static void ups_alarm_set(void) +{ + if (ups_status & STATUS(RB)) { + alarm_set("Replace battery!"); + } + if (ups_status & STATUS(FSD)) { + alarm_set("Shutdown imminent!"); + } +} + +/* Convert the local status information to NUT format and set NUT status. */ +static void ups_status_set(void) +{ + if (ups_status & STATUS(OL)) { + status_set("OL"); /* On line */ + } else { + status_set("OB"); /* On battery */ + } + if (ups_status & STATUS(DISCHRG)) { + status_set("DISCHRG"); /* Discharging */ + } + if (ups_status & STATUS(CHRG)) { + status_set("CHRG"); /* Charging */ + } + if (ups_status & STATUS(LB)) { + status_set("LB"); /* Low battery */ + } + if (ups_status & STATUS(OVER)) { + status_set("OVER"); /* Overload */ + } + if (ups_status & STATUS(RB)) { + status_set("RB"); /* Replace battery */ + } + if (ups_status & STATUS(TRIM)) { + status_set("TRIM"); /* SmartTrim */ + } + if (ups_status & STATUS(BOOST)) { + status_set("BOOST"); /* SmartBoost */ + } + if (ups_status & STATUS(BYPASS)) { + status_set("BYPASS"); /* On bypass */ + } + if (ups_status & STATUS(OFF)) { + status_set("OFF"); /* UPS is off */ + } + if (ups_status & STATUS(CAL)) { + status_set("CAL"); /* Calibration */ + } + if (ups_status & STATUS(FSD)) { + status_set("FSD"); /* Forced shutdown */ + } +} + +/* See header file for details. */ +item_t *find_nut_info(const char *varname, const unsigned long flag, const unsigned long noflag) +{ + item_t *item; + + for (item = subdriver->qx2nut; item->info_type != NULL; item++) { + + if (strcasecmp(item->info_type, varname)) + continue; + + if (flag && ((item->qxflags & flag) != flag)) + continue; + + if (noflag && (item->qxflags & noflag)) + continue; + + return item; + } + + upsdebugx(2, "%s: info type %s not found", __func__, varname); + return NULL; +} + +/* Process the answer we got back from the UPS + * Return -1 on errors, 0 on success */ +static int qx_process_answer(item_t *item, const size_t len) +{ + /* Query rejected by the UPS */ + if (subdriver->rejected && !strcasecmp(item->answer, subdriver->rejected)) { + upsdebugx(2, "%s: query rejected by the UPS (%s)", + __func__, item->info_type); + return -1; + } + + /* Short reply */ + if (item->answer_len && len < item->answer_len) { + upsdebugx(2, "%s: short reply (%s)", + __func__, item->info_type); + return -1; + } + + /* Wrong leading character */ + if (item->leading && item->answer[0] != item->leading) { + upsdebugx(2, + "%s: %s - invalid start character [%02x], expected [%02x]", + __func__, item->info_type, item->answer[0], item->leading); + return -1; + } + + /* Check boundaries */ + if (item->to && item->to < item->from) { + upsdebugx(1, + "%s: in %s, starting char's position (%d) " + "follows ending char's one (%d)", + __func__, item->info_type, item->from, item->to); + return -1; + } + + /* Get value */ + if (strlen(item->answer)) { + snprintf(item->value, sizeof(item->value), "%.*s", + item->to ? 1 + item->to - item->from : (int)strcspn(item->answer, "\r") - item->from, + item->answer + item->from); + } else { + snprintf(item->value, sizeof(item->value), "%s", ""); + } + + return 0; +} + +/* See header file for details. */ +int qx_process(item_t *item, const char *command) +{ + char buf[sizeof(item->answer) - 1] = "", *cmd; + ssize_t len; + size_t cmdlen = command ? + (strlen(command) >= SMALLBUF ? strlen(command) + 1 : SMALLBUF) : + (item->command && strlen(item->command) >= SMALLBUF ? strlen(item->command) + 1 : SMALLBUF); + size_t cmdsz = (sizeof(char) * cmdlen); /* in bytes, to be pedantic */ + + if ( !(cmd = xmalloc(cmdsz)) ) { + upslogx(LOG_ERR, "qx_process() failed to allocate buffer"); + return -1; + } + + /* Prepare the command to be used */ + memset(cmd, 0, cmdsz); + snprintf(cmd, cmdsz, "%s", command ? command : item->command); + + /* Preprocess the command */ + if ( + item->preprocess_command != NULL && + item->preprocess_command(item, cmd, cmdsz) == -1 + ) { + upsdebugx(4, "%s: failed to preprocess command [%s]", + __func__, item->info_type); + free (cmd); + return -1; + } + + /* Send the command */ + len = qx_command(cmd, buf, sizeof(buf)); + + memset(item->answer, 0, sizeof(item->answer)); + + if (len < 0 || len > INT_MAX) { + upsdebugx(4, "%s: failed to preprocess answer [%s]", + __func__, item->info_type); + free (cmd); + return -1; + } + + memcpy(item->answer, buf, sizeof(buf)); + + /* Preprocess the answer */ + if (item->preprocess_answer != NULL) { + len = item->preprocess_answer(item, (int)len); + if (len < 0 || len > INT_MAX) { + upsdebugx(4, "%s: failed to preprocess answer [%s]", + __func__, item->info_type); + /* Clear the failed answer, preventing it from + * being reused by next items with same command */ + memset(item->answer, 0, sizeof(item->answer)); + free (cmd); + return -1; + } + } + + free (cmd); + + /* Process the answer to get the value */ + return qx_process_answer(item, (size_t)len); +} + +/* See header file for details. */ +int ups_infoval_set(item_t *item) +{ + char value[SMALLBUF] = ""; + + /* Item need to be preprocessed? */ + if (item->preprocess != NULL){ + + /* Process the value returned by the UPS to NUT standards */ + if (item->preprocess(item, value, sizeof(value))) { + upsdebugx(4, "%s: failed to preprocess value [%s: %s]", + __func__, item->info_type, item->value); + return -1; + } + + /* Deal with status items */ + if (!strncmp(item->info_type, "ups.status", 10)) { + if (strlen(value) > 0) + update_status(value); + return 0; + } + + /* Deal with alarm items */ + if (!strncmp(item->info_type, "ups.alarm", 9)) { + if (strlen(value) > 0) + alarm_set(value); + return 0; + } + + } else { + + snprintf(value, sizeof(value), "%s", item->value); + + /* Cover most of the cases: either left/right filled with hashes, + * spaces or a mix of both */ + if (item->qxflags & QX_FLAG_TRIM) + str_trim_m(value, "# "); + + if (strcasecmp(item->dfl, "%s")) { + + if (strspn(value, "0123456789 .") != strlen(value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", + __func__, item->info_type, value); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, sizeof(value), item->dfl, strtod(value, NULL)); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + } + + } + + if (item->qxflags & QX_FLAG_NONUT) { + upslogx(LOG_INFO, "%s: %s", item->info_type, value); + return 1; + } + + if (!strlen(value)) { + upsdebugx(1, "%s: non significant value [%s]", + __func__, item->info_type); + return -1; + } + + dstate_setinfo(item->info_type, "%s", value); + + /* Fill batt.{chrg,runt}.act for guesstimation */ + if (!strcasecmp(item->info_type, "battery.charge")) + batt.chrg.act = strtol(value, NULL, 10); + else if (!strcasecmp(item->info_type, "battery.runtime")) + batt.runt.act = strtol(value, NULL, 10); + + return 1; +} + +/* See header file for details. */ +unsigned int qx_status(void) +{ + return ups_status; +} diff --git a/drivers/nutdrv_qx.h b/drivers/nutdrv_qx.h new file mode 100644 index 0000000..3cc3ebd --- /dev/null +++ b/drivers/nutdrv_qx.h @@ -0,0 +1,200 @@ +/* nutdrv_qx.h - Driver for USB and serial UPS units with Q* protocols + * + * Copyright (C) + * 2013 Daniele Pezzini + * Based on usbhid-ups.h - Copyright (C) + * 2003-2009 Arnaud Quette + * 2005-2006 Peter Selinger + * 2007-2009 Arjen de Korte + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_H +#define NUTDRV_QX_H + +#include +#include +#include +#include +#include "config.h" + +/* For testing purposes */ +/*#define TESTING*/ + +/* Driver's parameters */ +#define QX_VAR_ONDELAY "ondelay" +#define QX_VAR_OFFDELAY "offdelay" +#define QX_VAR_POLLFREQ "pollfreq" + +/* Parameters default values */ +#define DEFAULT_ONDELAY "180" /* Delay between return of utility power and powering up of load, in seconds */ +#define DEFAULT_OFFDELAY "30" /* Delay before power off, in seconds */ +#define DEFAULT_POLLFREQ 30 /* Polling interval between full updates, in seconds; the driver will do quick polls in the meantime */ + +#ifndef TRUE +typedef enum { FALSE, TRUE } bool_t; +#else +typedef int bool_t; +#endif + +/* Structure for rw vars */ +typedef struct { + char value[SMALLBUF]; /* Value for enum/range, or length for ST_FLAG_STRING */ + int (*preprocess)(char *value, const size_t len); /* Optional function to preprocess range/enum value. + * This function will be given value and its size_t and must return either 0 if value is supported or -1 if not supported. */ +} info_rw_t; + +/* Structure containing information about how to get/set data from/to the UPS and convert these to/from NUT standard */ +typedef struct item_t { + const char *info_type; /* NUT variable name + * If QX_FLAG_NONUT is set, name to print to the logs + * If both QX_FLAG_NONUT and QX_FLAG_SETVAR are set, name of the var to retrieve from ups.conf */ + const int info_flags; /* NUT flags (ST_FLAG_* values to set in dstate_addinfo) */ + info_rw_t *info_rw; /* An array of info_rw_t to handle r/w variables: + * If ST_FLAG_STRING is set: length of the string (dstate_setaux) + * If QX_FLAG_ENUM is set: enumerated values (dstate_addenum) + * If QX_FLAG_RANGE is set: range boundaries (dstate_addrange) + * If QX_FLAG_SETVAR is set the value given by the user will be checked against these infos. */ + const char *command; /* Command sent to the UPS to get answer/to execute an instant command/to set a variable */ + + char answer[SMALLBUF]; /* Answer from the UPS, filled at runtime. + * If you expect a nonvalid C string (e.g.: inner '\0's) or need to perform actions before the answer is used (and treated as a null-terminated string), you should set a preprocess_answer() function */ + const size_t answer_len; /* Expected min length of the answer. Set it to 0 if there's no minimum length to look after. */ + const char leading; /* Expected leading character of the answer (optional) */ + + char value[SMALLBUF]; /* Value from the answer, filled at runtime (i.e. answer between from and to) */ + const int from; /* Position of the starting character of the info (i.e. 'value') we're after in the answer */ + const int to; /* Position of the ending character of the info (i.e. 'value') we're after in the answer: use 0 if all the remaining of the line is needed */ + + const char *dfl; /* Format to store value from the UPS in NUT variables. Not used by the driver for QX_FLAG_{CMD,SETVAR} items. + * If there's no preprocess function, set it either to %s for strings or to a floating point specifier (e.g. %.1f) for numbers. + * Otherwise: + * If QX_FLAG_ABSENT: default value + * If QX_FLAG_CMD: default command value */ + + unsigned long qxflags; /* Driver's own flags */ + + int (*preprocess_command)(struct item_t *item, char *command, const size_t commandlen); + /* Last chance to preprocess the command to be sent to the UPS (e.g. to add CRC, ...). + * This function is given the currently processed item (item), the command to be sent to the UPS (command) and its size_t (commandlen). + * Return -1 in case of errors, else 0. + * command must be filled with the actual command to be sent to the UPS. */ + + int (*preprocess_answer)(struct item_t *item, const int len); + /* Function to preprocess the answer we got from the UPS before we do anything else (e.g. for CRC, decoding, ...). + * This function is given the currently processed item (item) with the answer we got from the UPS unmolested and already stored in item->answer and the length of that answer (len). + * Return -1 in case of errors, else the length of the newly allocated item->answer (from now on, treated as a null-terminated string). */ + + int (*preprocess)(struct item_t *item, char *value, const size_t valuelen); + /* Function to preprocess the data from/to the UPS + * This function is given the currently processed item (item), a char array (value) and its size_t (valuelen). + * Return -1 in case of errors, else 0. + * If QX_FLAG_SETVAR/QX_FLAG_CMD -> process command before it is sent: value must be filled with the command to be sent to the UPS. + * Otherwise -> process value we got from answer before it gets stored in a NUT variable: value must be filled with the processed value already compliant to NUT standards. */ +} item_t; + +/* Driver's own flags */ +#define QX_FLAG_STATIC 2UL /* Retrieve info only once. */ +#define QX_FLAG_SEMI_STATIC 4UL /* Retrieve info smartly, i.e. only when a command/setvar is executed and we expect that data could have been changed. */ +#define QX_FLAG_ABSENT 8UL /* Data is absent in the device, use default value. */ +#define QX_FLAG_QUICK_POLL 16UL /* Mandatory vars, polled also in QX_WALKMODE_QUICK_UPDATE. + * If there's a problem with a var not flagged as QX_FLAG_QUICK_POLL in QX_WALKMODE_INIT, the driver will automagically set QX_FLAG_SKIP on it and then it'll skip that item in QX_WALKMODE_{QUICK,FULL}_UPDATE. + * Otherwise, if the item has the flag QX_FLAG_QUICK_POLL set, in case of errors in QX_WALKMODE_INIT the driver will set datastale. */ +#define QX_FLAG_CMD 32UL /* Instant command. */ +#define QX_FLAG_SETVAR 64UL /* The var is settable and the actual item stores info on how to set it. */ +#define QX_FLAG_TRIM 128UL /* This var's value need to be trimmed of leading/trailing spaces/hashes. */ +#define QX_FLAG_ENUM 256UL /* Enum values exist and are stored in info_rw. */ +#define QX_FLAG_RANGE 512UL /* Ranges for this var available and are stored in info_rw. */ +#define QX_FLAG_NONUT 1024UL /* This var doesn't have a corresponding var in NUT. */ +#define QX_FLAG_SKIP 2048UL /* Skip this var: this item won't be processed. */ + +#define MAXTRIES 3 /* Max number of retries */ + +#ifdef TESTING +/* Testing struct */ +typedef struct { + const char *cmd; /* Command to match */ + const char answer[SMALLBUF]; /* Answer for that command. + * Note: if 'answer' contains inner '\0's, in order to preserve them, 'answer_len' as well as an item_t->preprocess_answer() function must be set */ + const int answer_len; /* Answer length: + * - if set to -1 -> auto calculate answer length (treat 'answer' as a null-terminated string) + * - otherwise -> use the provided length (if reasonable) and preserve inner '\0's (treat 'answer' as a sequence of bytes till the item_t->preprocess_answer() function gets called) */ +} testing_t; +#endif /* TESTING */ + +/* Subdriver interface */ +typedef struct { + const char *name; /* Name of this subdriver, i.e. name (must be equal to the protocol name) + space + version */ + int (*claim)(void); /* Function that allows the subdriver to "claim" a device: return 1 if device is covered by this subdriver, else 0 */ + item_t *qx2nut; /* Main table of vars and instcmds */ + void (*initups)(void); /* Subdriver specific upsdrv_initups. Called at the end of nutdrv_qx's own upsdrv_initups */ + void (*initinfo)(void); /* Subdriver specific upsdrv_initinfo. Called at the end of nutdrv_qx's own upsdrv_initinfo */ + void (*makevartable)(void); /* Subdriver specific ups.conf flags/vars */ + const char *accepted; /* String to match if the driver is expecting a reply from the UPS on instcmd/setvar. + * This comparison is done after the answer we got back from the UPS has been processed to get the value we are searching: + * - you don't have to include the trailing carriage return (\r) + * - you can decide at which index of the answer the value should start or end setting the appropriate from and to in the item_t */ + const char *rejected; /* String to match if the driver is expecting a reply from the UPS in case of error. + * This comparison is done on the answer we got back from the UPS before it has been processed: + * - include also the trailing carriage return (\r) and whatever character is expected */ +#ifdef TESTING + testing_t *testing; /* Testing table: commands and the replies used for testing the subdriver */ +#endif /* TESTING */ +} subdriver_t; + +/* The following functions are exported for the benefit of subdrivers */ + /* Execute an instant command. In detail: + * - look up the given 'cmdname' in the qx2nut data structure (if not found, try to fallback to commonly known commands); + * - if 'cmdname' is found, call its preprocess function, passing to it 'extradata', if any, otherwise its dfl value, if any; + * - send the command to the device and check the reply. + * Return STAT_INSTCMD_INVALID if the command is invalid, STAT_INSTCMD_FAILED if it failed, STAT_INSTCMD_HANDLED on success. */ +int instcmd(const char *cmdname, const char *extradata); + /* Set r/w variable to a value after it has been checked against its info_rw structure. Return STAT_SET_HANDLED on success, otherwise STAT_SET_UNKNOWN. */ +int setvar(const char *varname, const char *val); + /* Find an item of item_t type in qx2nut data structure by its info_type, optionally filtered by its qxflags, and return it if found, otherwise return NULL. + * - 'flag': flags that have to be set in the item, i.e. if one of the flags is absent in the item it won't be returned + * - 'noflag': flags that have to be absent in the item, i.e. if at least one of the flags is set in the item it won't be returned */ +item_t *find_nut_info(const char *varname, const unsigned long flag, const unsigned long noflag); + /* Send 'command' (a null-terminated byte string) or, if it is NULL, send the command stored in the item to the UPS and process the reply, saving it in item->answer. Return -1 on errors, 0 on success. */ +int qx_process(item_t *item, const char *command); + /* Process the value we got back from the UPS (set status bits and set the value of other parameters), calling the item-specific preprocess function, if any, otherwise executing the standard preprocessing (including trimming if QX_FLAG_TRIM is set). + * Return -1 on failure, 0 for a status update and 1 in all other cases. */ +int ups_infoval_set(item_t *item); + /* Return the currently processed status so that it can be checked with one of the status_bit_t passed to the STATUS() macro. */ +unsigned int qx_status(void); + /* Edit the current status: it takes one of the NUT status (all but OB are supported, simply set it as not OL), eventually preceded with an exclamation mark to clear it from the status (e.g. !OL). */ +void update_status(const char *nutvalue); + +/* Data for processing status values */ +#define STATUS(x) ((unsigned int)1U< + * 2021 Ablerex Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_blazer-common.h" + +#include "nutdrv_qx_ablerex.h" + +#define ABLEREX_VERSION "Ablerex 0.01" + +static int Q5_Vbc = -1; +static int ablerexQ5Vb = -1; + +static int ablerex_Q5(item_t *item, char *value, const size_t valuelen) { +/* + int RawValue = 0; + + RawValue = (unsigned char)item->value[0] * 256 + (unsigned char)item->value[1]; +*/ + + //ablerexQ5Vb = (unsigned char)buf[7] * 256 + (unsigned char)buf[8]; + //Q5_Vbc = (unsigned char)buf[9] * 256 + (unsigned char)buf[10]; + upsdebugx(2, "Q51: %d %d %d %d %d %d", item->answer[0], item->answer[1], item->answer[2], item->answer[3], item->answer[4], item->answer[5]); + upsdebugx(2, "Q52: %d %d %d %d %d %d", item->answer[6], item->answer[7], item->answer[8], item->answer[9], item->answer[10], item->answer[11]); + upsdebugx(2, "Q53: %d %d %d %d", item->answer[12], item->answer[13], item->answer[14], item->answer[15]); + + int Q5_Fout = (unsigned char)item->answer[1] * 256 + (unsigned char)item->answer[2]; + int Q5_Vb = (unsigned char)item->answer[7] * 256 + (unsigned char)item->answer[8]; + Q5_Vbc = (unsigned char)item->answer[9] * 256 + (unsigned char)item->answer[10]; + //int Q5_InvW = (unsigned char)item->answer[11] * 256 + (unsigned char)item->answer[12]; + int Q5_Err = (unsigned char)item->answer[13] * 256 + (unsigned char)item->answer[14]; + int Q5_O_Cur = (unsigned char)item->answer[15] * 256 + (unsigned char)item->answer[16]; + + ablerexQ5Vb = Q5_Vb; + upsdebugx(2, "Q5: %.1f %d %.1f", 0.1 * Q5_Fout, Q5_Err, 0.1 * Q5_O_Cur); + upsdebugx(2, "Q5Vb: %d Vbc %d", Q5_Vb, Q5_Vbc); + dstate_setinfo("output.frequency", "%.1f", 0.1 * Q5_Fout); + dstate_setinfo("ups.alarm", "%d", Q5_Err); + dstate_setinfo("output.current", "%.1f", 0.1 * Q5_O_Cur); + + snprintf(value, valuelen, "%.1f", Q5_Fout * 0.1); + +/* + switch (item->from) + { + case 1: + snprintf(value, valuelen, "%.1f", RawValue * 0.1); + upsdebugx(2, "Q51: %.1f", 0.1*RawValue); + break; + case 13: + snprintf(value, valuelen, "%.0f", RawValue); + upsdebugx(2, "Q52: %.0f", 0.1*RawValue); + break; + case 15: + snprintf(value, valuelen, "%.1f", RawValue * 0.1); + upsdebugx(2, "Q53: %.1f", 0.1*RawValue); + break; + + default: + //Don't know what happened + return -1; + } +*/ + + return 0; +} + +static int ablerex_battery(item_t *item, char *value, const size_t valuelen) { + double BattV = 0.0; + BattV = strtod(item->value, NULL); + upsdebugx(2, "battvoltact2: %.2f", BattV); + if (!dstate_getinfo("battery.voltage.nominal")) + { + snprintf(value, valuelen, "%.2f", BattV); + return 0; + } + + double nomBattV = 0.0; + nomBattV = strtod(dstate_getinfo("battery.voltage.nominal"), NULL); + upsdebugx(2, "battvoltact1: %.2f", nomBattV); + //return 0; + + double battvoltact = 0.0; + + if (ablerexQ5Vb > 0) { + battvoltact = ablerexQ5Vb * nomBattV / 1200; + } else { + if (BattV > 3.0) { + battvoltact = BattV; + } else { + battvoltact = BattV * 6 * nomBattV / 12; + } + } + + snprintf(value, valuelen, "%.2f", battvoltact); + upsdebugx(2, "battvoltact: %.2f / %.2f", battvoltact, BattV); + + return 0; +} + +static int ablerex_battery_charge(double BattIn) +{ + const double onlineP[] = { + 2.22, 2.21, 2.20, 2.19, 2.18, 2.17, 2.16, 2.15, 2.14, 2.13, + 2.12, 2.11, 2.10, 2.09, 2.08, 2.07, 2.06, 2.05, 2.04, 2.03, + 2.02, 2.01, 2.00, 1.99, 1.98, 1.97, 1.96, 1.95, 1.94, 1.93, + 1.92, 1.91, 1.90, 1.89, 1.88, 1.87, 1.86, 1.85, 1.84, 1.83, + 1.82, 1.81, 1.80, 1.79, 1.78, 1.77, 1.76, 1.75, 1.74, 1.73, + 1.72, 1.71, 1.70, 1.69, 1.68, 1.67 + }; + const int onlineC[] = { + 100, 90, 88, 87, 85, 83, 82, 80, 78, 77, + 75, 73, 72, 70, 68, 65, 65, 62, 62, 58, + 58, 55, 55, 53, 52, 50, 48, 47, 45, 43, + 42, 40, 38, 37, 35, 33, 32, 30, 28, 27, + 25, 23, 22, 20, 18, 17, 15, 13, 12, 10, + 8, 7, 5, 3, 2, 0, -1 + }; + const double offlineP[] = { + 13.5, 13.3, 13.2, 13.1, 13, 12.9, 12.8, 12.7, 12.6, 12.5, + 12.4, 12.3, 12.2, 12.1, 12, 11.9, 11.8, 11.7, 11.6, 11.5, + 11.4, 11.3, 11.2, 11.1, 11, 10.9, 10.8, 10.7, 10.6, 10.5, + 10.4, 10.3, 10.2, 10.1, 10 + }; + const int offlineC[] = { + 100, 90, 88, 86, 83, 80, 77, 74, 72, 69, + 66, 63, 61, 58, 55, 52, 49, 47, 44, 41, + 38, 36, 33, 30, 27, 24, 22, 19, 16, 13, + 11, 8, 5, 2, 0, -1 + }; + + int charge = 0; + int i; + + if (BattIn < 3.0) { + for (i = 0; onlineC[i] > 0; i++) { + if (BattIn >= onlineP[i]) { + charge = onlineC[i]; + break; + } + } + } else { + //double nomBattV = strtod(dstate_getinfo("battery.voltage.nominal"), NULL); + //double battV = BattIn / (nomBattV / 12); + + for (i = 0; offlineC[i] > 0; i++) { + if (BattIn >= offlineP[i]) { + charge = offlineC[i]; + break; + } + } + } + return charge; +} + +static int ablerex_batterycharge(item_t *item, char *value, const size_t valuelen) { + double BattV = 0.0; + BattV = strtod(item->value, NULL); + upsdebugx(2, "battvoltc2: %.2f", BattV); + if (!dstate_getinfo("battery.voltage.nominal")) + { + snprintf(value, valuelen, "%d", 100); + return 0; + } + + double nomBattV = 0.0; + nomBattV = strtod(dstate_getinfo("battery.voltage.nominal"), NULL); + upsdebugx(2, "battvv1: %.2f", nomBattV); + //return 0; + + if (BattV > 3.0) { + BattV = BattV / (nomBattV / 12); + } + int BattP = ablerex_battery_charge(BattV); + //dstate_setinfo("battery.charge", "%.0f", BattP); + + snprintf(value, valuelen, "%d", BattP); + upsdebugx(2, "battcharge: %d", BattP); + + return 0; +} + +static int ablerex_initbattery(item_t *item, char *value, const size_t valuelen) { + + double nomBattV = strtod(dstate_getinfo("battery.voltage.nominal"), NULL); + double batthigh = 0.0; + double battlow = 0.0; + + if (Q5_Vbc > 0) { + battlow = Q5_Vbc * nomBattV / 1200; + } else { + battlow = 960 * nomBattV / 1200; + } + batthigh = 1365 * nomBattV / 1200; + + switch (item->from) + { + case 1: + snprintf(value, valuelen, "%.2f", battlow); + upsdebugx(2, "BattLow: %.2f", battlow); + break; + case 2: + snprintf(value, valuelen, "%.2f", batthigh); + upsdebugx(2, "BattHigh: %.2f", batthigh); + break; + + default: + /* Don't know what happened */ + return -1; + } + + return 0; +} + +static int ablerex_At(item_t *item, char *value, const size_t valuelen) { + int RawValue = 0; + + RawValue = (unsigned char)item->answer[1] * 65536 * 256 + (unsigned char)item->answer[2] * 65536 + + (unsigned char)item->answer[3] * 256 + (unsigned char)item->answer[4]; + + snprintf(value, valuelen, "%d", RawValue); + upsdebugx(2, "At: %d", RawValue); + + return 0; +} + +static int ablerex_TR(item_t *item, char *value, const size_t valuelen) { + char TR[8]; + + TR[0] = item->answer[1]; + TR[1] = item->answer[2]; + TR[2] = item->answer[3]; + TR[3] = item->answer[4]; + TR[4] = 0; + + snprintf(value, valuelen, "%s", TR); + upsdebugx(2, "At: %s", TR); + + return 0; +} + +static int ablerex_process_status_bits(item_t *item, char *value, const size_t valuelen) +{ + char *val = ""; + + switch (item->from) + { + case 40: /* Bypass/Boost or Buck Active */ + + if (item->value[0] == '1') { + + double vi, vo; + + vi = strtod(dstate_getinfo("input.voltage"), NULL); + vo = strtod(dstate_getinfo("output.voltage"), NULL); + + if (item->value[2] == '1') {/* UPS Type is Standby (0 is On_line) */ + if (vo < 0.5 * vi) { + upsdebugx(2, "%s: output voltage too low", __func__); + return -1; + } else if (vo < 0.95 * vi) { + status_set("TRIM"); + } else if (vo < 1.05 * vi) { + status_set("BYPASS"); + } else if (vo < 1.5 * vi) { + status_set("BOOST"); + } else { + upsdebugx(2, "%s: output voltage too high", __func__); + return -1; + } + } else { + status_set("BYPASS"); + } + } + + break; + + case 41: /* UPS Failed - ups.alarm */ + + if (item->value[0] == '1') { /* Battery abnormal */ + status_set("RB"); + } + + double vout = strtod(dstate_getinfo("output.voltage"), NULL); + + if (vout < 50.0) { + status_set("OFF"); + } + break; + + default: + /* Don't know what happened */ + return -1; + } + + snprintf(value, valuelen, "%s", val); + + return 0; +} + +/* qx2nut lookup table */ +static item_t ablerex_qx2nut[] = { + + /* + * > [Q1\r] + * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] + * 01234567890123456789012345678901234567890123456 + * 0 1 2 3 4 + */ + + { "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, + { "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, + { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, + { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, + { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, ablerex_battery }, + { "battery.charge", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, ablerex_batterycharge }, + { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, + /* Status bits */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 42, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, ablerex_process_status_bits }, /* Ablerex Bypass/Boost or Buck Active */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, ablerex_process_status_bits }, /* Ablerex UPS Failed */ + { "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ + { "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ + { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ + + /* + * > [F\r] + * < [#220.0 000 024.0 50.0\r] + * 0123456789012345678901 + * 0 1 2 + */ + + { "output.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.1f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, + { "output.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, + { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, + { "output.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.1f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, + { "battery.voltage.low", 0, NULL, "F\r", "", 22, '#', "", 1, 2, "%.2f", QX_FLAG_QUICK_POLL, NULL, NULL, ablerex_initbattery }, + { "battery.voltage.high", 0, NULL, "F\r", "", 22, '#', "", 2, 3, "%.2f", QX_FLAG_QUICK_POLL, NULL, NULL, ablerex_initbattery }, + + /* Ablerex */ + { "output.frequency", 0, NULL, "Q5\r", "", 22, '(', "", 1, 18, "%.1f", 0, NULL, NULL, ablerex_Q5 }, + { "battery.runtime", 0, NULL, "At\r", "", 0, '(', "", 0, 0, "%d", 0, NULL, NULL, ablerex_At }, + //{ "ups.alarm", 0, NULL, "Q5\r", "", 22, '(', "", 1, 14, "%.0f", 0, QX_FLAG_QUICK_POLL, NULL, ablerex_Q5 }, + { "ups.test.result", 0, NULL, "TR\r", "", 0, '#', "", 0, 0, "%s", 0, NULL, NULL, ablerex_TR }, + //{ "output.current", 0, NULL, "Q5\r", "", 22, '(', "", 1, 16, "%.1f", 0, QX_FLAG_QUICK_POLL, NULL, ablerex_Q5 }, + + /* + * > [I\r] + * < [#------------- ------ VT12046Q \r] + * 012345678901234567890123456789012345678 + * 0 1 2 3 + */ + + { "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + + /* Instant commands */ + { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.off", 0, NULL, "S.2\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + /* Server-side settable vars */ + { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + + /* End of structure. */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + +/* Testing table */ +#ifdef TESTING +static testing_t ablerex_testing[] = { + { "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, + { "F\r", "#230.0 000 024.0 50.0\r", -1 }, + { "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 }, + { "Q\r", "", -1 }, + { "S03\r", "", -1 }, + { "C\r", "", -1 }, + { "S02R0005\r", "", -1 }, + { "S.5R0000\r", "", -1 }, + { "T04\r", "", -1 }, + { "TL\r", "", -1 }, + { "T\r", "", -1 }, + { "CT\r", "", -1 }, + { NULL } +}; +#endif /* TESTING */ + +/* Subdriver-specific initups */ +static void ablerex_initups(void) +{ + blazer_initups(ablerex_qx2nut); +} + +/* Subdriver interface */ +subdriver_t ablerex_subdriver = { + ABLEREX_VERSION, + blazer_claim, + ablerex_qx2nut, + ablerex_initups, + NULL, + blazer_makevartable, + "ACK", + NULL, +#ifdef TESTING + ablerex_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_ablerex.h b/drivers/nutdrv_qx_ablerex.h new file mode 100755 index 0000000..33c79c2 --- /dev/null +++ b/drivers/nutdrv_qx_ablerex.h @@ -0,0 +1,30 @@ +/* nutdrv_qx_ablerex.h - Subdriver for Ablerex Qx protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * 2021 Ablerex Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_ABLEREX_H +#define NUTDRV_QX_ABLEREX_H + +#include "nutdrv_qx.h" + +extern subdriver_t ablerex_subdriver; + +#endif /* NUTDRV_QX_ABLEREX_H */ diff --git a/drivers/nutdrv_qx_bestups.c b/drivers/nutdrv_qx_bestups.c new file mode 100644 index 0000000..adc102f --- /dev/null +++ b/drivers/nutdrv_qx_bestups.c @@ -0,0 +1,803 @@ +/* nutdrv_qx_bestups.c - Subdriver for Best Power/Sola Australia UPSes + * + * Copyright (C) + * 2014 Daniele Pezzini + * Based on: + * bestups.c - Copyright (C) + * 1999 Russell Kroll + * Jason White + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nut_float.h" +#include "nut_stdint.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_blazer-common.h" +#include "nutdrv_qx_bestups.h" + +#define BESTUPS_VERSION "BestUPS 0.06" + +/* Support functions */ +static int bestups_claim(void); +static void bestups_initups(void); +static void bestups_makevartable(void); + +/* Answer preprocess functions */ +static int bestups_preprocess_id_answer(item_t *item, const int len); + +/* Preprocess functions */ +static int bestups_process_setvar(item_t *item, char *value, const size_t valuelen); +static int bestups_process_bbb_status_bit(item_t *item, char *value, const size_t valuelen); +static int bestups_manufacturer(item_t *item, char *value, const size_t valuelen); +static int bestups_model(item_t *item, char *value, const size_t valuelen); +static int bestups_batt_runtime(item_t *item, char *value, const size_t valuelen); +static int bestups_batt_packs(item_t *item, char *value, const size_t valuelen); +static int bestups_get_pins_shutdown_mode(item_t *item, char *value, const size_t valuelen); +static int bestups_voltage_settings(item_t *item, char *value, const size_t valuelen); + +/* ups.conf settings */ +static int pins_shutdown_mode; + +/* General settings */ +static int inverted_bbb_bit = 0; + + +/* == Ranges/enums == */ + +/* Range for ups.delay.start */ +static info_rw_t bestups_r_ondelay[] = { + { "60", 0 }, + { "599940", 0 }, + { "", 0 } +}; + +/* Range for ups.delay.shutdown */ +static info_rw_t bestups_r_offdelay[] = { + { "12", 0 }, + { "5940", 0 }, + { "", 0 } +}; + +/* Range for number of battery packs */ +static info_rw_t bestups_r_batt_packs[] = { + { "0", 0 }, + { "5", 0 }, + { "", 0 } +}; + +/* Range for pin shutdown mode */ +static info_rw_t bestups_r_pins_shutdown_mode[] = { + { "0", 0 }, + { "6", 0 }, + { "", 0 } +}; + + +/* == qx2nut lookup table == */ +static item_t bestups_qx2nut[] = { + + /* Query UPS for status + * > [Q1\r] + * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] + * 01234567890123456789012345678901234567890123456 + * 0 1 2 3 4 + */ + + { "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, + { "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, + { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, + { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, + { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, + /* Status bits */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ + { "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ + { "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ +/* { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, *//* Beeper status: not supported; always 0 */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, bestups_process_bbb_status_bit }, /* Bypass/Boost or Buck Active - keep this one at the end as it needs the processed data from the previous items */ + + /* Query UPS for ratings and model infos + * > [ID\r] + * < [FOR,750,120,120,20.0,27.6\r] case #1: length = 26 + * < [FOR,1500,120,120,20.0,27.6\r] case #2: length = 27 + * < [FOR,3000,120,120,20.0,100.6\r] case #3: length = 28 + * < [FOR, 750,120,120,20.0, 27.6\r] after being preprocessed: length = 28 + * 0123456789012345678901234567 + * 0 1 2 + */ + + { "device.mfr", 0, NULL, "ID\r", "", 28, 0, "", 0, 2, "%s", QX_FLAG_STATIC, NULL, bestups_preprocess_id_answer, bestups_manufacturer }, + { "device.model", 0, NULL, "ID\r", "", 28, 0, "", 0, 2, "%s", QX_FLAG_STATIC, NULL, bestups_preprocess_id_answer, bestups_model }, + { "ups.power.nominal", 0, NULL, "ID\r", "", 28, 0, "", 4, 7, "%.0f", QX_FLAG_STATIC, NULL, bestups_preprocess_id_answer, NULL }, + { "input.voltage.nominal", 0, NULL, "ID\r", "", 28, 0, "", 9, 11, "%.0f", QX_FLAG_STATIC, NULL, bestups_preprocess_id_answer, NULL }, + { "output.voltage.nominal", 0, NULL, "ID\r", "", 28, 0, "", 13, 15, "%.0f", QX_FLAG_STATIC, NULL, bestups_preprocess_id_answer, NULL }, + { "battery.voltage.low", 0, NULL, "ID\r", "", 28, 0, "", 17, 20, "%.1f", QX_FLAG_SEMI_STATIC, NULL, bestups_preprocess_id_answer, NULL }, + { "battery.voltage.high", 0, NULL, "ID\r", "", 28, 0, "", 22, 26, "%.1f", QX_FLAG_SEMI_STATIC, NULL, bestups_preprocess_id_answer, NULL }, + + /* Query UPS for battery runtime (not available on the Patriot Pro/Sola 320 model series) + * > [RT\r] + * < [025\r] + * 0123 + * 0 + */ + + { "battery.runtime", 0, NULL, "RT\r", "", 4, 0, "", 0, 2, "%.0f", QX_FLAG_SKIP, NULL, NULL, bestups_batt_runtime }, + + /* Query UPS for number of battery packs (available only on the Axxium/Sola 620 model series) + * > [BP?\r] + * < [02\r] + * 012 + * 0 + */ + + { "battery.packs", ST_FLAG_RW, bestups_r_batt_packs, "BP?\r", "", 3, 0, "", 0, 1, "%d", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, bestups_batt_packs }, + + /* Set number of battery packs to n (integer, 0-5) (available only on the Axxium/Sola 620 model series) + * > [BPn\r] + * < [] + */ + + { "battery.packs", 0, bestups_r_batt_packs, "BP%.0f\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, bestups_process_setvar }, + + /* Query UPS for shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power's EPS-0059) + * > [SS?\r] + * < [0\r] + * 01 + * 0 + */ + + { "pins_shutdown_mode", ST_FLAG_RW, bestups_r_pins_shutdown_mode, "SS?\r", "", 2, 0, "", 0, 0, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT, NULL, NULL, bestups_get_pins_shutdown_mode }, + + /* Set shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power's EPS-0059) to n (integer, 0-6) + * > [SSn\r] + * < [] + */ + + { "pins_shutdown_mode", 0, bestups_r_pins_shutdown_mode, "SS%.0f\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, bestups_process_setvar }, + + /* Query UPS for voltage settings + * > [M\r] + * < [0\r] + * 01 + * 0 + */ + + { "input.transfer.low", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, + { "input.transfer.boost.low", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, + { "input.transfer.boost.high", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, + { "input.voltage.nominal", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, + { "output.voltage.nominal", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, + { "input.transfer.trim.low", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, + { "input.transfer.trim.high", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, + { "input.transfer.high", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "%d", 0, NULL, NULL, bestups_voltage_settings }, + + /* Instant commands */ + { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stayoff", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + /* Server-side settable vars */ + { "ups.delay.start", ST_FLAG_RW, bestups_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + { "ups.delay.shutdown", ST_FLAG_RW, bestups_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + + /* End of structure. */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + + +/* == Testing table == */ +#ifdef TESTING +static testing_t bestups_testing[] = { + { "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00100000\r", -1 }, + { "ID\r", "FOR,750,120,120,20.0,27.6\r", -1 }, + { "RT\r", "015\r", -1 }, + { "BP?\r", "02\r", -1 }, + { "BP1\r", "", -1 }, + { "SS?\r", "0\r", -1 }, + { "SS2\r", "", -1 }, + { "M\r", "0\r", -1 }, + { "S03\r", "", -1 }, + { "C\r", "", -1 }, + { "S02R0005\r", "", -1 }, + { "S.5R0001\r", "", -1 }, + { "T04\r", "", -1 }, + { "TL\r", "", -1 }, + { "T\r", "", -1 }, + { "CT\r", "", -1 }, + { NULL } +}; +#endif /* TESTING */ + + +/* == Support functions == */ + +/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ +static int bestups_claim(void) +{ + /* We need at least Q1 and ID to run this subdriver */ + + item_t *item = find_nut_info("input.voltage", 0, 0); + + /* Don't know what happened */ + if (!item) + return 0; + + /* No reply/Unable to get value */ + if (qx_process(item, NULL)) + return 0; + + /* Unable to process value */ + if (ups_infoval_set(item) != 1) + return 0; + + /* UPS Model */ + item = find_nut_info("device.model", 0, 0); + + /* Don't know what happened */ + if (!item) { + dstate_delinfo("input.voltage"); + return 0; + } + + /* No reply/Unable to get value */ + if (qx_process(item, NULL)) { + dstate_delinfo("input.voltage"); + return 0; + } + + /* Unable to process value */ + if (ups_infoval_set(item) != 1) { + dstate_delinfo("input.voltage"); + return 0; + } + + return 1; +} + +/* Subdriver-specific initups */ +static void bestups_initups(void) +{ + blazer_initups_light(bestups_qx2nut); +} + +/* Subdriver-specific flags/vars */ +static void bestups_makevartable(void) +{ + addvar(VAR_VALUE, "pins_shutdown_mode", "Set shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power's EPS-0059) to n (integer, 0-6)"); + + blazer_makevartable_light(); +} + + +/* == Answer preprocess functions == */ + +/* Preprocess the answer we got back from the UPS when queried with 'ID\r': make data begin always at the same indexes */ +static int bestups_preprocess_id_answer(item_t *item, const int len) +{ + int i; + char refined[SMALLBUF] = "", + rawval[SMALLBUF] = "", + *token, + *saveptr = NULL; + + if (len <= 0) + return len; + + if (len < 25 || len > 27) { + upsdebugx(4, "%s: wrong length [%s: %d]", __func__, item->info_type, len); + return -1; + } + + /* e.g.: + * 1. item->answer = "FOR,750,120,120,20.0,27.6\r"; len = 26 + * 2. item->answer = "FOR,1500,120,120,20.0,27.6\r"; len = 27 + * 3. item->answer = "FOR,3000,120,120,20.0,100.6\r"; len = 28 */ + upsdebugx(4, "read: '%.*s'", (int)strcspn(item->answer, "\r"), item->answer); + + snprintf(rawval, sizeof(rawval), "%s", item->answer); + + for (i = 1, token = strtok_r(rawval, ",", &saveptr); token != NULL; i++, token = strtok_r(NULL, ",", &saveptr)) { + + switch (i) + { + case 1: + snprintf(refined, sizeof(refined), "%s", token); + continue; + case 2: /* Output power */ + snprintfcat(refined, sizeof(refined), ",%4s", token); + continue; + case 6: /* Battery voltage at full charge (+ trailing CR) */ + snprintfcat(refined, sizeof(refined), ",%6s", token); + continue; + default: + snprintfcat(refined, sizeof(refined), ",%s", token); + } + + } + + if (i != 7 || strlen(refined) != 28) { + upsdebugx(2, "noncompliant reply: '%.*s'", (int)strcspn(refined, "\r"), refined); + return -1; + } + + upsdebugx(4, "read: '%.*s'", (int)strcspn(refined, "\r"), refined); + + /* e.g.: item->answer = "FOR, 750,120,120,20.0, 27.6\r"; len = 28 */ + return snprintf(item->answer, sizeof(item->answer), "%s", refined); +} + + +/* == Preprocess functions == */ + +/* *SETVAR(/NONUT)* Preprocess setvars */ +static int bestups_process_setvar(item_t *item, char *value, const size_t valuelen) +{ + if (!strlen(value)) { + upsdebugx(2, "%s: value not given for %s", __func__, item->info_type); + return -1; + } + + double val = strtod(value, NULL); + + if (!strcasecmp(item->info_type, "pins_shutdown_mode")) { + + if (d_equal(val, pins_shutdown_mode)) { + upslogx(LOG_INFO, "%s is already set to %.0f", item->info_type, val); + return -1; + } + + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->command, val); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Bypass/Boost or Buck status */ +static int bestups_process_bbb_status_bit(item_t *item, char *value, const size_t valuelen) +{ + /* Bypass/Boost/Buck bit is not reliable when a battery test, shutdown or on battery condition occurs: always ignore it in these cases */ + if (!((unsigned int)(qx_status()) & STATUS(OL)) || ((unsigned int)(qx_status()) & (STATUS(CAL) | STATUS(FSD)))) { + + if (item->value[0] == '1') + item->value[0] = '0'; + + return blazer_process_status_bits(item, value, valuelen); + + } + + /* UPSes with inverted bypass/boost/buck bit */ + if (inverted_bbb_bit) { + + if (item->value[0] == '1') + item->value[0] = '0'; + + else if (item->value[0] == '0') + item->value[0] = '1'; + + } + + return blazer_process_status_bits(item, value, valuelen); +} + +/* Identify UPS manufacturer */ +static int bestups_manufacturer(item_t *item, char *value, const size_t valuelen) +{ +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + + /* Best Power devices */ + if ( + !strcmp(item->value, "AX1") || + !strcmp(item->value, "FOR") || + !strcmp(item->value, "FTC") || + !strcmp(item->value, "PR2") || + !strcmp(item->value, "PRO") + ) { + snprintf(value, valuelen, item->dfl, "Best Power"); + return 0; + } + + /* Sola Australia devices */ + if ( + !strcmp(item->value, "325") || + !strcmp(item->value, "520") || + !strcmp(item->value, "620") + ) { + snprintf(value, valuelen, item->dfl, "Sola Australia"); + return 0; + } + + /* Unknown devices */ + snprintf(value, valuelen, item->dfl, "Unknown"); + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Identify UPS model and unskip qx2nut table's items accordingly */ +static int bestups_model(item_t *item, char *value, const size_t valuelen) +{ + item_t *unskip; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + + /* Best Power devices */ + + if (!strcmp(item->value, "AX1")) { + + snprintf(value, valuelen, item->dfl, "Axxium Rackmount"); + + } else if (!strcmp(item->value, "FOR")) { + + snprintf(value, valuelen, item->dfl, "Fortress"); + + } else if (!strcmp(item->value, "FTC")) { + + snprintf(value, valuelen, item->dfl, "Fortress Telecom"); + + } else if (!strcmp(item->value, "PR2")) { + + snprintf(value, valuelen, item->dfl, "Patriot Pro II"); + inverted_bbb_bit = 1; + + } else if (!strcmp(item->value, "PRO")) { + + snprintf(value, valuelen, item->dfl, "Patriot Pro"); + inverted_bbb_bit = 1; + + /* Sola Australia devices */ + } else if ( + !strcmp(item->value, "320") || + !strcmp(item->value, "325") || + !strcmp(item->value, "520") || + !strcmp(item->value, "525") || + !strcmp(item->value, "620") + ) { + + snprintf(value, valuelen, "Sola %s", item->value); + + /* Unknown devices */ + } else { + + snprintf(value, valuelen, item->dfl, "Unknown (%s)", item->value); + upslogx(LOG_INFO, "Unknown model detected - please report this ID: '%s'", item->value); + + } + + /* Unskip qx2nut table's items according to the UPS model */ + + /* battery.runtime var is not available on the Patriot Pro/Sola 320 model series: leave it skipped in these cases, otherwise unskip it */ + if (strcmp(item->value, "PRO") && strcmp(item->value, "320")) { + + unskip = find_nut_info("battery.runtime", 0, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + } + + /* battery.packs var is available only on the Axxium/Sola 620 model series: unskip it in these cases */ + if (!strcmp(item->value, "AX1") || !strcmp(item->value, "620")) { + + unskip = find_nut_info("battery.packs", 0, QX_FLAG_SETVAR); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Battery runtime */ +static int bestups_batt_runtime(item_t *item, char *value, const size_t valuelen) +{ + double runtime; + + if (strspn(item->value, "0123456789 .") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + + /* Battery runtime is reported by the UPS in minutes, NUT expects seconds */ + runtime = strtod(item->value, NULL) * 60; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, runtime); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Battery packs */ +static int bestups_batt_packs(item_t *item, char *value, const size_t valuelen) +{ + item_t *unskip; + + if (strspn(item->value, "0123456789 ") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, strtol(item->value, NULL, 10)); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + /* Unskip battery.packs setvar */ + unskip = find_nut_info("battery.packs", QX_FLAG_SETVAR, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + return 0; +} + +/* *NONUT* Get shutdown mode functionality of Pin 1 and Pin 7 on the UPS DB9 communication port (Per Best Power's EPS-0059) as set in the UPS */ +static int bestups_get_pins_shutdown_mode(item_t *item, char *value, const size_t valuelen) +{ + item_t *unskip; + long l; + + if (strspn(item->value, "0123456789") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + + l = strtol(item->value, NULL, 10); + if (l > INT_MAX) { + upsdebugx(2, "%s: pins_shutdown_mode out of range [%s: %s]", + __func__, item->info_type, item->value); + return -1; + } + pins_shutdown_mode = (int)l; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, pins_shutdown_mode); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + /* We were not asked by the user to change the value */ + if ((item->qxflags & QX_FLAG_NONUT) && !getval(item->info_type)) + return 0; + + /* Unskip setvar */ + unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + return 0; +} + +/* Voltage settings */ +static int bestups_voltage_settings(item_t *item, char *value, const size_t valuelen) +{ + long index; + int val; + const char *nominal_voltage; + const struct { + const int low; /* Low voltage -> input.transfer.low / input.transfer.boost.low */ + const int boost; /* Boost voltage -> input.transfer.boost.high */ + const int nominal; /* Nominal voltage -> input.voltage.nominal / output.voltage.nominal */ + const int buck; /* Buck voltage -> input.transfer.trim.low */ + const int high; /* High voltage -> input.transfer.high / input.transfer.trim.high */ + } voltage_settings[] = { + /* U models voltage limits, for: + * - Fortress (750U, 1050U, 1425U, 1800U and 2250U) + * - Fortress Rackmount (750, 1050, 1425, 1800, and 2250 VA) + * - Patriot Pro II (400U, 750U, and 1000U) */ + /* M low boost nominal buck high */ + /* 0 */ { 96, 109, 120, 130, 146 }, /* LEDs lit: 2,3,4 (Default) */ + /* 1 */ { 96, 109, 120, 138, 156 }, /* LEDs lit: 1,3,4 */ + /* 2 */ { 90, 104, 120, 130, 146 }, /* LEDs lit: 2,3,5 */ + /* 3 */ { 90, 104, 120, 138, 156 }, /* LEDs lit: 1,3,5 */ + /* 4 */ { 90, 104, 110, 120, 130 }, /* LEDs lit: 3,4,5 */ + /* 5 */ { 90, 104, 110, 130, 146 }, /* LEDs lit: 2,4,5 */ + /* 6 */ { 90, 96, 110, 120, 130 }, /* LEDs lit: 3,4,6 */ + /* 7 */ { 90, 96, 110, 130, 146 }, /* LEDs lit: 2,4,6 */ + /* 8 */ { 96, 109, 128, 146, 156 }, /* LEDs lit: 1,2,4 */ + /* 9 */ { 90, 104, 128, 146, 156 }, /* LEDs lit: 1,2,5 */ + + /* E models voltage limits, for: + * - Fortress (750E, 1050E, 1425E, and 2250E) + * - Fortress Rackmount (750, 1050, 1425, and 2250 VA) + * - Patriot Pro II (400E, 750E, and 1000E) */ + /* M low boost nominal buck high */ + /* 0 */ { 200, 222, 240, 250, 284 }, /* LEDs lit: 2,3,4 */ + /* 1 */ { 200, 222, 240, 264, 290 }, /* LEDs lit: 1,3,4 */ + /* 2 */ { 188, 210, 240, 250, 284 }, /* LEDs lit: 2,3,5 */ + /* 3 */ { 188, 210, 240, 264, 290 }, /* LEDs lit: 1,3,5 */ + /* 4 */ { 188, 210, 230, 244, 270 }, /* LEDs lit: 3,4,5 (Default) */ + /* 5 */ { 188, 210, 230, 250, 284 }, /* LEDs lit: 2,4,5 */ + /* 6 */ { 180, 200, 230, 244, 270 }, /* LEDs lit: 3,4,6 */ + /* 7 */ { 180, 200, 230, 250, 284 }, /* LEDs lit: 2,4,6 */ + /* 8 */ { 165, 188, 208, 222, 244 }, /* LEDs lit: 4,5,6 */ + /* 9 */ { 165, 188, 208, 244, 270 } /* LEDs lit: 3,5,6 */ + }; + + if (strspn(item->value, "0123456789") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + + index = strtol(item->value, NULL, 10); + + if (index < 0 || index > 9) { + upsdebugx(2, "%s: value '%ld' out of range [0..9]", __func__, index); + return -1; + } + + nominal_voltage = dstate_getinfo("input.voltage.nominal"); + + if (!nominal_voltage) + nominal_voltage = dstate_getinfo("output.voltage.nominal"); + + if (!nominal_voltage) { + upsdebugx(2, "%s: unable to get nominal voltage", __func__); + return -1; + } + + /* E models */ + if (strtol(nominal_voltage, NULL, 10) > 160) + index += 10; + + if (!strcasecmp(item->info_type, "input.transfer.low") || !strcasecmp(item->info_type, "input.transfer.boost.low")) { + + val = voltage_settings[index].low; + + } else if (!strcasecmp(item->info_type, "input.transfer.boost.high")) { + + val = voltage_settings[index].boost; + + } else if (!strcasecmp(item->info_type, "input.voltage.nominal") || !strcasecmp(item->info_type, "output.voltage.nominal")) { + + val = voltage_settings[index].nominal; + + } else if (!strcasecmp(item->info_type, "input.transfer.trim.low")) { + + val = voltage_settings[index].buck; + + } else if (!strcasecmp(item->info_type, "input.transfer.trim.high") || !strcasecmp(item->info_type, "input.transfer.high")) { + + val = voltage_settings[index].high; + + } else { + + /* Don't know what happened */ + return -1; + + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, val); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + + +/* == Subdriver interface == */ +subdriver_t bestups_subdriver = { + BESTUPS_VERSION, + bestups_claim, + bestups_qx2nut, + bestups_initups, + NULL, + bestups_makevartable, + NULL, + NULL, +#ifdef TESTING + bestups_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_bestups.h b/drivers/nutdrv_qx_bestups.h new file mode 100644 index 0000000..4f1e560 --- /dev/null +++ b/drivers/nutdrv_qx_bestups.h @@ -0,0 +1,29 @@ +/* nutdrv_qx_bestups.h - Subdriver for Best Power/Sola Australia UPSes + * + * Copyright (C) + * 2014 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_BESTUPS_H +#define NUTDRV_QX_BESTUPS_H + +#include "nutdrv_qx.h" + +extern subdriver_t bestups_subdriver; + +#endif /* NUTDRV_QX_BESTUPS_H */ diff --git a/drivers/nutdrv_qx_blazer-common.c b/drivers/nutdrv_qx_blazer-common.c new file mode 100644 index 0000000..2ff16bc --- /dev/null +++ b/drivers/nutdrv_qx_blazer-common.c @@ -0,0 +1,459 @@ +/* nutdrv_qx_blazer-common.c - Common functions/settings for nutdrv_qx_{mecer,megatec,megatec-old,mustek,q1,voltronic-qs,zinto}.{c,h} + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_blazer-common.h" + +/* == Ranges == */ + +/* Range for ups.delay.start */ +info_rw_t blazer_r_ondelay[] = { + { "0", 0 }, + { "599940", 0 }, + { "", 0 } +}; + +/* Range for ups.delay.shutdown */ +info_rw_t blazer_r_offdelay[] = { + { "12", 0 }, + { "600", 0 }, + { "", 0 } +}; + +/* == Support functions == */ + +/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ +int blazer_claim(void) +{ + /* To tell whether the UPS is supported or not, we'll check both status (Q1/QS/D) and vendor (I/FW?) - provided that we were not told not to do it with the ups.conf flag 'novendor'. */ + + item_t *item = find_nut_info("input.voltage", 0, 0); + + /* Don't know what happened */ + if (!item) + return 0; + + /* No reply/Unable to get value */ + if (qx_process(item, NULL)) + return 0; + + /* Unable to process value */ + if (ups_infoval_set(item) != 1) + return 0; + + if (testvar("novendor")) + return 1; + + /* Vendor */ + item = find_nut_info("ups.firmware", 0, 0); + + /* Don't know what happened */ + if (!item) { + dstate_delinfo("input.voltage"); + return 0; + } + + /* No reply/Unable to get value */ + if (qx_process(item, NULL)) { + dstate_delinfo("input.voltage"); + return 0; + } + + /* Unable to process value */ + if (ups_infoval_set(item) != 1) { + dstate_delinfo("input.voltage"); + return 0; + } + + return 1; +} + +/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. + * NOTE: this 'light' version only checks for status (Q1/QS/D/..) */ +int blazer_claim_light(void) +{ + /* To tell whether the UPS is supported or not, we'll check just status (Q1/QS/D/..). */ + + item_t *item = find_nut_info("input.voltage", 0, 0); + + /* Don't know what happened */ + if (!item) + return 0; + + /* No reply/Unable to get value */ + if (qx_process(item, NULL)) + return 0; + + /* Unable to process value */ + if (ups_infoval_set(item) != 1) + return 0; + + return 1; +} + +/* Subdriver-specific flags/vars */ +void blazer_makevartable(void) +{ + addvar(VAR_FLAG, "norating", "Skip reading rating information from UPS"); + addvar(VAR_FLAG, "novendor", "Skip reading vendor information from UPS"); + + blazer_makevartable_light(); +} + +/* Subdriver-specific flags/vars + * NOTE: this 'light' version only handles vars/flags related to UPS status query (Q1/QS/D/...) */ +void blazer_makevartable_light(void) +{ + addvar(VAR_FLAG, "ignoresab", "Ignore 'Shutdown Active' bit in UPS status"); +} + +/* Subdriver-specific initups */ +void blazer_initups(item_t *qx2nut) +{ + int nr, nv, isb; + item_t *item; + + nr = testvar("norating"); + nv = testvar("novendor"); + isb = testvar("ignoresab"); + + if (!nr && !nv && !isb) + return; + + for (item = qx2nut; item->info_type != NULL; item++) { + + if (!item->command) + continue; + + /* norating */ + if (nr && !strcasecmp(item->command, "F\r")) { + upsdebugx(2, "%s: skipping %s", __func__, item->info_type); + item->qxflags |= QX_FLAG_SKIP; + } + + /* novendor */ + if (nv && (!strcasecmp(item->command, "I\r") || !strcasecmp(item->command, "FW?\r"))) { + upsdebugx(2, "%s: skipping %s", __func__, item->info_type); + item->qxflags |= QX_FLAG_SKIP; + } + + /* ignoresab */ + if (isb && !strcasecmp(item->info_type, "ups.status") && item->from == 44 && item->to == 44) { + upsdebugx(2, "%s: skipping %s ('Shutdown Active' bit)", __func__, item->info_type); + item->qxflags |= QX_FLAG_SKIP; + } + + } +} + +/* Subdriver-specific initups + * NOTE: this 'light' version only checks for status (Q1/QS/D/..) related items */ +void blazer_initups_light(item_t *qx2nut) +{ + item_t *item; + + if (!testvar("ignoresab")) + return; + + for (item = qx2nut; item->info_type != NULL; item++) { + + if (strcasecmp(item->info_type, "ups.status") || item->from != 44 || item->to != 44) + continue; + + upsdebugx(2, "%s: skipping %s ('Shutdown Active' bit)", __func__, item->info_type); + item->qxflags |= QX_FLAG_SKIP; + break; + + } +} + +/* == Preprocess functions == */ + +/* Preprocess setvars */ +int blazer_process_setvar(item_t *item, char *value, const size_t valuelen) +{ + if (!strlen(value)) { + upsdebugx(2, "%s: value not given for %s", __func__, item->info_type); + return -1; + } + + if (!strcasecmp(item->info_type, "ups.delay.start")) { + + long ondelay = strtol(value, NULL, 10); + + if (ondelay < 0) { + upslogx(LOG_ERR, "%s: ondelay '%ld' should not be negative", + item->info_type, ondelay); + return -1; + } + + /* Truncate to minute */ + ondelay -= (ondelay % 60); + snprintf(value, valuelen, "%ld", ondelay); + + } else if (!strcasecmp(item->info_type, "ups.delay.shutdown")) { + + long offdelay = strtol(value, NULL, 10); + + if (offdelay < 0) { + upslogx(LOG_ERR, "%s: offdelay '%ld' should not be negative", + item->info_type, offdelay); + return -1; + } + + /* Truncate to nearest settable value */ + if (offdelay < 60) { + offdelay -= (offdelay % 6); + } else { + offdelay -= (offdelay % 60); + } + + snprintf(value, valuelen, "%ld", offdelay); + + } else { + + /* Don't know what happened */ + return -1; + + } + + return 0; +} + +/* Preprocess instant commands */ +int blazer_process_command(item_t *item, char *value, const size_t valuelen) +{ +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + + if (!strcasecmp(item->info_type, "shutdown.return")) { + + /* Sn: Shutdown after n minutes and then turn on when mains is back + * SnRm: Shutdown after n minutes and then turn on after m minutes + * Accepted values for n: .2 -> .9 , 01 -> 10 + * Accepted values for m: 0001 -> 9999 + * Note: "S01R0001" and "S01R0002" may not work on early (GE) firmware versions. + * The failure mode is that the UPS turns off and never returns. + * The fix is to push the return value up by 2, i.e. S01R0003, and it will return online properly. + * (thus the default of ondelay=3 mins) */ + + long offdelay = strtol(dstate_getinfo("ups.delay.shutdown"), NULL, 10), + ondelay = strtol(dstate_getinfo("ups.delay.start"), NULL, 10) / 60; + char buf[SMALLBUF] = ""; + + if (ondelay <= 0) { + + if (offdelay < 0) { + upslogx(LOG_ERR, "%s: offdelay '%ld' should not be negative", + item->info_type, offdelay); + return -1; + } + + if (offdelay < 60) { + snprintf(buf, sizeof(buf), ".%ld", offdelay / 6); + } else { + snprintf(buf, sizeof(buf), "%02ld", offdelay / 60); + } + + } else if (offdelay < 60) { + + if (offdelay < 0) { + upslogx(LOG_ERR, "%s: offdelay '%ld' should not be negative", + item->info_type, offdelay); + return -1; + } + + snprintf(buf, sizeof(buf), ".%ldR%04ld", offdelay / 6, ondelay); + + } else { + + if (offdelay < 0) { + upslogx(LOG_ERR, "%s: offdelay '%ld' should not be negative", + item->info_type, offdelay); + return -1; + } + + snprintf(buf, sizeof(buf), "%02ldR%04ld", offdelay / 60, ondelay); + + } + + snprintf(value, valuelen, item->command, buf); + + } else if (!strcasecmp(item->info_type, "shutdown.stayoff")) { + + /* SnR0000 + * Shutdown after n minutes and stay off + * Accepted values for n: .2 -> .9 , 01 -> 10 */ + + long offdelay = strtol(dstate_getinfo("ups.delay.shutdown"), NULL, 10); + char buf[SMALLBUF] = ""; + + if (offdelay < 0) { + upslogx(LOG_ERR, "%s: offdelay '%ld' should not be negative", + item->info_type, offdelay); + return -1; + } + + if (offdelay < 60) { + snprintf(buf, sizeof(buf), ".%ld", offdelay / 6); + } else { + snprintf(buf, sizeof(buf), "%02ld", offdelay / 60); + } + + snprintf(value, valuelen, item->command, buf); + + } else if (!strcasecmp(item->info_type, "test.battery.start")) { + + long delay = strlen(value) > 0 ? strtol(value, NULL, 10) : 600; + + if ((delay < 60) || (delay > 5940)) { + upslogx(LOG_ERR, "%s: battery test time '%ld' out of range [60..5940] seconds", item->info_type, delay); + return -1; + } + + delay = delay / 60; + + snprintf(value, valuelen, item->command, delay); + + } else { + + /* Don't know what happened */ + return -1; + + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Process status bits */ +int blazer_process_status_bits(item_t *item, char *value, const size_t valuelen) +{ + char *val = ""; + + if (strspn(item->value, "01") != 1) { + upsdebugx(3, "%s: unexpected value %s@%d->%s", __func__, item->value, item->from, item->value); + return -1; + } + + switch (item->from) + { + case 38: /* Utility Fail (Immediate) */ + + if (item->value[0] == '1') + val = "!OL"; + else + val = "OL"; + break; + + case 39: /* Battery Low */ + + if (item->value[0] == '1') + val = "LB"; + else + val = "!LB"; + break; + + case 40: /* Bypass/Boost or Buck Active */ + + if (item->value[0] == '1') { + + double vi, vo; + + vi = strtod(dstate_getinfo("input.voltage"), NULL); + vo = strtod(dstate_getinfo("output.voltage"), NULL); + + if (vo < 0.5 * vi) { + upsdebugx(2, "%s: output voltage too low", __func__); + return -1; + } else if (vo < 0.95 * vi) { + val = "TRIM"; + } else if (vo < 1.05 * vi) { + val = "BYPASS"; + } else if (vo < 1.5 * vi) { + val = "BOOST"; + } else { + upsdebugx(2, "%s: output voltage too high", __func__); + return -1; + } + + } + + break; + + case 41: /* UPS Failed - ups.alarm */ + + if (item->value[0] == '1') + val = "UPS selftest failed!"; + break; + + case 42: /* UPS Type - ups.type */ + + if (item->value[0] == '1') + val = "offline / line interactive"; + else + val = "online"; + break; + + case 43: /* Test in Progress */ + + if (item->value[0] == '1') + val = "CAL"; + else + val = "!CAL"; + break; + + case 44: /* Shutdown Active */ + + if (item->value[0] == '1') + val = "FSD"; + else + val = "!FSD"; + break; + + case 45: /* Beeper status - ups.beeper.status */ + + if (item->value[0] == '1') + val = "enabled"; + else + val = "disabled"; + break; + + default: + /* Don't know what happened */ + return -1; + } + + snprintf(value, valuelen, "%s", val); + + return 0; +} diff --git a/drivers/nutdrv_qx_blazer-common.h b/drivers/nutdrv_qx_blazer-common.h new file mode 100644 index 0000000..c331599 --- /dev/null +++ b/drivers/nutdrv_qx_blazer-common.h @@ -0,0 +1,44 @@ +/* nutdrv_qx_blazer-common.h - Common functions/settings for nutdrv_qx_{mecer,megatec,megatec-old,mustek,q1,voltronic-qs,zinto}.{c,h} + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_BLAZER_COMMON_H +#define NUTDRV_QX_BLAZER_COMMON_H + +#include "nutdrv_qx.h" + +/* Support functions */ +void blazer_makevartable(void); +void blazer_makevartable_light(void); +void blazer_initups(item_t *qx2nut); +void blazer_initups_light(item_t *qx2nut); +int blazer_claim(void); +int blazer_claim_light(void); + +/* Preprocess functions */ +int blazer_process_command(item_t *item, char *value, const size_t valuelen); +int blazer_process_setvar(item_t *item, char *value, const size_t valuelen); +int blazer_process_status_bits(item_t *item, char *value, const size_t valuelen); + +/* Ranges */ +extern info_rw_t blazer_r_ondelay[]; +extern info_rw_t blazer_r_offdelay[]; + +#endif /* NUTDRV_QX_BLAZER_COMMON_H */ diff --git a/drivers/nutdrv_qx_hunnox.c b/drivers/nutdrv_qx_hunnox.c new file mode 100644 index 0000000..f104479 --- /dev/null +++ b/drivers/nutdrv_qx_hunnox.c @@ -0,0 +1,138 @@ +/* nutdrv_qx_hunnox.c - Subdriver for Hunnox protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * 2020 Mariano Jan https://marianojan.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_blazer-common.h" + +#include "nutdrv_qx_hunnox.h" + +#define HUNNOX_VERSION "Hunnox 0.01" + +/* qx2nut lookup table */ +static item_t hunnox_qx2nut[] = { + + /* + * > [Q1\r] + * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] + * 01234567890123456789012345678901234567890123456 + * 0 1 2 3 4 + */ + + { "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, + { "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, + { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, + { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, + { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, + { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", QX_FLAG_QUICK_POLL, NULL, NULL, NULL }, + /* Status bits */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ + { "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ + { "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ + { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ + + /* + * > [F\r] + * < [#220.0 000 024.0 50.0\r] + * 0123456789012345678901 + * 0 1 2 + */ + + { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + /* + * > [FW?\r] + * < [#------------- ------ VT12046Q \r] + * 012345678901234567890123456789012345678 + * 0 1 2 3 + */ + + { "device.mfr", 0, NULL, "FW?\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "device.model", 0, NULL, "FW?\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "ups.firmware", 0, NULL, "FW?\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + + /* Instant commands */ + { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + /* Server-side settable vars */ + { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, "0", QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, "60", QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + + /* End of structure. */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + +/* Testing table */ +#ifdef TESTING +static testing_t hunnox_testing[] = { + { "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, + { "F\r", "#230.0 000 024.0 50.0\r", -1 }, + { "FW?\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 }, + { "Q\r", "", -1 }, + { "S03\r", "", -1 }, + { "C\r", "", -1 }, + { "S02R0005\r", "", -1 }, + { "S.5R0000\r", "", -1 }, + { "T04\r", "", -1 }, + { "TL\r", "", -1 }, + { "T\r", "", -1 }, + { "CT\r", "", -1 }, + { NULL } +}; +#endif /* TESTING */ + +/* Subdriver-specific initups */ +static void hunnox_initups(void) +{ + blazer_initups(hunnox_qx2nut); +} + +/* Subdriver interface */ +subdriver_t hunnox_subdriver = { + HUNNOX_VERSION, + blazer_claim, + hunnox_qx2nut, + hunnox_initups, + NULL, + blazer_makevartable, + "UPS No Ack", + NULL, +#ifdef TESTING + hunnox_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_hunnox.h b/drivers/nutdrv_qx_hunnox.h new file mode 100644 index 0000000..ca96465 --- /dev/null +++ b/drivers/nutdrv_qx_hunnox.h @@ -0,0 +1,30 @@ +/* nutdrv_qx_hunnox.h - Subdriver for Hunnox protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * 2020 Mariano Jan https://marianojan.com + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_HUNNOX_H +#define NUTDRV_QX_HUNNOX_H + +#include "nutdrv_qx.h" + +extern subdriver_t hunnox_subdriver; + +#endif /* NUTDRV_QX_HUNNOX_H */ diff --git a/drivers/nutdrv_qx_masterguard.c b/drivers/nutdrv_qx_masterguard.c new file mode 100644 index 0000000..08168f6 --- /dev/null +++ b/drivers/nutdrv_qx_masterguard.c @@ -0,0 +1,1090 @@ +/* nutdrv_qx_masterguard.c - Subdriver for Masterguard A/E Series + * + * Copyright (C) + * 2020-2021 Edgar Fuß , Mathematisches Institut der Universität Bonn + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version, or a 2-clause BSD License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nutdrv_qx.h" + +#include "nutdrv_qx_masterguard.h" +#include + +#define MASTERGUARD_VERSION "Masterguard 0.02" + +/* series (for un-SKIP) */ +static char masterguard_my_series = '?'; +/* slave address for commands that require it */ +static char masterguard_my_slaveaddr[3] = "??"; /* null-terminated for strtol() in claim() */ +/* next slaveaddr to use after the SS command (which needs the old one) has been run */ +static long masterguard_next_slaveaddr; +/* output current/power computation */ +static long masterguard_my_power = 0; +/* battery voltage computation */ +static long masterguard_my_numcells = 0; + + +/* ranges */ + +static info_rw_t masterguard_r_slaveaddr[] = { + { "0", NULL }, + { "99", NULL }, + { "" , NULL } +}; + +static info_rw_t masterguard_r_batpacks[] = { + { "0", NULL }, /* actually 1 for most models, see masterguard_model() */ + { "9", NULL }, /* varies across models, see masterguard_model() */ + { "" , NULL } +}; + +static info_rw_t masterguard_r_offdelay[] = { + { "0", NULL }, + { "5940", NULL }, /* 99*60 */ + { "" , NULL } +}; + +static info_rw_t masterguard_r_ondelay[] = { + { "0", NULL }, + { "599940", NULL }, /* 9999*60 */ + { "" , NULL } +}; + + +/* enums */ + +static info_rw_t *masterguard_e_outvolts = NULL; /* set in masterguard_output_voltages() */ + + +/* preprocess functions */ + +/* set masterguard_my_slaveaddr (for masterguard_add_slaveaddr) */ +static int masterguard_slaveaddr(item_t *item, char *value, const size_t valuelen) { + if (strlen(item->value) != 2) { + upsdebugx(2, "slaveaddr length not 2"); + return -1; + } + memcpy(masterguard_my_slaveaddr, item->value, 2); + if (valuelen >= 3) memcpy(value, item->value, 3); + return 0; +} + +/* set masterguard_my_series (for activating supported commands in masterguard_claim() */ +static int masterguard_series(item_t *item, char *value, const size_t valuelen) { + NUT_UNUSED_VARIABLE(valuelen); + + switch (item->value[0]) { + case 'A': + break; + case 'E': + break; + default: + upsdebugx(2, "unknown series %s", item->value); + return -1; + } + masterguard_my_series = item->value[0]; + memcpy(value, item->value, 2); + return 0; +} + +/* Convert strangely formatted model name in WH output + * (spaces, -19 only after battery packs) to something readable + * Also set min/max battery packs according to model */ +static int masterguard_model(item_t *item, char *value, const size_t valuelen) { + char *model; + int rack; + char min_bp, max_bp; + + rack = (strstr(item->value, "-19") != NULL); + if (strncmp(item->value, "A 700", 6) == 0) { + model = "A700"; + min_bp = 0; max_bp = 0; + } else if (strncmp(item->value, "A 1000", 6) == 0) { + model = "A1000"; + min_bp = 0; max_bp = 2; + } else if (strncmp(item->value, "A 2000", 6) == 0) { + model = "A2000"; + min_bp = rack ? 1 : 0; max_bp = rack ? 5 : 2; + } else if (strncmp(item->value, "A 3000", 6) == 0) { + model = "A3000"; + min_bp = rack ? 1 : 0; max_bp = rack ? 5 : 2; + } else if (strncmp(item->value, "E 60", 4) == 0) { + model = "E60"; + min_bp = 1; max_bp = 1; /* ??? */ + } else if (strncmp(item->value, "E100", 4) == 0) { + model = "E100"; + min_bp = 1; max_bp = 1; /* ??? */ + } else if (strncmp(item->value, "E200", 4) == 0) { + model = "E200"; + min_bp = 1; max_bp = 1; /* ??? */ + } else { + upsdebugx(2, "unknown T %s", item->value); + return -1; + } + masterguard_r_batpacks[0].value[0] = '0' + min_bp; + masterguard_r_batpacks[1].value[0] = '0' + max_bp; + snprintf(value, valuelen, "%s%s", model, rack ? "-19" : ""); + return 0; +} + +/* set masterguard_my_power (for power/current calculations) according to model */ +static int masterguard_power(item_t *item, char *value, const size_t valuelen) { + int p; + + if (strncmp(item->value, "A 700", 6) == 0) { + p = 700; + } else if (strncmp(item->value, "A 1000", 6) == 0) { + p = 1000; + } else if (strncmp(item->value, "A 2000", 6) == 0) { + p = 2000; + } else if (strncmp(item->value, "A 3000", 6) == 0) { + p = 3000; + } else if (strncmp(item->value, "E 60", 4) == 0) { + p = 6000; + } else if (strncmp(item->value, "E100", 4) == 0) { + p = 10000; + } else if (strncmp(item->value, "E200", 4) == 0) { + p = 20000; + } else { + upsdebugx(2, "unknown T %s", item->value); + return -1; + } + masterguard_my_power = p; + snprintf(value, valuelen, "%d", p); + return 0; +} + +/* convert mmm.ss to seconds */ +static int masterguard_mmm_ss(item_t *item, char *value, const size_t valuelen) { + int m, s; + + if (sscanf(item->value, "%d.%d", &m, &s) != 2) { + upsdebugx(2, "unparsable mmm.ss %s", item->value); + return -1; + } + snprintf(value, valuelen, "%d", 60*m + s); + return 0; +} + +/* convert hhh to seconds */ +static int masterguard_hhh(item_t *item, char *value, const size_t valuelen) { + int h; + if (sscanf(item->value, "%d", &h) != 1) { + upsdebugx(2, "unparsable hhh %s", item->value); + return -1; + } + snprintf(value, valuelen, "%d", 60*60*h); + return 0; +} + +/* convert TTTT:hh:mm:dd to seconds */ +static int masterguard_tttt_hh_mm_ss(item_t *item, char *value, const size_t valuelen) { + int t, h, m, s; + + if (sscanf(item->value, "%d:%d:%d:%d", &t, &h, &m, &s) != 4) { + upsdebugx(2, "unparsable TTTT:hh:mm:ss %s", item->value); + return -1; + } + snprintf(value, valuelen, "%d", 86400*t + 3600*h + 60*m + s); + return 0; +} + +/* set masterguard_my_numcells (for nominal battery voltage computation) */ +static int masterguard_numcells(item_t *item, char *value, const size_t valuelen) { + int v; + if (sscanf(item->value, "%d", &v) != 1) { + upsdebugx(2, "unparsable vvv %s", item->value); + return -1; + } + masterguard_my_numcells = v; + snprintf(value, valuelen, "%d", v); + return 0; +} + +/* compute nominal battery voltage */ +static int masterguard_battvolt(item_t *item, char *value, const size_t valuelen) { + float s; + if (sscanf(item->value, "%f", &s) != 1) { + upsdebugx(2, "unparsable ss.ss %s", item->value); + return -1; + } + snprintf(value, valuelen, "%.2f", masterguard_my_numcells * s); + return 0; +} + +/* compute output power from load percentage */ +static int masterguard_ups_power(item_t *item, char *value, const size_t valuelen) { + int q; + + if (sscanf(item->value, "%d", &q) != 1) { + upsdebugx(2, "unparsable qqq %s", item->value); + return -1; + } + snprintf(value, valuelen, "%.0f", q / 100.0 * masterguard_my_power + 0.5); + return 0; +} + +/* helper routine, not to be called from table */ +static int masterguard_output_current_fraction(item_t *item, char *value, const size_t valuelen, double fraction) { + NUT_UNUSED_VARIABLE(item); + + snprintf(value, valuelen, "%.2f", + fraction * masterguard_my_power / strtod(dstate_getinfo("output.voltage") , NULL) + 0.005); + return 0; +} + +/* compute output current from load percentage and output voltage */ +static int masterguard_output_current(item_t *item, char *value, const size_t valuelen) { + int q; + + if (sscanf(item->value, "%d", &q) != 1) { + upsdebugx(2, "unparsable qqq %s", item->value); + return -1; + } + return masterguard_output_current_fraction(item, value, valuelen, q/100.0); +} + +/* compute nominal output current from output voltage */ +static int masterguard_output_current_nominal(item_t *item, char *value, const size_t valuelen) { + return masterguard_output_current_fraction(item, value, valuelen, 1.0); +} + +/* digest status bits */ +static int masterguard_status(item_t *item, char *value, const size_t valuelen) { + int neg; + char *s; + + switch (item->value[0]) { + case '0': neg = 1; break; + case '1': neg = 0; break; + default: + upsdebugx(2, "unknown flag value %c", item->value[0]); + return -1; + } + switch (item->from) { + case 53: /* B7 */ s = "OL"; neg = !neg; break; + case 54: /* B6 */ s = "LB"; break; + case 55: /* B5 */ s = "BYPASS"; break; + case 56: /* B4 */ s = neg ? "" : "UPS Failed"; neg = 0; break; + case 57: /* B3 */ s = neg ? "online" : "offline"; neg = 0; break; + case 58: /* B2 */ s = "CAL"; break; + case 59: /* B1 */ s = "FSD"; break; + /* 60: blank */ + /* 61: B0 reserved */ + /* 62: T7 reserved */ + case 63: /* T6 */ s = neg ? "" : "problems in parallel operation mode"; neg = 0; break; + /* 64: T5 part of a parallel set */ + case 65: /* T4 */ s = "RB"; break; + case 66: /* T3 */ s = neg ? "" : "no battery connected"; neg = 0; break; + case 67: /* T210 */ + neg = 0; + if (strncmp(item->value, "000", 3) == 0) { + s = "no test in progress"; + } else if (strncmp(item->value, "001", 3) == 0) { + s = "in progress"; + } else if (strncmp(item->value, "010", 3) == 0) { + s = "OK"; + } else if (strncmp(item->value, "011", 3) == 0) { + s = "failed"; + } else if (strncmp(item->value, "100", 3) == 0) { + s = "not possible"; + } else if (strncmp(item->value, "101", 3) == 0) { + s = "aborted"; + } else if (strncmp(item->value, "110", 3) == 0) { + s = "autonomy time calibration in progress"; + } else if (strncmp(item->value, "111", 3) == 0) { + s = "unknown"; + } else { + upsdebugx(2, "unknown test result %s", item->value); + return -1; + } + break; + default: + upsdebugx(2, "unknown flag position %d", item->from); + return -1; + } + snprintf(value, valuelen, "%s%s", neg ? "!" : "", s); + return 0; +} + +/* convert beeper status bit to string required by NUT */ +static int masterguard_beeper_status(item_t *item, char *value, const size_t valuelen) { + switch (item->value[0]) { + case '0': + if (valuelen >= 9) + strcpy(value, "disabled"); + else + *value = '\0'; + break; + case '1': + if (valuelen >= 8) + strcpy(value, "enabled"); + else + *value = '\0'; + break; + default: + upsdebugx(2, "unknown beeper status %c", item->value[0]); + return -1; + } + return 0; +} + +/* parse list of available (nominal) output voltages into masterguard_w_outvolts enum */ +static int masterguard_output_voltages(item_t *item, char *value, const size_t valuelen) { + char sep[] = " "; + char *w; + size_t n = 0; + + strncpy(value, item->value, valuelen); /* save before strtok mangles it */ + for (w = strtok(item->value, sep); w; w = strtok(NULL, sep)) { + n++; + upsdebugx(4, "output voltage #%zu: %s", n, w); + if ((masterguard_e_outvolts = realloc(masterguard_e_outvolts, n * sizeof(info_rw_t))) == NULL) { + upsdebugx(1, "output voltages: allocating #%zu failed", n); + return -1; + } + strncpy(masterguard_e_outvolts[n - 1].value, w, SMALLBUF - 1); + masterguard_e_outvolts[n - 1].preprocess = NULL; + } + /* need to do this seperately in case the loop is run zero times */ + if ((masterguard_e_outvolts = realloc(masterguard_e_outvolts, (n + 1) * sizeof(info_rw_t))) == NULL) { + upsdebugx(1, "output voltages: allocating terminator after #%zu failed", n); + return -1; + } + masterguard_e_outvolts[n].value[0] = '\0'; + masterguard_e_outvolts[n].preprocess = NULL; + return 0; +} + +/* parse fault record string into readable form */ +static int masterguard_fault(item_t *item, char *value, const size_t valuelen) { + char c; + float f; + int t, h, m, s; + long l; + + if (sscanf(item->value, "%c %f %d:%d:%d:%d", &c, &f, &t, &h, &m, &s) != 6) { + upsdebugx(1, "unparsable fault record %s", item->value); + return -1; + } + l = 86400*t + 3600*h + 60*m + s; + snprintf(value, valuelen, "%ld: ", l); + switch (c) { + case '0': + snprintfcat(value, valuelen, "none"); + break; + case '1': + snprintfcat(value, valuelen, "bus fault (%.0fV)", f); + break; + case '2': + snprintfcat(value, valuelen, "inverter fault (%.0fV)", f); + break; + case '3': + snprintfcat(value, valuelen, "overheat fault (%.0fC)", f); + break; + case '4': + snprintfcat(value, valuelen, "battery overvoltage fault (%.2fV)", f); + break; + case '5': + snprintfcat(value, valuelen, "battery mode overload fault (%.0f%%)", f); + break; + case '6': + snprintfcat(value, valuelen, "bypass mode overload fault (%.0f%%)", f); + break; + case '7': + snprintfcat(value, valuelen, "inverter mode outpt short-circuit fault (%.0fV)", f); + break; + case '8': + snprintfcat(value, valuelen, "fan lock fault"); + break; + case '9': + snprintfcat(value, valuelen, "battery fault (%.0fV)", f); + break; + case 'A': + snprintfcat(value, valuelen, "charger fault"); + break; + case 'B': + snprintfcat(value, valuelen, "EPO activated"); + break; + case 'C': + snprintfcat(value, valuelen, "parallel error"); + break; + case 'D': + snprintfcat(value, valuelen, "MCU communication error"); + break; + case 'E': + case 'F': + upsdebugx(1, "reserved fault id %c", c); + return -1; + default: + upsdebugx(1, "unknown fault id %c", c); + return -1; + } + return 0; +} + + +/* pre-command preprocessing functions */ + +/* add slave address (from masterguard_my_slaveaddr) to commands that require it */ +static int masterguard_add_slaveaddr(item_t *item, char *command, const size_t commandlen) { + NUT_UNUSED_VARIABLE(item); + NUT_UNUSED_VARIABLE(commandlen); + + size_t l; + + l = strlen(command); + if (strncmp(command + l - 4, ",XX\r", 4) != 0) { + upsdebugx(1, "add slaveaddr: no ,XX\\r at end of command %s", command); + return -1; + } + upsdebugx(4, "add slaveaddr %s to command %s", masterguard_my_slaveaddr, command); + memcpy(command + l - 3, masterguard_my_slaveaddr, 2); + return 0; +} + + +/* instant command preprocessing functions */ + +/* helper, not to be called directly from table */ +/*!! use parameter from the value field instead of ups.delay.{shutdown,return}?? */ +static int masterguard_shutdown(item_t *item, char *value, const size_t valuelen, const int stayoff) { + NUT_UNUSED_VARIABLE(item); + + long offdelay; + char *p; + const char *val, *name; + char offstr[3]; + + offdelay = strtol((val = dstate_getinfo(name = "ups.delay.shutdown")), &p, 10); + if (*p != '\0') goto ill; + if (offdelay < 0) { + goto ill; + } else if (offdelay < 60) { + offstr[0] = '.'; + offstr[1] = '0' + (char)offdelay / 6; + } else if (offdelay <= 99*60) { + int m = (int)(offdelay / 60); + offstr[0] = '0' + (char)(m / 10); + offstr[1] = '0' + (char)(m % 10); + } else goto ill; + offstr[2] = '\0'; + if (stayoff) { + snprintf(value, valuelen, "S%s\r", offstr); + } else { + long ondelay; + + ondelay = strtol((val = dstate_getinfo(name = "ups.delay.start")), &p, 10); + if (*p != '\0') goto ill; + if (ondelay < 0 || ondelay > 9999*60) goto ill; + snprintf(value, valuelen, "S%sR%04ld\r", offstr, ondelay); + } + return 0; + +ill: + upsdebugx(2, "shutdown: illegal %s %s", name, val); + return -1; +} + +static int masterguard_shutdown_return(item_t *item, char *value, const size_t valuelen) { + return masterguard_shutdown(item, value, valuelen, 0); +} + +static int masterguard_shutdown_stayoff(item_t *item, char *value, const size_t valuelen) { + return masterguard_shutdown(item, value, valuelen, 1); +} + +static int masterguard_test_battery(item_t *item, char *value, const size_t valuelen) { + NUT_UNUSED_VARIABLE(item); + + long duration; + char *p; + + if (value[0] == '\0') { + upsdebugx(2, "battery test: no duration"); + return -1; + } + duration = strtol(value, &p, 10); + if (*p != '\0') goto ill; + if (duration == 10) { + strncpy(value, "T\r", valuelen); + return 0; + } + if (duration < 60 || duration > 99*60) goto ill; + snprintf(value, valuelen, "T%02ld\r", duration / 60); + return 0; + +ill: upsdebugx(2, "battery test: illegal duration %s", value); + return -1; +} + + +/* variable setting preprocessing functions */ + +/* set variable, input format specifier (d/f/s, thms) in item->dfl */ +static int masterguard_setvar(item_t *item, char *value, const size_t valuelen) { + char *p; + char t = 's'; + long i = 0; + double f = 0.0; + char s[80]; + + if (value[0] == '\0') { + upsdebugx(2, "setvar: no value"); + return -1; + } + if (!item->dfl || item->dfl[0] == '\0') { + upsdebugx(2, "setvar: no dfl"); + return -1; + } + if (item->dfl[1] == '\0') { + t = item->dfl[0]; + switch (t) { + case 'd': + i = strtol(value, &p, 10); + if (*p != '\0') goto ill; + break; + case 'f': + f = strtod(value, &p); + if (*p != '\0') { + goto ill; + } else if (errno) { + upsdebug_with_errno(2, "setvar: f value %s", value); + return -1; + } + break; + case 's': + /* copy to s to avoid snprintf()ing value to itself */ + if (strlen(value) >= sizeof s) goto ill; + strcpy(s, value); + break; + default: + upsdebugx(2, "setvar: unknown dfl %c", item->dfl[0]); + return -1; + } + } else if (strncmp(item->dfl, "thms", 4) == 0) { + int tt, h, m, sec; + if (sscanf(item->value, "%d:%d:%d:%d", &tt, &h, &m, &sec) == 4) { + if (tt < 0 || tt > 9999 || h < 0 || h > 23 || m < 0 || m > 59 || sec < 0 || sec > 59) goto ill; + } else { + long l; + char *pl; + + l = strtol(value, &pl, 10); + if (*pl != '\0') goto ill; + sec = l % 60; l /= 60; + m = l % 60; l /= 60; + h = l % 24; l /= 24; + if (l > 9999) goto ill; + tt = (int)l; + } + snprintf(s, sizeof s, "%04d:%02d:%02d:%02d", tt, h, m, sec); + } else { + upsdebugx(2, "setvar: unknown dfl %s", item->dfl); + return -1; + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + switch (t) { + case 'd': + snprintf(value, valuelen, item->command, i); + break; + case 'f': + snprintf(value, valuelen, item->command, f); + break; + case 's': + snprintf(value, valuelen, item->command, s); + break; + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + return 0; +ill: + upsdebugx(2, "setvar: illegal %s value %s", item->dfl, value); + return -1; +} + +/* record new slave address in masterguard_next_slaveaddr; moved to masterguard_my_slaveaddr in masterguard_new_slaveaddr() after the slaveaddr-changing command finished */ +static int masterguard_set_slaveaddr(item_t *item, char *value, const size_t valuelen) { + char *p; + + masterguard_next_slaveaddr = strtol(value, &p, 10); + if (*p != '\0') { + upsdebugx(2, "set_slaveaddr: illegal value %s", value); + return -1; + } + upsdebugx(3, "next slaveaddr %ld", masterguard_next_slaveaddr); + return masterguard_setvar(item, value, valuelen); +} + + +/* variable setting answer preprocessing functions */ + +/* set my_slaveaddr to next_slaveaddr /after/ issuing the SS command (which, itself, needs the /old/ slaveaddr) */ +static int masterguard_new_slaveaddr(item_t *item, const int len) { + NUT_UNUSED_VARIABLE(item); + + upsdebugx(3, "saved slaveaddr %ld", masterguard_next_slaveaddr); + if (masterguard_next_slaveaddr < 0 || masterguard_next_slaveaddr > 99) { + upsdebugx(2, "%s: illegal value %ld", __func__, masterguard_next_slaveaddr); + return -1; + } + masterguard_my_slaveaddr[0] = '0' + (char)(masterguard_next_slaveaddr / 10); + masterguard_my_slaveaddr[1] = '0' + (char)(masterguard_next_slaveaddr % 10); + upsdebugx(3, "new slaveaddr %s", masterguard_my_slaveaddr); + return len; +} + + +/* qx2nut lookup table */ +static item_t masterguard_qx2nut[] = { + /* static values */ + + /* type flags rw command answer len leading value from to dfl qxflags precmd preans preproc */ + { "device.mfr", 0, NULL, "", "", 0, '\0', "", 0, 0, "Masterguard", QX_FLAG_STATIC | QX_FLAG_ABSENT,NULL, NULL, NULL }, + { "load.high", 0, NULL, "", "", 0, '\0', "", 0, 0, "140", QX_FLAG_STATIC | QX_FLAG_ABSENT,NULL, NULL, NULL }, + /* battery.charge.low */ + /* battery.charge.warning */ + { "battery.type", 0, NULL, "", "", 0, '\0', "", 0, 0, "PbAc", QX_FLAG_STATIC | QX_FLAG_ABSENT,NULL, NULL, NULL }, + + + /* variables */ + + /* + * > [WH\r] + * < [(XX VV.VV PP.PP TTTTTTTTTTTTTTTTTTTTTTTTTTTTTT B MMM FF.FF VVV SS.SS HHH.hh GGG.gg RRR mm nn MMM NNN FF.FF FF.FF\r] + * 01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012 + * 0 1 2 3 4 5 6 7 8 9 0 1 + * (00 10.06 03.09 A 700 + 0 Bat Pack-19 0 230 50.00 012 02.30 006.00 012.00 018 10 40 160 276 47.00 53.00 + */ + /* type flags rw command answer len leading value from to dfl qxflags precmd preans preproc */ + { "ups.id", ST_FLAG_RW, masterguard_r_slaveaddr,"WH\r", "", 113, '(', "", 1, 2, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE, NULL, NULL, masterguard_slaveaddr }, + { "ups.firmware", 0, NULL, "WH\r", "", 113, '(', "", 4, 8, "%s", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "ups.firmware.aux", 0, NULL, "WH\r", "", 113, '(', "", 10, 14, "%s", QX_FLAG_STATIC, NULL, NULL, NULL }, + /* several values are deduced from the T field */ + { "experimental.series", 0, NULL, "WH\r", "", 113, '(', "", 16, 16, "%s", QX_FLAG_STATIC | QX_FLAG_NONUT, NULL, NULL, masterguard_series }, + { "device.model", 0, NULL, "WH\r", "", 113, '(', "", 16, 45, "%s", QX_FLAG_STATIC, NULL, NULL, masterguard_model }, + { "ups.power.nominal", 0, NULL, "WH\r", "", 113, '(', "", 16, 45, "%s", QX_FLAG_STATIC, NULL, NULL, masterguard_power }, +/* not used, use GS instead because the value is settable + { "battery.packs", 0, NULL, "WH\r", "", 113, '(', "", 47, 47, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, +*/ + { "input.voltage.nominal", 0, NULL, "WH\r", "", 113, '(', "", 49, 51, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.frequency.nominal", 0, NULL, "WH\r", "", 113, '(', "", 53, 57, "%.2f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "experimental.number_of_battery_cells", 0, NULL, "WH\r", "", 113, '(', "", 59, 61, "%.0f", QX_FLAG_STATIC | QX_FLAG_NONUT, NULL, NULL, masterguard_numcells }, + { "experimental.nominal_cell_voltage", 0, NULL, "WH\r", "", 113, '(', "", 63, 67, "%.2f", QX_FLAG_STATIC | QX_FLAG_NONUT, NULL, NULL, NULL }, + { "battery.voltage.nominal", 0, NULL, "WH\r", "", 113, '(', "", 63, 67, "%.2f", QX_FLAG_STATIC, NULL, NULL, masterguard_battvolt}, + { "experimental.runtime_half", 0, NULL, "WH\r", "", 113, '(', "", 69, 74, "%.0f", QX_FLAG_STATIC | QX_FLAG_NONUT, NULL, NULL, masterguard_mmm_ss }, + { "experimental.runtime_full", 0, NULL, "WH\r", "", 113, '(', "", 76, 81, "%.0f", QX_FLAG_STATIC | QX_FLAG_NONUT, NULL, NULL, masterguard_mmm_ss }, + { "experimental.recharge_time", 0, NULL, "WH\r", "", 113, '(', "", 83, 85, "%.0f", QX_FLAG_STATIC | QX_FLAG_NONUT, NULL, NULL, masterguard_hhh }, +/*!! what's the difference between low/high and low.critical/high.critical?? */ + { "ambient.0.temperature.low", 0, NULL, "WH\r", "", 113, '(', "", 87, 88, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "ambient.0.temperature.high", 0, NULL, "WH\r", "", 113, '(', "", 90, 91, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.voltage.low.critical", 0, NULL, "WH\r", "", 113, '(', "", 93, 95, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.voltage.high.critical",0, NULL, "WH\r", "", 113, '(', "", 97, 99, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.frequency.low", 0, NULL, "WH\r", "", 113, '(', "", 101, 105, "%.2f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.frequency.high", 0, NULL, "WH\r", "", 113, '(', "", 107, 111, "%.2f", QX_FLAG_STATIC, NULL, NULL, NULL }, + + /* + * > [Q3\r] + * 76543210 76543210 + * < [(XX MMM.M NNN.N PPP.P QQQ RR.R SS.SS TT.T ttt.tt CCC BBBBBBBB TTTTTTTT\r] + * 01234567890123456789012345678901234567890123456789012345678901234567890 + * 0 1 2 3 4 5 6 7 + * (00 225.9 225.9 229.3 043 50.0 02.27 23.4 017.03 100 00000000 00000000 + * (01 226.9 226.9 226.9 039 50.0 02.30 21.8 000.00 000 01100000 00011000 + */ + /* type flags rw command answer len leading value from to dfl qxflags precmd preans preproc */ + { "input.voltage", 0, NULL, "Q3\r", "", 71, '(', "", 4, 8, "%.1f", 0, NULL, NULL, NULL }, + { "experimental.input_fault_voltage", 0, NULL, "Q3\r", "", 71, '(', "", 10, 14, "%.1f", QX_FLAG_NONUT, NULL, NULL, NULL }, + { "output.voltage", 0, NULL, "Q3\r", "", 71, '(', "", 16, 20, "%.1f", 0, NULL, NULL, NULL }, + { "ups.load", 0, NULL, "Q3\r", "", 71, '(', "", 22, 24, "%.0f", 0, NULL, NULL, NULL }, + { "ups.power", 0, NULL, "Q3\r", "", 71, '(', "", 22, 24, "%.0f", 0, NULL, NULL, masterguard_ups_power }, + { "output.current", 0, NULL, "Q3\r", "", 71, '(', "", 22, 24, "%f", 0, NULL, NULL, masterguard_output_current }, + { "input.frequency", 0, NULL, "Q3\r", "", 71, '(', "", 26, 29, "%.1f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q3\r", "", 71, '(', "", 31, 35, "%.1f", 0, NULL, NULL, masterguard_battvolt }, + { "ups.temperature", 0, NULL, "Q3\r", "", 71, '(', "", 37, 40, "%.1f", 0, NULL, NULL, NULL }, +/*!! report both ups.temperature and ambient.0.temperature?? */ + { "ambient.0.temperature", 0, NULL, "Q3\r", "", 71, '(', "", 37, 40, "%.1f", 0, NULL, NULL, NULL }, + { "battery.runtime", 0, NULL, "Q3\r", "", 71, '(', "", 42, 47, "%.0f", 0, NULL, NULL, masterguard_mmm_ss }, + { "battery.charge", 0, NULL, "Q3\r", "", 71, '(', "", 49, 51, "%.0f", 0, NULL, NULL, NULL }, + /* Status bits, first half (B) */ + { "ups.status", 0, NULL, "Q3\r", "", 71, '(', "", 53, 53, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, masterguard_status }, /* B7: Utility Fail */ + { "ups.status", 0, NULL, "Q3\r", "", 71, '(', "", 54, 54, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, masterguard_status }, /* B6: Battery Low */ + { "ups.status", 0, NULL, "Q3\r", "", 71, '(', "", 55, 55, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, masterguard_status }, /* B5: Bypass/Boost Active */ + { "ups.alarm", 0, NULL, "Q3\r", "", 71, '(', "", 56, 56, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, masterguard_status }, /* B4: UPS Failed */ + { "ups.type", 0, NULL, "Q3\r", "", 71, '(', "", 57, 57, NULL, QX_FLAG_STATIC, NULL, NULL, masterguard_status }, /* B3: UPS Type */ + { "ups.status", 0, NULL, "Q3\r", "", 71, '(', "", 58, 58, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, masterguard_status }, /* B2: Test in Progress */ + { "ups.status", 0, NULL, "Q3\r", "", 71, '(', "", 59, 59, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, masterguard_status }, /* B1: Shutdown Active */ + /* unused */ /* B0: unused */ + /* Status bits, second half (T) */ + /* unused */ /* T7: unused */ + { "ups.alarm", 0, NULL, "Q3\r", "", 69, '(', "", 63, 63, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, masterguard_status }, /* T6: problems in parallel operation mode */ + /* part of a parallel set */ /* T5: is part of a parallel set */ + { "ups.status", 0, NULL, "Q3\r", "", 71, '(', "", 65, 65, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, masterguard_status }, /* T4: Battery: end of service life */ + { "ups.alarm", 0, NULL, "Q3\r", "", 71, '(', "", 66, 66, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, masterguard_status }, /* T3: battery connected */ + { "ups.test.result", 0, NULL, "Q3\r", "", 71, '(', "", 67, 69, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, masterguard_status }, /* T210: Test Status */ + + /* + * > [GS,XX\r] + * < [(XX,ii p a\r] + * 01234567890 + * 0 1 + * (00,00 0 1 + */ + /* type flags rw command answer len leading value from to dfl qxflags precmd preans preproc */ + /* ups.id obtained via WH */ + { "ups.id", 0, NULL, "SS%02d--,XX\r","", 0, '\0', "", 0, 0, "d", QX_FLAG_SETVAR | QX_FLAG_RANGE, masterguard_add_slaveaddr, masterguard_new_slaveaddr, masterguard_set_slaveaddr }, + { "battery.packs", ST_FLAG_RW, masterguard_r_batpacks, "GS,XX\r", "", 0, '(', "", 7, 7, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE, masterguard_add_slaveaddr, NULL, NULL }, + { "battery.packs", 0, NULL, "SS--%1d-,XX\r","", 0, '\0', "", 0, 0, "d", QX_FLAG_SETVAR | QX_FLAG_RANGE, masterguard_add_slaveaddr, NULL, masterguard_setvar }, +/*!! which QX_FLAGs to use?? (changed by instcmd) */ + { "ups.beeper.status", 0, NULL, "GS,XX\r", "", 11, '(', "", 9, 9, NULL, QX_FLAG_SEMI_STATIC, masterguard_add_slaveaddr, NULL, masterguard_beeper_status }, + /* set with beeper.{en,dis}able */ + + /* + * > [GBS,XX\r] + * < [(XX,CCC hhhh HHHH AAAA BBBB DDDD EEE SS.SS\r] + * 0123456789012345678901234567890123456789012 + * 0 1 2 3 4 + * (00,100 0017 0000 0708 0712 0994 115 02.28 + * (01,000 0000 0360 0708 0712 0994 076 02.30 + */ + /* type flags rw command answer len leading value from to dfl qxflags precmd preans preproc */ + { "battery.charge", 0, NULL, "GBS,XX\r", "", 43, '(', "", 4, 6, "%.0f", 0, masterguard_add_slaveaddr, NULL, NULL }, + /* + * hhhh: hold time (minutes) + * HHHH: recharge time to 90% (minutes) + * AAAA: Ageing factor (promilles) + * BBBB: Ageing factor time dependant (promilles) + * DDDD: Ageing factor cyclic use (promilles) + * EEE: Calibration factor (percent) + * SS.SS: Actual battery cell voltage + */ + + /* + * > [GSN,XX\r] + * < [(XX,SSN=SSSSSSSSSSSnnnnn\r] + * 0123456789012345678901234 + * 0 1 2 + * (00,SSN=6A1212 2782 + */ + /* type flags rw command answer len leading value from to dfl qxflags precmd preans preproc */ + { "device.part", 0, NULL, "GSN,XX\r", "", 25, '(', "", 8, 18, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, masterguard_add_slaveaddr, NULL, NULL }, + { "device.serial", 0, NULL, "GSN,XX\r", "", 25, '(', "", 20, 23, "%s", QX_FLAG_STATIC, masterguard_add_slaveaddr, NULL, NULL }, + + /* + * > [DRC,XX\r] + * < [(XX,TTTT:hh:mm:ss\r] + * 012345678901234567 + * 0 1 + * (00,1869:19:06:37 + */ + /* type flags rw command answer len leading value from to dfl qxflags precmd preans preproc */ + /* this is not really the uptime, but the running time since last maintenance */ + { "device.uptime", ST_FLAG_RW, NULL, "DRC,XX\r", "", 17, '(', "", 4, 16, "%.0f", QX_FLAG_SEMI_STATIC, masterguard_add_slaveaddr, NULL, masterguard_tttt_hh_mm_ss }, + { "device.uptime", 0, NULL, "SRC%s,XX\r", "", 0, '\0', "", 0, 0, "thms", QX_FLAG_SETVAR, masterguard_add_slaveaddr, NULL, masterguard_setvar }, + + /* + * > [MSO\r] + * < [(220 230 240\r] + * 0123456789012 + * 0 1 + * (220 230 240 + */ + /* type flags rw command answer len leading value from to dfl qxflags precmd preans preproc */ + { "experimental.output_voltages", 0, NULL, "MSO\r", "", 5, '(', "", 1, 0, "%s", QX_FLAG_STATIC | QX_FLAG_NONUT, NULL, NULL, masterguard_output_voltages }, + + /* + * > [PNV\r] + * < [(PNV=nnn\r] + * 012345678 + * 0 + * (PNV=230 + */ + /* type flags rw command answer len leading value from to dfl qxflags precmd preans preproc */ + { "output.voltage.nominal", ST_FLAG_RW, NULL /* see claim */, "PNV\r", "", 8, '(', "", 5, 7, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM, NULL, NULL, NULL }, + { "output.voltage.nominal", 0, NULL, "PNV=%03d\r", "", 0, '\0', "", 0, 0, "d", QX_FLAG_SETVAR, NULL, NULL, masterguard_setvar }, + { "output.current.nominal", 0, NULL, "PNV\r", "", 8, '(', "", 5, 7, "%.0f", QX_FLAG_SEMI_STATIC, NULL, NULL, masterguard_output_current_nominal }, + + /* + * > [FLT,XX\r] + * < [(XX,A aaaa TTTT:hh:mm:ss B bbbb TTTT:hh:mm:ss C cccc TTTT:hh:mm:ss D dddd TTTT:hh:mm:ss E eeee TTTT:hh:mm:ss\r + * 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 + * 0 1 2 3 4 5 6 7 8 9 0 + * (00,7 0043 0000:16:48:06 0 0000 0000:00:00:00 0 0000 0000:00:00:00 0 0000 0000:00:00:00 0 0000 0000:00:00:00 + * (01,9 0010 1780:14:57:19 7 0046 0000:21:14:41 0 0000 0000:00:00:00 0 0000 0000:00:00:00 0 0000 0000:00:00:00 + */ + /* type flags rw command answer len leading value from to dfl qxflags precmd preans preproc */ + { "experimental.fault_1", 0, NULL, "FLT,XX\r", "", 108, '(', "", 4, 23, "%s", QX_FLAG_NONUT, masterguard_add_slaveaddr, NULL, masterguard_fault }, + { "experimental.fault_2", 0, NULL, "FLT,XX\r", "", 108, '(', "", 25, 44, "%s", QX_FLAG_NONUT, masterguard_add_slaveaddr, NULL, masterguard_fault }, + { "experimental.fault_3", 0, NULL, "FLT,XX\r", "", 108, '(', "", 46, 65, "%s", QX_FLAG_NONUT, masterguard_add_slaveaddr, NULL, masterguard_fault }, + { "experimental.fault_4", 0, NULL, "FLT,XX\r", "", 108, '(', "", 67, 86, "%s", QX_FLAG_NONUT, masterguard_add_slaveaddr, NULL, masterguard_fault }, + { "experimental.fault_5", 0, NULL, "FLT,XX\r", "", 108, '(', "", 88, 107, "%s", QX_FLAG_NONUT, masterguard_add_slaveaddr, NULL, masterguard_fault }, + + + /* instant commands */ + /* type flags rw command answer len leading value from to dfl qxflags precmd preans preproc */ +/*!! what's the difference between load.off.delay and shutdown.stayoff?? */ +#if 0 + { "load.off", 0, NULL, "S.0\r", "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "C\r", "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + /* load.off.delay */ + /* load.on.delay */ +#endif + { "shutdown.return", 0, NULL, NULL, "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, masterguard_shutdown_return }, + { "shutdown.stayoff", 0, NULL, NULL, "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, masterguard_shutdown_stayoff }, + { "shutdown.stop", 0, NULL, "C\r", "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + /* shutdown.reboot */ + /* shutdown.reboot.graceful */ + /* test.panel.start */ + /* test.panel.stop */ + /* test.failure.start */ + /* test.failure.stop */ + { "test.battery.start", 0, NULL, NULL, "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, masterguard_test_battery }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.deep", 0, NULL, "TUD\r", "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.stop", 0, NULL, "CT\r", "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + /* test.system.start */ + /* calibrate.start */ + /* calibrate.stop */ + { "bypass.start", 0, NULL, "FOFF\r", "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "bypass.stop", 0, NULL, "FON\r", "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + /* reset.input.minmax */ + /* reset.watchdog */ + { "beeper.enable", 0, NULL, "SS---1,XX\r", "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, masterguard_add_slaveaddr, NULL, NULL }, + { "beeper.disable", 0, NULL, "SS---0,XX\r", "", 0, '\0', "", 0, 0, NULL, QX_FLAG_CMD, masterguard_add_slaveaddr, NULL, NULL }, + /* beeper.mute */ + /* beeper.toggle */ + /* outlet.* */ + + + /* server variables */ + /* type flags rw command answer len leading value from to dfl qxflags precmd preans preproc */ + { "ups.delay.shutdown", ST_FLAG_RW, masterguard_r_offdelay, NULL, "", 0, '\0', "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, NULL }, + { "ups.delay.start", ST_FLAG_RW, masterguard_r_ondelay, NULL, "", 0, '\0', "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, NULL }, + + + /* end marker */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + + +/*!! todo + +untested: +Sxx (.n/nn) +C after S.0 + +unused: +>G01,00 +(00,00000 + +additional E series commands: +PSR +BUS* +V +INVDC + +use ups.{delay,timer}.{start,reboot,shutdown}? +report ups.contacts? +how to report battery.charger.status? +set battery.packs.bad? + +how to report battery aeging/run time? +how to report nominal hold time at half/full load? + +*/ + + +/* commands supported by A series */ +static char *masterguard_commands_a[] = { + "Q", "Q1", "Q3", "T", "TL", "S", "C", "CT", "WH", "M", "N", "O", "DECO", "DRC", "SRC", "FLT", "FCLR", "G", "SS", "GS", "MSO", "PNV", "FOFF", "FON", "TUD", "GBS", "SSN", "GSN", NULL +}; + +/* commands supported by E series */ +static char *masterguard_commands_e[] = { + "Q", "Q1", "Q3", "PSR", "T", "TL", "S", "C", "CT", "WH", "DRC", "SRC", "FLT", "FCLR", "SS", "GS", "MSO", "PNV", "FOFF", "FON", "TUD", "GBS", "SSN", "GSN", "BUS", "V", "INVDC", "BUSP", "BUSN", NULL +}; + +/* claim function. fetch some mandatory values, + * disable unsupported commands, + * set enum for supported output voltages */ +static int masterguard_claim(void) { + item_t *item; + /* mandatory values */ + char *mandatory[] = { + "series", /* SKIP */ + "device.model", /* minimal number of battery packs */ + "ups.power.nominal", /* load computation */ + "ups.id", /* slave address */ + "output_voltages", /* output voltages enum */ +#if 0 + "battery.packs", /* battery voltage computation */ +#endif + NULL + }; + char **sp; + long config_slaveaddr; + char *sa; + char **commands; + + if ((sa = getval("slave_address")) != NULL) { + char *p; + + if (*sa == '\0') { + upsdebugx(2, "claim: empty slave_address"); + return 0; + } + config_slaveaddr = strtol(sa, &p, 10); + if (*p != '\0' || config_slaveaddr < 0 || config_slaveaddr > 99) { + upsdebugx(2, "claim: illegal slave_address %s", sa); + return 0; + } + } else { + config_slaveaddr = -1; + } + for (sp = mandatory; *sp != NULL; sp++) { + char value[SMALLBUF] = ""; + + if ((item = find_nut_info(*sp, 0, QX_FLAG_SETVAR)) == NULL) { + upsdebugx(2, "claim: cannot find %s", *sp); + return 0; + } + /* since qx_process_answer() is not exported, there's no way + * to avoid sending the same command to the UPS again */ + if (qx_process(item, NULL) < 0) { + upsdebugx(2, "claim: cannot process %s", *sp); + return 0; + } + /* only call the preprocess function; don't call ups_infoval_set() + * because that does a dstate_setinfo() before dstate_setflags() + * is called (via qx_set_var() in qx_ups_walk() with QX_WALKMODE_INIT); + * that leads to r/w vars ending up r/o. */ + if (item->preprocess == NULL ) { + upsdebugx(2, "claim: no preprocess function for %s", *sp); + return 0; + } + if (item->preprocess(item, value, sizeof value)) { + upsdebugx(2, "claim: failed to preprocess %s", *sp); + return 0; + } + } + + if (config_slaveaddr >= 0 && config_slaveaddr != strtol(masterguard_my_slaveaddr, NULL, 10)) { + upsdebugx(2, "claim: slave address mismatch: want %02ld, have %s", config_slaveaddr, masterguard_my_slaveaddr); + return 0; + } + + switch (masterguard_my_series) { + case 'A': + commands = masterguard_commands_a; + break; + case 'E': + commands = masterguard_commands_e; + break; + default: + return 0; + } + + /* set SKIP flag for unimplemented commands */ + for (item = masterguard_qx2nut; item->info_type != NULL; item++) { + int match = 0; + if (item->command == NULL || item->command[0] == '\0') continue; + for (sp = commands; sp != NULL; sp++) { + const char *p = *sp, *q = item->command; + + while (1) { + if (*p == '\0' && (*q < 'A' || *q > 'Z')) { + match = 1; break; + } else if (*p == '\0' || *q < 'A' || *q > 'Z' || *p != *q) { + match = 0; break; + } + p++; q++; + } + if (match) break; + } + if (nut_debug_level >= 3) { + char cmd[10]; + char *p = cmd; const char *q = item->command; + while (*q >= 'A' && *q <= 'Z') { + *p++ = *q++; + if (p - cmd >= (ptrdiff_t)sizeof cmd - 1) break; + } + *p++ = '\0'; + upsdebugx(3, "command %s %simplemented", cmd, match ? "" : "NOT "); + + } + if (!match) + item->qxflags |= QX_FLAG_SKIP; + } + + /* set enum for output.voltage.nominal */ + if ((item = find_nut_info("output.voltage.nominal", QX_FLAG_ENUM, QX_FLAG_SETVAR)) == NULL) { + upsdebugx(2, "claim: cannot find output.voltage.nominal"); + return 0; + } + item->info_rw = masterguard_e_outvolts; + + return 1; +} + + +static void masterguard_makevartable(void) { + addvar(VAR_VALUE, "series", "Series (A/E)"); + addvar(VAR_VALUE, "slave_address", "Slave address (UPS id) to match"); + addvar(VAR_VALUE, "input_fault_voltage", "Input fault voltage (whatever that means)"); + addvar(VAR_VALUE, "number_of_battery_cells", "Number of battery cells in series"); + addvar(VAR_VALUE, "nominal_cell_voltage", "Nominal battery cell voltage"); + addvar(VAR_VALUE, "runtime_half", "Nominal battery run time at 50% load (seconds)"); + addvar(VAR_VALUE, "runtime_full", "Nominal battery run time at 100% load (seconds)"); + addvar(VAR_VALUE, "recharge_time", "Nominal battery recharge time to 95% capacity (seconds)"); + addvar(VAR_VALUE, "output_voltages", "Possible output voltages (volts)"); + addvar(VAR_VALUE, "fault_1", "Fault record 1 (newest)"); + addvar(VAR_VALUE, "fault_2", "Fault record 2"); + addvar(VAR_VALUE, "fault_3", "Fault record 3"); + addvar(VAR_VALUE, "fault_4", "Fault record 4"); + addvar(VAR_VALUE, "fault_5", "Fault record 5 (oldest)"); +} + + +#ifdef TESTING +static testing_t masterguard_testing[] = { + { NULL } +}; +#endif /* TESTING */ + +subdriver_t masterguard_subdriver = { + MASTERGUARD_VERSION, + masterguard_claim, + masterguard_qx2nut, + NULL, /* initups */ + NULL, /* intinfo */ + masterguard_makevartable, + NULL, /* accepted */ + NULL, /* rejected */ +#ifdef TESTING + masterguard_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_masterguard.h b/drivers/nutdrv_qx_masterguard.h new file mode 100644 index 0000000..71b7f95 --- /dev/null +++ b/drivers/nutdrv_qx_masterguard.h @@ -0,0 +1,29 @@ +/* nutdrv_qx_masterguard.h - Subdriver for Masterguard A/E Series + * + * Copyright (C) + * 2020-2021 Edgar Fuß + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version, or a 2-clause BSD License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_MASTERGUARD_H +#define NUTDRV_QX_MASTERGUARD_H + +#include "nutdrv_qx.h" + +extern subdriver_t masterguard_subdriver; + +#endif /* NUTDRV_QX_MASTERGUARD_H */ diff --git a/drivers/nutdrv_qx_mecer.c b/drivers/nutdrv_qx_mecer.c new file mode 100644 index 0000000..d044f33 --- /dev/null +++ b/drivers/nutdrv_qx_mecer.c @@ -0,0 +1,308 @@ +/* nutdrv_qx_mecer.c - Subdriver for Mecer/Voltronic Power P98 UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_blazer-common.h" + +#include "nutdrv_qx_mecer.h" + +#define MECER_VERSION "Mecer 0.07" + +/* Support functions */ +static int mecer_claim(void); +static void mecer_initups(void); + +/* Preprocess functions */ +static int voltronic_p98_protocol(item_t *item, char *value, const size_t valuelen); +static int mecer_process_test_battery(item_t *item, char *value, const size_t valuelen); + + +/* == qx2nut lookup table == */ +static item_t mecer_qx2nut[] = { + + /* Query UPS for protocol (Voltronic Power UPSes) + * > [QPI\r] + * < [(PI98\r] + * 012345 + * 0 + */ + + { "ups.firmware.aux", 0, NULL, "QPI\r", "", 6, '(', "", 1, 4, "%s", QX_FLAG_STATIC, NULL, NULL, voltronic_p98_protocol }, + + /* + * > [Q1\r] + * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] + * 01234567890123456789012345678901234567890123456 + * 0 1 2 3 4 + */ + + { "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, + { "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, + { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, + { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, + { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, + /* Status bits */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ + { "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ + { "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ + { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ + + /* + * > [F\r] + * < [#220.0 000 024.0 50.0\r] + * 0123456789012345678901 + * 0 1 2 + */ + + { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + + /* + * > [I\r] + * < [#------------- ------ VT12046Q \r] + * 012345678901234567890123456789012345678 + * 0 1 2 3 + */ + + { "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + + /* Instant commands + * The UPS will reply '(ACK\r' in case of success, '(NAK\r' if the command is rejected or invalid */ + + { "beeper.toggle", 0, NULL, "Q\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.off", 0, NULL, "S00R0000\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "C\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "shutdown.return", 0, NULL, "S%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stop", 0, NULL, "C\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start", 0, NULL, "T%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, mecer_process_test_battery }, + { "test.battery.start.deep", 0, NULL, "TL\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.stop", 0, NULL, "CT\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + /* Server-side settable vars */ + { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + + /* End of structure. */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + + +/* == Testing table == */ +#ifdef TESTING +static testing_t mecer_testing[] = { + { "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, + { "QPI\r", "(PI98\r", -1 }, + { "F\r", "#230.0 000 024.0 50.0\r", -1 }, + { "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 }, + { "Q\r", "(ACK\r", -1 }, + { "S03\r", "(NAK\r", -1 }, + { "C\r", "(NAK\r", -1 }, + { "S02R0005\r", "(ACK\r", -1 }, + { "S.5R0000\r", "(ACK\r", -1 }, + { "T04\r", "(NAK\r", -1 }, + { "TL\r", "(ACK\r", -1 }, + { "T\r", "(NAK\r", -1 }, + { "CT\r", "(ACK\r", -1 }, + { NULL } +}; +#endif /* TESTING */ + + +/* == Support functions == */ + +/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ +static int mecer_claim(void) +{ + /* Apart from status (Q1), try to identify protocol (QPI, for Voltronic Power P98 units) or whether the UPS uses '(ACK\r'/'(NAK\r' replies */ + + item_t *item = find_nut_info("input.voltage", 0, 0); + + /* Don't know what happened */ + if (!item) + return 0; + + /* No reply/Unable to get value */ + if (qx_process(item, NULL)) + return 0; + + /* Unable to process value */ + if (ups_infoval_set(item) != 1) + return 0; + + /* UPS Protocol */ + item = find_nut_info("ups.firmware.aux", 0, 0); + + /* Don't know what happened */ + if (!item) { + dstate_delinfo("input.voltage"); + return 0; + } + + /* No reply/Unable to get value/Command rejected */ + if (qx_process(item, NULL)) { + + /* No reply/Command echoed back or rejected with something other than '(NAK\r' -> Not a '(ACK/(NAK' unit */ + if (!strlen(item->answer) || strcasecmp(item->answer, "(NAK\r")) { + dstate_delinfo("input.voltage"); + return 0; + } + + /* Command rejected with '(NAK\r' -> '(ACK/(NAK' unit */ + + /* Skip protocol query from now on */ + item->qxflags |= QX_FLAG_SKIP; + + } else { + + /* Unable to process value/Command echoed back or rejected with something other than '(NAK\r'/Protocol not supported */ + if (ups_infoval_set(item) != 1) { + dstate_delinfo("input.voltage"); + return 0; + } + + /* Voltronic Power P98 unit */ + + } + + return 1; +} + +/* Subdriver-specific initups */ +static void mecer_initups(void) +{ + blazer_initups(mecer_qx2nut); +} + + +/* == Preprocess functions == */ + +/* Protocol used by the UPS */ +static int voltronic_p98_protocol(item_t *item, char *value, const size_t valuelen) +{ + if (strcasecmp(item->value, "PI98")) { + upslogx(LOG_ERR, "Protocol [%s] is not supported by this driver", item->value); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, "Voltronic Power P98"); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* *CMD* Preprocess 'test.battery.start' instant command */ +static int mecer_process_test_battery(item_t *item, char *value, const size_t valuelen) +{ + const char *protocol = dstate_getinfo("ups.firmware.aux"); + char buf[SMALLBUF] = ""; + long min, test_time; + + /* Voltronic P98 units -> Accepted values for test time: .2 -> .9 (.2=12sec ..), 01 -> 99 (minutes) -> range = [12..5940] */ + if (protocol && !strcasecmp(protocol, "Voltronic Power P98")) + min = 12; + /* Other units: 01 -> 99 (minutes) -> [60..5940] */ + else + min = 60; + + if (strlen(value) != strspn(value, "0123456789")) { + upslogx(LOG_ERR, "%s: non numerical value [%s]", item->info_type, value); + return -1; + } + + test_time = (strlen(value) > 0) ? strtol(value, NULL, 10) : 600; + + if ((test_time < min) || (test_time > 5940)) { + upslogx(LOG_ERR, + "%s: battery test time '%ld' out of range [%ld..5940] seconds", + item->info_type, test_time, min); + return -1; + } + + /* test time < 1 minute */ + if (test_time < 60) { + + test_time = test_time / 6; + snprintf(buf, sizeof(buf), ".%ld", test_time); + + /* test time > 1 minute */ + } else { + + test_time = test_time / 60; + snprintf(buf, sizeof(buf), "%02ld", test_time); + + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->command, buf); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + + +/* == Subdriver interface == */ +subdriver_t mecer_subdriver = { + MECER_VERSION, + mecer_claim, + mecer_qx2nut, + mecer_initups, + NULL, + blazer_makevartable, + "ACK", + "(NAK\r", +#ifdef TESTING + mecer_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_mecer.h b/drivers/nutdrv_qx_mecer.h new file mode 100644 index 0000000..7ad2914 --- /dev/null +++ b/drivers/nutdrv_qx_mecer.h @@ -0,0 +1,29 @@ +/* nutdrv_qx_mecer.h - Subdriver for Mecer/Voltronic Power P98 UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_MECER_H +#define NUTDRV_QX_MECER_H + +#include "nutdrv_qx.h" + +extern subdriver_t mecer_subdriver; + +#endif /* NUTDRV_QX_MECER_H */ diff --git a/drivers/nutdrv_qx_megatec-old.c b/drivers/nutdrv_qx_megatec-old.c new file mode 100644 index 0000000..4b6fdde --- /dev/null +++ b/drivers/nutdrv_qx_megatec-old.c @@ -0,0 +1,138 @@ +/* nutdrv_qx_megatec-old.c - Subdriver for Megatec/old protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_blazer-common.h" + +#include "nutdrv_qx_megatec-old.h" + +#define MEGATEC_OLD_VERSION "Megatec/old 0.07" + +/* qx2nut lookup table */ +static item_t megatec_old_qx2nut[] = { + + /* + * > [D\r] + * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] + * 01234567890123456789012345678901234567890123456 + * 0 1 2 3 4 + */ + + { "input.voltage", 0, NULL, "D\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, + { "input.voltage.fault", 0, NULL, "D\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, + { "output.voltage", 0, NULL, "D\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, + { "ups.load", 0, NULL, "D\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, + { "input.frequency", 0, NULL, "D\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "D\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "ups.temperature", 0, NULL, "D\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, + /* Status bits */ + { "ups.status", 0, NULL, "D\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ + { "ups.status", 0, NULL, "D\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ + { "ups.status", 0, NULL, "D\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ + { "ups.alarm", 0, NULL, "D\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ + { "ups.type", 0, NULL, "D\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ + { "ups.status", 0, NULL, "D\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ + { "ups.status", 0, NULL, "D\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ + { "ups.beeper.status", 0, NULL, "D\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ + + /* + * > [F\r] + * < [#220.0 000 024.0 50.0\r] + * 0123456789012345678901 + * 0 1 2 + */ + + { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + + /* + * > [I\r] + * < [#------------- ------ VT12046Q \r] + * 012345678901234567890123456789012345678 + * 0 1 2 3 + */ + + { "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + + /* Instant commands */ + { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + /* Server-side settable vars */ + { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + + /* End of structure. */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + +/* Testing table */ +#ifdef TESTING +static testing_t megatec_old_testing[] = { + { "D\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, + { "F\r", "#230.0 000 024.0 50.0\r", -1 }, + { "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 }, + { "Q\r", "", -1 }, + { "S03\r", "", -1 }, + { "C\r", "", -1 }, + { "S02R0005\r", "", -1 }, + { "S.5R0000\r", "", -1 }, + { "T04\r", "", -1 }, + { "TL\r", "", -1 }, + { "T\r", "", -1 }, + { "CT\r", "", -1 }, + { NULL } +}; +#endif /* TESTING */ + +/* Subdriver-specific initups */ +static void megatec_old_initups(void) +{ + blazer_initups(megatec_old_qx2nut); +} + +/* Subdriver interface */ +subdriver_t megatec_old_subdriver = { + MEGATEC_OLD_VERSION, + blazer_claim_light, + megatec_old_qx2nut, + megatec_old_initups, + NULL, + blazer_makevartable, + "ACK", + NULL, +#ifdef TESTING + megatec_old_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_megatec-old.h b/drivers/nutdrv_qx_megatec-old.h new file mode 100644 index 0000000..dd2b4aa --- /dev/null +++ b/drivers/nutdrv_qx_megatec-old.h @@ -0,0 +1,29 @@ +/* nutdrv_qx_megatec-old.h - Subdriver for Megatec/old protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_MEGATEC_OLD_H +#define NUTDRV_QX_MEGATEC_OLD_H + +#include "nutdrv_qx.h" + +extern subdriver_t megatec_old_subdriver; + +#endif /* NUTDRV_QX_MEGATECH_OLD_H */ diff --git a/drivers/nutdrv_qx_megatec.c b/drivers/nutdrv_qx_megatec.c new file mode 100644 index 0000000..e22979a --- /dev/null +++ b/drivers/nutdrv_qx_megatec.c @@ -0,0 +1,138 @@ +/* nutdrv_qx_megatec.c - Subdriver for Megatec protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_blazer-common.h" + +#include "nutdrv_qx_megatec.h" + +#define MEGATEC_VERSION "Megatec 0.06" + +/* qx2nut lookup table */ +static item_t megatec_qx2nut[] = { + + /* + * > [Q1\r] + * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] + * 01234567890123456789012345678901234567890123456 + * 0 1 2 3 4 + */ + + { "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, + { "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, + { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, + { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, + { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, + /* Status bits */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ + { "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ + { "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ + { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ + + /* + * > [F\r] + * < [#220.0 000 024.0 50.0\r] + * 0123456789012345678901 + * 0 1 2 + */ + + { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + + /* + * > [I\r] + * < [#------------- ------ VT12046Q \r] + * 012345678901234567890123456789012345678 + * 0 1 2 3 + */ + + { "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + + /* Instant commands */ + { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + /* Server-side settable vars */ + { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + + /* End of structure. */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + +/* Testing table */ +#ifdef TESTING +static testing_t megatec_testing[] = { + { "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, + { "F\r", "#230.0 000 024.0 50.0\r", -1 }, + { "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 }, + { "Q\r", "", -1 }, + { "S03\r", "", -1 }, + { "C\r", "", -1 }, + { "S02R0005\r", "", -1 }, + { "S.5R0000\r", "", -1 }, + { "T04\r", "", -1 }, + { "TL\r", "", -1 }, + { "T\r", "", -1 }, + { "CT\r", "", -1 }, + { NULL } +}; +#endif /* TESTING */ + +/* Subdriver-specific initups */ +static void megatec_initups(void) +{ + blazer_initups(megatec_qx2nut); +} + +/* Subdriver interface */ +subdriver_t megatec_subdriver = { + MEGATEC_VERSION, + blazer_claim, + megatec_qx2nut, + megatec_initups, + NULL, + blazer_makevartable, + "ACK", + NULL, +#ifdef TESTING + megatec_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_megatec.h b/drivers/nutdrv_qx_megatec.h new file mode 100644 index 0000000..be8dfd6 --- /dev/null +++ b/drivers/nutdrv_qx_megatec.h @@ -0,0 +1,29 @@ +/* nutdrv_qx_megatec.h - Subdriver for Megatec protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_MEGATEC_H +#define NUTDRV_QX_MEGATEC_H + +#include "nutdrv_qx.h" + +extern subdriver_t megatec_subdriver; + +#endif /* NUTDRV_QX_MEGATECH_H */ diff --git a/drivers/nutdrv_qx_mustek.c b/drivers/nutdrv_qx_mustek.c new file mode 100644 index 0000000..7990b41 --- /dev/null +++ b/drivers/nutdrv_qx_mustek.c @@ -0,0 +1,138 @@ +/* nutdrv_qx_mustek.c - Subdriver for Mustek protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_blazer-common.h" + +#include "nutdrv_qx_mustek.h" + +#define MUSTEK_VERSION "Mustek 0.07" + +/* qx2nut lookup table */ +static item_t mustek_qx2nut[] = { + + /* + * > [QS\r] + * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] + * 01234567890123456789012345678901234567890123456 + * 0 1 2 3 4 + */ + + { "input.voltage", 0, NULL, "QS\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, + { "input.voltage.fault", 0, NULL, "QS\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, + { "output.voltage", 0, NULL, "QS\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, + { "ups.load", 0, NULL, "QS\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, + { "input.frequency", 0, NULL, "QS\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "QS\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "ups.temperature", 0, NULL, "QS\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, + /* Status bits */ + { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ + { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ + { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ + { "ups.alarm", 0, NULL, "QS\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ + { "ups.type", 0, NULL, "QS\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ + { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ + { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ + { "ups.beeper.status", 0, NULL, "QS\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ + + /* + * > [F\r] + * < [#220.0 000 024.0 50.0\r] + * 0123456789012345678901 + * 0 1 2 + */ + + { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + + /* + * > [I\r] + * < [#------------- ------ VT12046Q \r] + * 012345678901234567890123456789012345678 + * 0 1 2 3 + */ + + { "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + + /* Instant commands */ + { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + /* Server-side settable vars */ + { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + + /* End of structure. */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + +/* Testing table */ +#ifdef TESTING +static testing_t mustek_testing[] = { + { "QS\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, + { "F\r", "#230.0 000 024.0 50.0\r", -1 }, + { "I\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 }, + { "Q\r", "", -1 }, + { "S03\r", "", -1 }, + { "C\r", "", -1 }, + { "S02R0005\r", "", -1 }, + { "S.5R0000\r", "", -1 }, + { "T04\r", "", -1 }, + { "TL\r", "", -1 }, + { "T\r", "", -1 }, + { "CT\r", "", -1 }, + { NULL } +}; +#endif /* TESTING */ + +/* Subdriver-specific initups */ +static void mustek_initups(void) +{ + blazer_initups(mustek_qx2nut); +} + +/* Subdriver interface */ +subdriver_t mustek_subdriver = { + MUSTEK_VERSION, + blazer_claim_light, + mustek_qx2nut, + mustek_initups, + NULL, + blazer_makevartable, + "ACK", + NULL, +#ifdef TESTING + mustek_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_mustek.h b/drivers/nutdrv_qx_mustek.h new file mode 100644 index 0000000..7bc41cd --- /dev/null +++ b/drivers/nutdrv_qx_mustek.h @@ -0,0 +1,29 @@ +/* nutdrv_qx_mustek.h - Subdriver for Mustek protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_MUSTEK_H +#define NUTDRV_QX_MUSTEK_H + +#include "nutdrv_qx.h" + +extern subdriver_t mustek_subdriver; + +#endif /* NUTDRV_QX_MUSTEK_H */ diff --git a/drivers/nutdrv_qx_q1.c b/drivers/nutdrv_qx_q1.c new file mode 100644 index 0000000..08073bc --- /dev/null +++ b/drivers/nutdrv_qx_q1.c @@ -0,0 +1,123 @@ +/* nutdrv_qx_q1.c - Subdriver for Q1 protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * NOTE: + * This subdriver implements the same protocol as the one used by the 'megatec' subdriver minus the vendor (I) and ratings (F) queries. + * In the claim function: + * - it doesn't even try to get 'vendor' information (I) + * - it checks only status (Q1), through 'input.voltage' variable + * Therefore it should be able to work even if the UPS doesn't support vendor/ratings *and* the user doesn't use the 'novendor'/'norating' flags, as long as: + * - the UPS replies a Q1-compliant answer (i.e. not necessary filled with all of the Q1-required data, but at least of the right length and with not available data filled with some replacement character) + * - the UPS reports a valid input.voltage (used in the claim function) + * - the UPS reports valid status bits (1st, 2nd, 3rd, 6th, 7th are the mandatory ones) + * + */ + +#include "main.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_blazer-common.h" + +#include "nutdrv_qx_q1.h" + +#define Q1_VERSION "Q1 0.07" + +/* qx2nut lookup table */ +static item_t q1_qx2nut[] = { + + /* + * > [Q1\r] + * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] + * 01234567890123456789012345678901234567890123456 + * 0 1 2 3 4 + */ + + { "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, + { "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, + { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, + { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, + { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, + /* Status bits */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ + { "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ + { "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ + { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ + + /* Instant commands */ + { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + /* Server-side settable vars */ + { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + + /* End of structure. */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + +/* Testing table */ +#ifdef TESTING +static testing_t q1_testing[] = { + { "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, + { "Q\r", "", -1 }, + { "S03\r", "", -1 }, + { "C\r", "", -1 }, + { "S02R0005\r", "", -1 }, + { "S.5R0000\r", "", -1 }, + { "T04\r", "", -1 }, + { "TL\r", "", -1 }, + { "T\r", "", -1 }, + { "CT\r", "", -1 }, + { NULL } +}; +#endif /* TESTING */ + +/* Subdriver-specific initups */ +static void q1_initups(void) +{ + blazer_initups_light(q1_qx2nut); +} + +/* Subdriver interface */ +subdriver_t q1_subdriver = { + Q1_VERSION, + blazer_claim_light, + q1_qx2nut, + q1_initups, + NULL, + blazer_makevartable_light, + "ACK", + NULL, +#ifdef TESTING + q1_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_q1.h b/drivers/nutdrv_qx_q1.h new file mode 100644 index 0000000..d990ef7 --- /dev/null +++ b/drivers/nutdrv_qx_q1.h @@ -0,0 +1,39 @@ +/* nutdrv_qx_q1.h - Subdriver for Q1 protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * NOTE: + * This subdriver implements the same protocol as the one used by the 'megatec' subdriver minus the vendor (I) and ratings (F) queries. + * In the claim function: + * - it doesn't even try to get 'vendor' information (I) + * - it checks only status (Q1), through 'input.voltage' variable + * Therefore it should be able to work even if the UPS doesn't support vendor/ratings *and* the user doesn't use the 'novendor'/'norating' flags, as long as: + * - the UPS replies a Q1-compliant answer (i.e. not necessary filled with all of the Q1-required data, but at least of the right length and with not available data filled with some replacement character) + * - the UPS reports a valid input.voltage (used in the claim function) + * - the UPS reports valid status bits (1st, 2nd, 3rd, 6th, 7th are the mandatory ones) + * + */ + +#ifndef NUTDRV_QX_Q1_H +#define NUTDRV_QX_Q1_H + +#include "nutdrv_qx.h" + +extern subdriver_t q1_subdriver; + +#endif /* NUTDRV_QX_Q1_H */ diff --git a/drivers/nutdrv_qx_voltronic-qs-hex.c b/drivers/nutdrv_qx_voltronic-qs-hex.c new file mode 100644 index 0000000..9485fe0 --- /dev/null +++ b/drivers/nutdrv_qx_voltronic-qs-hex.c @@ -0,0 +1,572 @@ +/* nutdrv_qx_voltronic-qs-hex.c - Subdriver for Voltronic Power UPSes with QS-Hex protocol + * + * Copyright (C) + * 2014 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_blazer-common.h" + +#include "nutdrv_qx_voltronic-qs-hex.h" + +#define VOLTRONIC_QS_HEX_VERSION "Voltronic-QS-Hex 0.10" + +/* Support functions */ +static int voltronic_qs_hex_claim(void); +static void voltronic_qs_hex_initups(void); + +/* Answer preprocess functions */ +static int voltronic_qs_hex_preprocess_qs_answer(item_t *item, const int len); +static int voltronic_qs_hex_char_to_binary(const unsigned char value); + +/* Preprocess functions */ +static int voltronic_qs_hex_protocol(item_t *item, char *value, const size_t valuelen); +static int voltronic_qs_hex_input_output_voltage(item_t *item, char *value, const size_t valuelen); +static int voltronic_qs_hex_load(item_t *item, char *value, const size_t valuelen); +static int voltronic_qs_hex_frequency(item_t *item, char *value, const size_t valuelen); +static int voltronic_qs_hex_battery_voltage(item_t *item, char *value, const size_t valuelen); +static int voltronic_qs_hex_process_ratings_bits(item_t *item, char *value, const size_t valuelen); + + +/* == Ranges == */ + +/* Range for ups.delay.start */ +static info_rw_t voltronic_qs_hex_r_ondelay[] = { + { "60", 0 }, + { "599940", 0 }, + { "", 0 } +}; + +/* Range for ups.delay.shutdown */ +static info_rw_t voltronic_qs_hex_r_offdelay[] = { + { "12", 0 }, + { "540", 0 }, + { "", 0 } +}; + + +/* == qx2nut lookup table == */ +static item_t voltronic_qs_hex_qx2nut[] = { + + /* Query UPS for protocol + * > [M\r] + * < [P\r] + * 01 + * 0 + */ + + { "ups.firmware.aux", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "PM-%s", QX_FLAG_STATIC, NULL, NULL, voltronic_qs_hex_protocol }, + + /* Query UPS for status + * > [QS\r] + * < [#6C01 35 6C01 35 03 519A 1312D0 E6 1E 00001001\r] ('P' protocol, after being preprocessed) + * < [#6901 6C 6802 6C 00 5FD7 12C000 E4 1E 00001001 00000010\r] ('T' protocol, after being preprocessed) + * 01234567890123456789012345678901234567890123456789012345 + * 0 1 2 3 4 5 + */ + + { "input.voltage", 0, NULL, "QS\r", "", 47, '#', "", 1, 7, "%.1f", 0, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_input_output_voltage }, + { "output.voltage", 0, NULL, "QS\r", "", 47, '#', "", 9, 15, "%.1f", 0, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_input_output_voltage }, + { "ups.load", 0, NULL, "QS\r", "", 47, '#', "", 17, 18, "%d", 0, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_load }, + { "output.frequency", 0, NULL, "QS\r", "", 47, '#', "", 20, 30, "%.1f", 0, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_frequency }, + { "battery.voltage", 0, NULL, "QS\r", "", 47, '#', "", 32, 36, "%.2f", 0, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_battery_voltage }, + /* Status bits */ + { "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Utility Fail (Immediate) */ + { "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Battery Low */ + { "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ + { "ups.alarm", 0, NULL, "QS\r", "", 47, '#', "", 41, 41, NULL, 0, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* UPS Failed */ + { "ups.type", 0, NULL, "QS\r", "", 47, '#', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* UPS Type */ + { "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Test in Progress */ + { "ups.status", 0, NULL, "QS\r", "", 47, '#', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Shutdown Active */ + { "ups.beeper.status", 0, NULL, "QS\r", "", 47, '#', "", 45, 45, "%s", 0, NULL, voltronic_qs_hex_preprocess_qs_answer, blazer_process_status_bits }, /* Beeper status */ + /* Ratings bits */ + { "output.frequency.nominal", 0, NULL, "QS\r", "", 56, '#', "", 47, 47, "%.1f", QX_FLAG_SKIP, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_process_ratings_bits }, + { "battery.voltage.nominal", 0, NULL, "QS\r", "", 56, '#', "", 48, 49, "%.1f", QX_FLAG_SKIP, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_process_ratings_bits }, +/* { "reserved.1", 0, NULL, "QS\r", "", 56, '#', "", 50, 50, "%s", QX_FLAG_SKIP, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_process_ratings_bits }, *//* Reserved */ +/* { "reserved.2", 0, NULL, "QS\r", "", 56, '#', "", 51, 51, "%s", QX_FLAG_SKIP, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_process_ratings_bits }, *//* Reserved */ + { "output.voltage.nominal", 0, NULL, "QS\r", "", 56, '#', "", 52, 54, "%.1f", QX_FLAG_SKIP, NULL, voltronic_qs_hex_preprocess_qs_answer, voltronic_qs_hex_process_ratings_bits }, + + /* Instant commands */ + { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + + /* Server-side settable vars */ + { "ups.delay.start", ST_FLAG_RW, voltronic_qs_hex_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + { "ups.delay.shutdown", ST_FLAG_RW, voltronic_qs_hex_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + + /* End of structure. */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + + +/* == Testing table == */ +#ifdef TESTING +static testing_t voltronic_qs_hex_testing[] = { + { "QS\r", "#\x6C\x01 \x35 \x6C\x01 \x35 \x03 \x51\x9A \x28\x02\x12\xD0 \xE6 \x1E \x09\r", 27 }, + { "M\r", "P\r", -1 }, + { "Q\r", "", -1 }, + { "S00R0000\r", "", -1 }, + { "C\r", "", -1 }, + { "S02R0005\r", "", -1 }, + { "S.5R0000\r", "N\r", -1 }, + { "T\r", "", -1 }, + { NULL } +}; +#endif /* TESTING */ + + +/* == Support functions == */ + +/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ +static int voltronic_qs_hex_claim(void) +{ + /* We need at least M and QS to run this subdriver */ + + /* UPS Protocol */ + item_t *item = find_nut_info("ups.firmware.aux", 0, 0); + + /* Don't know what happened */ + if (!item) + return 0; + + /* No reply/Unable to get value */ + if (qx_process(item, NULL)) + return 0; + + /* Unable to process value/Protocol not supported */ + if (ups_infoval_set(item) != 1) + return 0; + + item = find_nut_info("input.voltage", 0, 0); + + /* Don't know what happened */ + if (!item) { + dstate_delinfo("ups.firmware.aux"); + return 0; + } + + /* No reply/Unable to get value */ + if (qx_process(item, NULL)) { + dstate_delinfo("ups.firmware.aux"); + return 0; + } + + /* Unable to process value */ + if (ups_infoval_set(item) != 1) { + dstate_delinfo("ups.firmware.aux"); + return 0; + } + + return 1; +} + +/* Subdriver-specific initups */ +static void voltronic_qs_hex_initups(void) +{ + blazer_initups_light(voltronic_qs_hex_qx2nut); +} + + +/* == Answer preprocess functions == */ + +/* Preprocess the answer we got back from the UPS when queried with 'QS\r' */ +static int voltronic_qs_hex_preprocess_qs_answer(item_t *item, const int len) +{ + int i, token; + char refined[SMALLBUF] = ""; + + if (len <= 0) + return len; + + if (item->answer[0] != '#') { + upsdebugx(4, "%s: wrong leading character [%s: 0x%0x]", __func__, item->info_type, item->answer[0]); + return -1; + } + + snprintf(refined, sizeof(refined), "%s", "#"); + + /* e.g.: item->answer = "#\x6C\x01 \x35 \x6C\x01 \x35 \x03 \x51\x9A \x28\x02\x12\xD0 \xE6 \x1E \x09\r" */ + upsdebug_hex(4, "read", item->answer, (size_t)len); + + for (i = 1, token = 1; i < len; i++) { + + /* New token */ + if (item->answer[i] == 0x20) { + snprintfcat(refined, sizeof(refined), "%s", " "); + token++; + continue; + } + + /* 'Unescape' raw data */ + if (i < len && item->answer[i] == 0x28) { + + switch (item->answer[i + 1]) + { + case 0x00: /* Escaped because: CR */ + snprintfcat(refined, sizeof(refined), "%02x", 0x0D); + break; + case 0x01: /* Escaped because: XON */ + snprintfcat(refined, sizeof(refined), "%02x", 0x11); + break; + case 0x02: /* Escaped because: XOFF */ + snprintfcat(refined, sizeof(refined), "%02x", 0x13); + break; + case 0x03: /* Escaped because: LF */ + snprintfcat(refined, sizeof(refined), "%02x", 0x0A); + break; + case 0x04: /* Escaped because: space */ + snprintfcat(refined, sizeof(refined), "%02x", 0x20); + break; + default: + if (token != 10 && token != 11) + snprintfcat(refined, sizeof(refined), "%02x", ((unsigned char *)item->answer)[i]); + else + snprintfcat(refined, sizeof(refined), "%08d", voltronic_qs_hex_char_to_binary(((unsigned char *)item->answer)[i])); + continue; + } + + i++; + continue; + + } + + /* Trailing CR */ + if (item->answer[i] == 0x0D) + break; + + if (token != 10 && token != 11) + snprintfcat(refined, sizeof(refined), "%02x", ((unsigned char *)item->answer)[i]); + else + snprintfcat(refined, sizeof(refined), "%08d", voltronic_qs_hex_char_to_binary(((unsigned char *)item->answer)[i])); + + } + + if ( + token < 10 || + token > 11 || + (token == 10 && strlen(refined) != 46) || + (token == 11 && strlen(refined) != 55) + ) { + upsdebugx(2, "noncompliant reply: %s", refined); + return -1; + } + + upsdebugx(4, "read: %s", refined); + + /* e.g.: item->answer = "#6C01 35 6C01 35 03 519A 1312D0 E6 1E 00001001" */ + return snprintf(item->answer, sizeof(item->answer), "%s\r", refined); +} + +/* Transform a char into its binary form (as an int) */ +static int voltronic_qs_hex_char_to_binary(const unsigned char value) +{ + unsigned char remainder = value; + int ret = 0, + power = 1; + + while (remainder) { + + if (remainder & 1) + ret += power; + + power *= 10; + remainder >>= 1; + + } + + return ret; +} + + +/* == Preprocess functions == */ + +/* Protocol used by the UPS */ +static int voltronic_qs_hex_protocol(item_t *item, char *value, const size_t valuelen) +{ + item_t *unskip; + int i; + const struct { + const char *info_type; /* info_type of the item to be unskipped */ + const unsigned long flags; /* qxflags that have to be set in the item */ + const unsigned long noflags; /* qxflags that have to be absent in the item */ + } items_to_be_unskipped[] = { + { "test.battery.start.quick", QX_FLAG_CMD, 0 }, + { "output.frequency.nominal", 0, 0 }, + { "battery.voltage.nominal", 0, 0 }, + { "output.voltage.nominal", 0, 0 }, + { NULL, 0, 0 } + }; + + if (strcasecmp(item->value, "P") && strcasecmp(item->value, "T")) { + upsdebugx(2, "%s: invalid protocol [%s]", __func__, item->value); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, item->value); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + /* Unskip items supported only by devices that implement 'T' protocol */ + + if (!strcasecmp(item->value, "P")) + return 0; + + for (i = 0; items_to_be_unskipped[i].info_type; i++) { + unskip = find_nut_info(items_to_be_unskipped[i].info_type, items_to_be_unskipped[i].flags, items_to_be_unskipped[i].noflags); + /* Don't know what happened */ + if (!unskip) + return -1; + unskip->qxflags &= ~QX_FLAG_SKIP; + } + + return 0; +} + +/* Input/Output voltage */ +static int voltronic_qs_hex_input_output_voltage(item_t *item, char *value, const size_t valuelen) +{ + long val; + double ret; + char *str_end; + + if (strspn(item->value, "0123456789ABCDEFabcdef ") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + + val = strtol(item->value, &str_end, 16) * strtol(str_end, NULL, 16) / 51; + ret = val / 256.0; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, ret); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Device load */ +static int voltronic_qs_hex_load(item_t *item, char *value, const size_t valuelen) +{ + if (strspn(item->value, "0123456789ABCDEFabcdef") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, strtol(item->value, NULL, 16)); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Output frequency */ +static int voltronic_qs_hex_frequency(item_t *item, char *value, const size_t valuelen) +{ + double val1, val2, ret; + char *str_end; + + if (strspn(item->value, "0123456789ABCDEFabcdef ") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + + val1 = strtol(item->value, &str_end, 16); + val2 = strtol(str_end, NULL, 16); + + ret = val2 / val1; + ret = ret > 99.9 ? 99.9 : ret; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, ret); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Battery voltage */ +static int voltronic_qs_hex_battery_voltage(item_t *item, char *value, const size_t valuelen) +{ + long val1, val2; + char *str_end; + + if (strspn(item->value, "0123456789ABCDEFabcdef ") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + + val1 = strtol(item->value, &str_end, 16); + val2 = strtol(str_end, NULL, 16); + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, (val1 * val2) / 510.0); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Ratings bits */ +static int voltronic_qs_hex_process_ratings_bits(item_t *item, char *value, const size_t valuelen) +{ + long val; + double ret; + + if (strspn(item->value, "01") != strlen(item->value)) { + upsdebugx(3, "%s: unexpected value %s@%d->%s", __func__, item->info_type, item->from, item->value); + return -1; + } + + val = strtol(item->value, NULL, 10); + + switch (item->from) + { + case 47: /* Nominal output frequency */ + if (val == 0) /* 0 -> 50 Hz */ + ret = 50; + else /* 1 -> 60 Hz */ + ret = 60; + break; + case 48: /* Nominal battery voltage */ + if (val == 0) /* 0 -> 12 V */ + ret = 12; + else if (val == 1) /* 1 -> 24 V */ + ret = 24; + else if (val == 10) /* 10 -> 36 V */ + ret = 36; + else /* 11 -> 48 V */ + ret = 48; + break; +/* case 50: *//* Reserved */ +/* break;*/ +/* case 51: *//* Reserved */ +/* break;*/ + case 52: /* Nominal output voltage */ + switch (val) + { + case 0: + ret = 110; + break; + case 1: + ret = 120; + break; + case 10: + ret = 220; + break; + case 11: + ret = 230; + break; + case 100: + ret = 240; + break; + default: + /* Unknown */ + return -1; + } + break; + default: + /* Don't know what happened */ + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, ret); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + + +/* == Subdriver interface == */ +subdriver_t voltronic_qs_hex_subdriver = { + VOLTRONIC_QS_HEX_VERSION, + voltronic_qs_hex_claim, + voltronic_qs_hex_qx2nut, + voltronic_qs_hex_initups, + NULL, + blazer_makevartable_light, + NULL, + "N\r", +#ifdef TESTING + voltronic_qs_hex_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_voltronic-qs-hex.h b/drivers/nutdrv_qx_voltronic-qs-hex.h new file mode 100644 index 0000000..e4735df --- /dev/null +++ b/drivers/nutdrv_qx_voltronic-qs-hex.h @@ -0,0 +1,29 @@ +/* nutdrv_qx_voltronic-qs-hex.h - Subdriver for Voltronic Power UPSes with QS-Hex protocol + * + * Copyright (C) + * 2014 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_VOLTRONIC_QS_HEX_H +#define NUTDRV_QX_VOLTRONIC_QS_HEX_H + +#include "nutdrv_qx.h" + +extern subdriver_t voltronic_qs_hex_subdriver; + +#endif /* NUTDRV_QX_VOLTRONIC_QS_HEX_H */ diff --git a/drivers/nutdrv_qx_voltronic-qs.c b/drivers/nutdrv_qx_voltronic-qs.c new file mode 100644 index 0000000..43df438 --- /dev/null +++ b/drivers/nutdrv_qx_voltronic-qs.c @@ -0,0 +1,230 @@ +/* nutdrv_qx_voltronic-qs.c - Subdriver for Voltronic Power UPSes with QS protocol + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_blazer-common.h" + +#include "nutdrv_qx_voltronic-qs.h" + +#define VOLTRONIC_QS_VERSION "Voltronic-QS 0.07" + +/* Support functions */ +static int voltronic_qs_claim(void); +static void voltronic_qs_initups(void); + +/* Preprocess functions */ +static int voltronic_qs_protocol(item_t *item, char *value, const size_t valuelen); + + +/* == Ranges == */ + +/* Range for ups.delay.start */ +static info_rw_t voltronic_qs_r_ondelay[] = { + { "60", 0 }, + { "599940", 0 }, + { "", 0 } +}; + +/* Range for ups.delay.shutdown */ +static info_rw_t voltronic_qs_r_offdelay[] = { + { "12", 0 }, + { "540", 0 }, + { "", 0 } +}; + + +/* == qx2nut lookup table == */ +static item_t voltronic_qs_qx2nut[] = { + + /* Query UPS for protocol + * > [M\r] + * < [V\r] + * 01 + * 0 + */ + + { "ups.firmware.aux", 0, NULL, "M\r", "", 2, 0, "", 0, 0, "PM-%s", QX_FLAG_STATIC, NULL, NULL, voltronic_qs_protocol }, + + /* Query UPS for status + * > [QS\r] + * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] + * 01234567890123456789012345678901234567890123456 + * 0 1 2 3 4 + */ + + { "input.voltage", 0, NULL, "QS\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, + { "input.voltage.fault", 0, NULL, "QS\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, + { "output.voltage", 0, NULL, "QS\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, + { "ups.load", 0, NULL, "QS\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, + { "output.frequency", 0, NULL, "QS\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "QS\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "ups.temperature", 0, NULL, "QS\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, + /* Status bits */ + { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ + { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ + { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ + { "ups.alarm", 0, NULL, "QS\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ + { "ups.type", 0, NULL, "QS\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ + { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ + { "ups.status", 0, NULL, "QS\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ + { "ups.beeper.status", 0, NULL, "QS\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ + + /* Query UPS for ratings + * > [F\r] + * < [#220.0 003 12.00 50.0\r] + * 0123456789012345678901 + * 0 1 2 + */ + + { "output.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "output.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "output.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + + /* Instant commands */ + { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "C\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + /* Server-side settable vars */ + { "ups.delay.start", ST_FLAG_RW, voltronic_qs_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + { "ups.delay.shutdown", ST_FLAG_RW, voltronic_qs_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + + /* End of structure. */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + + +/* == Testing table == */ +#ifdef TESTING +static testing_t voltronic_qs_testing[] = { + { "QS\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, + { "F\r", "#220.0 003 12.00 50.0\r", -1 }, + { "M\r", "V\r", -1 }, + { "Q\r", "", -1 }, + { "C\r", "", -1 }, + { "S02R0005\r", "", -1 }, + { "S.5R0000\r", "", -1 }, + { "T\r", "", -1 }, + { NULL } +}; +#endif /* TESTING */ + + +/* == Support functions == */ + +/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ +static int voltronic_qs_claim(void) +{ + /* We need at least M and QS to run this subdriver */ + + /* UPS Protocol */ + item_t *item = find_nut_info("ups.firmware.aux", 0, 0); + + /* Don't know what happened */ + if (!item) + return 0; + + /* No reply/Unable to get value */ + if (qx_process(item, NULL)) + return 0; + + /* Unable to process value/Protocol not supported */ + if (ups_infoval_set(item) != 1) + return 0; + + item = find_nut_info("input.voltage", 0, 0); + + /* Don't know what happened */ + if (!item) { + dstate_delinfo("ups.firmware.aux"); + return 0; + } + + /* No reply/Unable to get value */ + if (qx_process(item, NULL)) { + dstate_delinfo("ups.firmware.aux"); + return 0; + } + + /* Unable to process value */ + if (ups_infoval_set(item) != 1) { + dstate_delinfo("ups.firmware.aux"); + return 0; + } + + return 1; +} + +/* Subdriver-specific initups */ +static void voltronic_qs_initups(void) +{ + blazer_initups_light(voltronic_qs_qx2nut); +} + + +/* == Preprocess functions == */ + +/* Protocol used by the UPS */ +static int voltronic_qs_protocol(item_t *item, char *value, const size_t valuelen) +{ + if (strcasecmp(item->value, "V")) { + upsdebugx(2, "%s: invalid protocol [%s]", __func__, item->value); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, item->value); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + + +/* == Subdriver interface == */ +subdriver_t voltronic_qs_subdriver = { + VOLTRONIC_QS_VERSION, + voltronic_qs_claim, + voltronic_qs_qx2nut, + voltronic_qs_initups, + NULL, + blazer_makevartable_light, + NULL, + NULL, +#ifdef TESTING + voltronic_qs_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_voltronic-qs.h b/drivers/nutdrv_qx_voltronic-qs.h new file mode 100644 index 0000000..ac9f39b --- /dev/null +++ b/drivers/nutdrv_qx_voltronic-qs.h @@ -0,0 +1,29 @@ +/* nutdrv_qx_voltronic-qs.h - Subdriver for Voltronic Power UPSes with QS protocol + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_VOLTRONIC_QS_H +#define NUTDRV_QX_VOLTRONIC_QS_H + +#include "nutdrv_qx.h" + +extern subdriver_t voltronic_qs_subdriver; + +#endif /* NUTDRV_QX_VOLTRONIC_QS_H */ diff --git a/drivers/nutdrv_qx_voltronic.c b/drivers/nutdrv_qx_voltronic.c new file mode 100644 index 0000000..8e2f2ad --- /dev/null +++ b/drivers/nutdrv_qx_voltronic.c @@ -0,0 +1,4522 @@ +/* nutdrv_qx_voltronic.c - Subdriver for Voltronic Power UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nut_float.h" +#include "nut_stdint.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_voltronic.h" + +#define VOLTRONIC_VERSION "Voltronic 0.06" + +/* Support functions */ +static int voltronic_claim(void); +static void voltronic_makevartable(void); +static void voltronic_massive_unskip(const long protocol); + +/* Range/enum functions */ +static int voltronic_batt_low(char *value, const size_t len); +static int voltronic_bypass_volt_max(char *value, const size_t len); +static int voltronic_bypass_volt_min(char *value, const size_t len); +static int voltronic_bypass_freq_max(char *value, const size_t len); +static int voltronic_bypass_freq_min(char *value, const size_t len); +static int voltronic_eco_freq_min(char *value, const size_t len); +static int voltronic_eco_freq_max(char *value, const size_t len); + +/* Preprocess functions */ +static int voltronic_process_setvar(item_t *item, char *value, const size_t valuelen); +static int voltronic_process_command(item_t *item, char *value, const size_t valuelen); +static int voltronic_capability(item_t *item, char *value, const size_t valuelen); +static int voltronic_capability_set(item_t *item, char *value, const size_t valuelen); +static int voltronic_capability_set_nonut(item_t *item, char *value, const size_t valuelen); +static int voltronic_capability_reset(item_t *item, char *value, const size_t valuelen); +static int voltronic_eco_volt(item_t *item, char *value, const size_t valuelen); +static int voltronic_eco_volt_range(item_t *item, char *value, const size_t valuelen); +static int voltronic_eco_freq(item_t *item, char *value, const size_t valuelen); +static int voltronic_bypass(item_t *item, char *value, const size_t valuelen); +static int voltronic_batt_numb(item_t *item, char *value, const size_t valuelen); +static int voltronic_batt_runtime(item_t *item, char *value, const size_t valuelen); +static int voltronic_protocol(item_t *item, char *value, const size_t valuelen); +static int voltronic_fault(item_t *item, char *value, const size_t valuelen); +static int voltronic_warning(item_t *item, char *value, const size_t valuelen); +static int voltronic_mode(item_t *item, char *value, const size_t valuelen); +static int voltronic_status(item_t *item, char *value, const size_t valuelen); +static int voltronic_output_powerfactor(item_t *item, char *value, const size_t valuelen); +static int voltronic_serial_numb(item_t *item, char *value, const size_t valuelen); +static int voltronic_outlet(item_t *item, char *value, const size_t valuelen); +static int voltronic_outlet_delay(item_t *item, char *value, const size_t valuelen); +static int voltronic_outlet_delay_set(item_t *item, char *value, const size_t valuelen); +static int voltronic_p31b(item_t *item, char *value, const size_t valuelen); +static int voltronic_p31b_set(item_t *item, char *value, const size_t valuelen); +static int voltronic_p31g(item_t *item, char *value, const size_t valuelen); +static int voltronic_p31g_set(item_t *item, char *value, const size_t valuelen); +static int voltronic_phase(item_t *item, char *value, const size_t valuelen); +static int voltronic_phase_set(item_t *item, char *value, const size_t valuelen); +static int voltronic_parallel(item_t *item, char *value, const size_t valuelen); + +/* Capability vars */ +static char *bypass_alarm, + *battery_alarm, + *bypass_when_off, + *alarm_control, + *converter_mode, + *eco_mode, + *battery_open_status_check, + *bypass_forbidding, + *site_fault_detection, + *advanced_eco_mode, + *constant_phase_angle, + *limited_runtime_on_battery; + +/* ups.conf settings */ +static long max_bypass_volt, + min_bypass_volt, + battery_number, + output_phase_angle, + work_range_type; +static double max_bypass_freq, + min_bypass_freq; + + +/* == Ranges/enums == */ + +/* Range for ups.delay.start */ +static info_rw_t voltronic_r_ondelay[] = { + { "0", 0 }, + { "599940", 0 }, + { "", 0 } +}; + +/* Range for ups.delay.shutdown */ +static info_rw_t voltronic_r_offdelay[] = { + { "12", 0 }, + { "5940", 0 }, + { "", 0 } +}; + +/* Enumlist for output phase angle */ +static info_rw_t voltronic_e_phase[] = { + { "000", 0 }, + { "120", 0 }, + { "180", 0 }, + { "240", 0 }, + { "", 0 } +}; + +/* Range for battery low voltage */ +static info_rw_t voltronic_r_batt_low[] = { + { "20", 0 }, + { "24", voltronic_batt_low }, + { "28", voltronic_batt_low }, + { "", 0 } +}; + +/* Preprocess range value for battery low voltage */ +static int voltronic_batt_low(char *value, const size_t len) +{ + long val = strtol(value, NULL, 10); + const char *ovn = dstate_getinfo("output.voltage.nominal"), + *ocn = dstate_getinfo("output.current.nominal"); + NUT_UNUSED_VARIABLE(len); + + if (!ovn || !ocn) { + upsdebugx(2, "%s: unable to get the value of output voltage nominal/output current nominal", __func__); + return -1; + } + + if ((strtol(ovn, NULL, 10) * strtol(ocn, NULL, 10)) < 1000) { + + if (val == 24) + return 0; + else + return -1; + + } else { + + if (val == 28) + return 0; + else + return -1; + } +} + +/* Range for outlet.n.delay.shutdown */ +static info_rw_t voltronic_r_outlet_delay[] = { + { "0", 0 }, + { "59940", 0 }, + { "", 0 } +}; + +/* Enumlist for device grid working range type */ +static info_rw_t voltronic_e_work_range[] = { + { "Appliance", 0 }, /* 00 */ + { "UPS", 0 }, /* 01 */ + { "", 0 } +}; + +/* Enumlist for battery type */ +static info_rw_t voltronic_e_batt_type[] = { + { "Li", 0 }, /* 00 */ + { "Flooded", 0 }, /* 01 */ + { "AGM", 0 }, /* 02 */ + { "", 0 } +}; + +/* Range for number of battery packs */ +static info_rw_t voltronic_r_batt_packs[] = { + { "1", 0 }, + { "99", 0 }, + { "", 0 } +}; + +/* Range for number of batteries */ +static info_rw_t voltronic_r_batt_numb[] = { + { "1", 0 }, + { "9", 0 }, + { "", 0 } +}; + +/* Range for Bypass Mode maximum voltage */ +static info_rw_t voltronic_r_bypass_volt_max[] = { + { "60", voltronic_bypass_volt_max }, /* P09 */ + { "115", voltronic_bypass_volt_max }, /* P02/P03/P10/P13/P14/P99 ivn<200 */ + { "120", voltronic_bypass_volt_max }, /* P01 ivn<200 */ + { "132", voltronic_bypass_volt_max }, /* P99 ivn<200 */ + { "138", voltronic_bypass_volt_max }, /* P02/P03/P10/P13/P14 ivn<200 */ + { "140", voltronic_bypass_volt_max }, /* P01 ivn<200, P09 */ + { "230", voltronic_bypass_volt_max }, /* P01 ivn>=200 */ + { "231", voltronic_bypass_volt_max }, /* P02/P03/P10/P13/P14/P99 ivn>=200 */ + { "261", voltronic_bypass_volt_max }, /* P99 ivn>=200 */ + { "264", voltronic_bypass_volt_max }, /* P01 ivn>=200 */ + { "276", voltronic_bypass_volt_max }, /* P02/P03/P10/P13/P14 ivn>=200 */ + { "", 0 } +}; + +/* Preprocess range value for Bypass Mode maximum voltage */ +static int voltronic_bypass_volt_max(char *value, const size_t len) +{ + long protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10), + val = strtol(value, NULL, 10), + ivn; + const char *involtnom = dstate_getinfo("input.voltage.nominal"); + NUT_UNUSED_VARIABLE(len); + + if (!involtnom) { + upsdebugx(2, "%s: unable to get input.voltage.nominal", __func__); + return -1; + } + + ivn = strtol(involtnom, NULL, 10); + + switch (val) + { + case 60: /* P09 */ + + if (protocol == 9) + return 0; + + break; + + case 115: /* P02/P03/P10/P13/P14/P99 ivn<200 */ + + if (ivn >= 200) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 120: /* P01 ivn<200 */ + + if (ivn >= 200) + return -1; + + if (protocol == 1) + return 0; + + break; + + case 132: /* P99 ivn<200 */ + + if (ivn >= 200) + return -1; + + if (protocol == 99) + return 0; + + break; + + case 138: /* P02/P03/P10/P13/P14 ivn<200 */ + + if (ivn >= 200) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14) + return 0; + + break; + + case 140: /* P01 ivn<200, P09 */ + + if (protocol == 9) + return 0; + + if (ivn >= 200) + return -1; + + if (protocol == 1) + return 0; + + break; + + case 230: /* P01 ivn>=200 */ + + if (ivn < 200) + return -1; + + if (protocol == 1) + return 0; + + break; + + case 231: /* P02/P03/P10/P13/P14/P99 ivn>=200 */ + + if (ivn < 200) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 261: /* P99 ivn>=200 */ + + if (ivn < 200) + return -1; + + if (protocol == 99) + return 0; + + break; + + case 264: /* P01 ivn>=200 */ + + if (ivn < 200) + return -1; + + if (protocol == 1) + return 0; + + break; + + case 276: /* P02/P03/P10/P13/P14 ivn>=200 */ + + if (ivn < 200) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14) + return 0; + + break; + + default: + + upsdebugx(2, "%s: unknown value (%s)", __func__, value); + break; + + } + + return -1; +} + +/* Range for Bypass Mode minimum voltage */ +static info_rw_t voltronic_r_bypass_volt_min[] = { + { "50", voltronic_bypass_volt_min }, /* P99 ivn<200 */ + { "55", voltronic_bypass_volt_min }, /* P02/P03/P10/P13/P14 ivn<200 */ + { "60", voltronic_bypass_volt_min }, /* P09 */ + { "85", voltronic_bypass_volt_min }, /* P01/P99 ivn<200 */ + { "104", voltronic_bypass_volt_min }, /* P02/P03/P10/P13/P14 ivn<200 */ + { "110", voltronic_bypass_volt_min }, /* P02/P03/P10/P13/P14 ivn>=200 */ + { "115", voltronic_bypass_volt_min }, /* P01 ivn<200 */ + { "140", voltronic_bypass_volt_min }, /* P09 */ + { "149", voltronic_bypass_volt_min }, /* P99 ivn>=200 */ + { "170", voltronic_bypass_volt_min }, /* P01 ivn>=200 */ + { "209", voltronic_bypass_volt_min }, /* P02/P03/P10/P13/P14/P99 ivn>=200 */ + { "220", voltronic_bypass_volt_min }, /* P01 ivn>=200 */ + { "", 0 } +}; + +/* Preprocess range value for Bypass Mode minimum voltage */ +static int voltronic_bypass_volt_min(char *value, const size_t len) +{ + long protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10), + val = strtol(value, NULL, 10), + ivn; + const char *involtnom = dstate_getinfo("input.voltage.nominal"); + NUT_UNUSED_VARIABLE(len); + + if (!involtnom) { + upsdebugx(2, "%s: unable to get input.voltage.nominal", __func__); + return -1; + } + + ivn = strtol(involtnom, NULL, 10); + + switch (val) + { + case 50: /* P99 ivn<200 */ + + if (ivn >= 200) + return -1; + + if (protocol == 99) + return 0; + + break; + + case 55: /* P02/P03/P10/P13/P14 ivn<200 */ + + if (ivn >= 200) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14) + return 0; + + break; + + case 60: /* P09 */ + case 140: /* P09 */ + + if (protocol == 9) + return 0; + + break; + + case 85: /* P01/P99 ivn<200 */ + + if (ivn >= 200) + return -1; + + if (protocol == 1 || protocol == 99) + return 0; + + break; + + case 104: /* P02/P03/P10/P13/P14 ivn<200 */ + + if (ivn >= 200) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14) + return 0; + + break; + + case 110: /* P02/P03/P10/P13/P14 ivn>=200 */ + + if (ivn < 200) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14) + return 0; + + break; + + case 115: /* P01 ivn<200 */ + + if (ivn >= 200) + return -1; + + if (protocol == 1) + return 0; + + break; + + case 149: /* P99 ivn>=200 */ + + if (ivn < 200) + return -1; + + if (protocol == 99) + return 0; + + break; + + case 170: /* P01 ivn>=200 */ + + if (ivn < 200) + return -1; + + if (protocol == 1) + return 0; + + break; + + case 209: /* P02/P03/P10/P13/P14/P99 ivn>=200 */ + + if (ivn < 200) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 220: /* P01 ivn>=200 */ + + if (ivn < 200) + return -1; + + if (protocol == 1) + return 0; + + break; + + default: + + upsdebugx(2, "%s: unknown value (%s)", __func__, value); + break; + + } + + return -1; +} + +/* Range for Bypass Mode maximum frequency */ +static info_rw_t voltronic_r_bypass_freq_max[] = { + { "51.0", voltronic_bypass_freq_max }, /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==50.0 */ + { "54.0", voltronic_bypass_freq_max }, /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ + { "60.0", voltronic_bypass_freq_max }, /* P01/P09 ofn==50.0 */ + { "61.0", voltronic_bypass_freq_max }, /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==60.0 */ + { "64.0", voltronic_bypass_freq_max }, /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ + { "70.0", voltronic_bypass_freq_max }, /* P01/P09 ofn==60.0 */ + { "", 0 } +}; + +/* Preprocess range value for Bypass Mode maximum frequency */ +static int voltronic_bypass_freq_max(char *value, const size_t len) +{ + long protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10), + val = strtol(value, NULL, 10); + double ofn; + const char *outfreqnom = dstate_getinfo("output.frequency.nominal"); + NUT_UNUSED_VARIABLE(len); + + if (!outfreqnom) { + upsdebugx(2, "%s: unable to get output.frequency.nominal", __func__); + return -1; + } + + ofn = strtod(outfreqnom, NULL); + + switch (val) + { + case 51: /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 1 || protocol == 2 || protocol == 3 || protocol == 9 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 54: /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 60: /* P01/P09 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 1 || protocol == 9) + return 0; + + break; + + case 61: /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 1 || protocol == 2 || protocol == 3 || protocol == 9 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 64: /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 70: /* P01/P09 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 1 || protocol == 9) + return 0; + + break; + + default: + + upsdebugx(2, "%s: unknown value (%s)", __func__, value); + break; + + } + + return -1; +} + +/* Range for Bypass Mode minimum frequency */ +static info_rw_t voltronic_r_bypass_freq_min[] = { + { "40.0", voltronic_bypass_freq_min }, /* P01/P09 ofn==50.0 */ + { "46.0", voltronic_bypass_freq_min }, /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ + { "49.0", voltronic_bypass_freq_min }, /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==50.0 */ + { "50.0", voltronic_bypass_freq_min }, /* P01/P09 ofn==60.0 */ + { "56.0", voltronic_bypass_freq_min }, /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ + { "59.0", voltronic_bypass_freq_min }, /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==60.0 */ + { "", 0 } +}; + +/* Preprocess range value for Bypass Mode minimum frequency */ +static int voltronic_bypass_freq_min(char *value, const size_t len) +{ + long protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10), + val = strtol(value, NULL, 10); + double ofn; + const char *outfreqnom = dstate_getinfo("output.frequency.nominal"); + NUT_UNUSED_VARIABLE(len); + + if (!outfreqnom) { + upsdebugx(2, "%s: unable to get output.frequency.nominal", __func__); + return -1; + } + + ofn = strtod(outfreqnom, NULL); + + switch (val) + { + case 40: /* P01/P09 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 1 || protocol == 9) + return 0; + + break; + + case 46: /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 49: /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 1 || protocol == 2 || protocol == 3 || protocol == 9 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 50: /* P01/P09 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 1 || protocol == 9) + return 0; + + break; + + case 56: /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 59: /* P01/P09/P02/P03/P10/P13/P14/P99 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 1 || protocol == 2 || protocol == 3 || protocol == 9 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + default: + + upsdebugx(2, "%s: unknown value (%s)", __func__, value); + break; + + } + + return -1; +} + +/* Range for Eco Mode maximum voltage: filled at runtime by voltronic_eco_volt */ +static info_rw_t voltronic_r_eco_volt_max[] = { + { "", 0 }, + { "", 0 }, + { "", 0 } +}; + +/* Range for ECO Mode minimum voltage: filled at runtime by voltronic_eco_volt */ +static info_rw_t voltronic_r_eco_volt_min[] = { + { "", 0 }, + { "", 0 }, + { "", 0 } +}; + +/* Range for ECO Mode minimum frequency */ +static info_rw_t voltronic_r_eco_freq_min[] = { + { "40.0", voltronic_eco_freq_min }, /* P01/P09 ofn==50.0 */ + { "46.0", voltronic_eco_freq_min }, /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ + { "47.0", voltronic_eco_freq_min }, /* P01/P09 ofn==50.0 */ + { "48.0", voltronic_eco_freq_min }, /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ + { "50.0", voltronic_eco_freq_min }, /* P01/P09 ofn==60.0 */ + { "56.0", voltronic_eco_freq_min }, /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ + { "57.0", voltronic_eco_freq_min }, /* P01/P09 ofn==60.0 */ + { "58.0", voltronic_eco_freq_min }, /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ + { "", 0 } +}; + +/* Preprocess range value for ECO Mode minimum frequency */ +static int voltronic_eco_freq_min(char *value, const size_t len) +{ + long protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10), + val = strtol(value, NULL, 10); + double ofn; + const char *outfreqnom = dstate_getinfo("output.frequency.nominal"); + NUT_UNUSED_VARIABLE(len); + + if (!outfreqnom) { + upsdebugx(2, "%s: unable to get output.frequency.nominal", __func__); + return -1; + } + + ofn = strtod(outfreqnom, NULL); + + switch (val) + { + case 40: /* P01/P09 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 1 || protocol == 9) + return 0; + + break; + + case 46: /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 47: /* P01/P09 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 1 || protocol == 9) + return 0; + + break; + + case 48: /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 50: /* P01/P09 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 1 || protocol == 9) + return 0; + + break; + + case 56: /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 57: /* P01/P09 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 1 || protocol == 9) + return 0; + + break; + + case 58: /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + default: + + upsdebugx(2, "%s: unknown value (%s)", __func__, value); + break; + + } + + return -1; +} + +/* Range for ECO Mode maximum frequency */ +static info_rw_t voltronic_r_eco_freq_max[] = { + { "52.0", voltronic_eco_freq_max }, /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ + { "53.0", voltronic_eco_freq_max }, /* P01/P09 ofn==50.0 */ + { "54.0", voltronic_eco_freq_max }, /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ + { "60.0", voltronic_eco_freq_max }, /* P01/P09 ofn==50.0 */ + { "62.0", voltronic_eco_freq_max }, /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ + { "63.0", voltronic_eco_freq_max }, /* P01/P09 ofn==60.0 */ + { "64.0", voltronic_eco_freq_max }, /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ + { "70.0", voltronic_eco_freq_max }, /* P01/P09 ofn==60.0 */ + { "", 0 } +}; + +/* Preprocess range value for ECO Mode maximum frequency */ +static int voltronic_eco_freq_max(char *value, const size_t len) +{ + long protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10), + val = strtol(value, NULL, 10); + double ofn; + const char *outfreqnom = dstate_getinfo("output.frequency.nominal"); + NUT_UNUSED_VARIABLE(len); + + if (!outfreqnom) { + upsdebugx(2, "%s: unable to get output.frequency.nominal", __func__); + return -1; + } + + ofn = strtod(outfreqnom, NULL); + + switch (val) + { + case 52: /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 53: /* P01/P09 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 1 || protocol == 9) + return 0; + + break; + + case 54: /* P02/P03/P10/P13/P14/P99 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 60: /* P01/P09 ofn==50.0 */ + + if (ofn != 50.0) + return -1; + + if (protocol == 1 || protocol == 9) + return 0; + + break; + + case 62: /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 63: /* P01/P09 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 1 || protocol == 9) + return 0; + + break; + + case 64: /* P02/P03/P10/P13/P14/P99 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) + return 0; + + break; + + case 70: /* P01/P09 ofn==60.0 */ + + if (ofn != 60.0) + return -1; + + if (protocol == 1 || protocol == 9) + return 0; + + break; + + default: + + upsdebugx(2, "%s: unknown value (%s)", __func__, value); + break; + + } + + return -1; +} + +/* Enumlist for UPS capabilities that have a NUT var */ +static info_rw_t voltronic_e_cap[] = { + { "no", 0 }, + { "yes", 0 }, + { "", 0 } +}; + +/* Enumlist for NONUT capabilities */ +static info_rw_t voltronic_e_cap_nonut[] = { + { "enabled", 0 }, + { "disabled", 0 }, + { "", 0 } +}; + + +/* == qx2nut lookup table == */ +static item_t voltronic_qx2nut[] = { + + /* Query UPS for protocol + * > [QPI\r] + * < [(PI00\r] + * 012345 + * 0 + */ + + { "ups.firmware.aux", 0, NULL, "QPI\r", "", 6, '(', "", 1, 4, "%s", QX_FLAG_STATIC, NULL, NULL, voltronic_protocol }, + + /* Query UPS for ratings + * > [QRI\r] + * < [(230.0 004 024.0 50.0\r] + * 0123456789012345678901 + * 0 1 2 + */ + + { "output.voltage.nominal", 0, NULL, "QRI\r", "", 22, '(', "", 1, 5, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "output.current.nominal", 0, NULL, "QRI\r", "", 22, '(', "", 7, 9, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "battery.voltage.nominal", 0, NULL, "QRI\r", "", 22, '(', "", 11, 15, "%.1f", QX_FLAG_SEMI_STATIC, NULL, NULL, NULL }, /* as *per battery pack*: the value will change when the number of batteries is changed (battery_number through BATNn) */ + { "output.frequency.nominal", 0, NULL, "QRI\r", "", 22, '(', "", 17, 20, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + + /* Query UPS for ratings + * > [QMD\r] + * < [(#######OLHVT1K0 ###1000 80 1/1 230 230 02 12.0\r] <- Some UPS may reply with spaces instead of hashes + * 012345678901234567890123456789012345678901234567 + * 0 1 2 3 4 + */ + + { "device.model", 0, NULL, "QMD\r", "", 48, '(', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "ups.power.nominal", 0, NULL, "QMD\r", "", 48, '(', "", 17, 23, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "output.powerfactor", 0, NULL, "QMD\r", "", 48, '(', "", 25, 26, "%.1f", QX_FLAG_STATIC, NULL, NULL, voltronic_output_powerfactor }, + { "input.phases", 0, NULL, "QMD\r", "", 48, '(', "", 28, 28, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "output.phases", 0, NULL, "QMD\r", "", 48, '(', "", 30, 30, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.voltage.nominal", 0, NULL, "QMD\r", "", 48, '(', "", 32, 34, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "output.voltage.nominal", 0, NULL, "QMD\r", "", 48, '(', "", 36, 38, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, /* redundant with value from QRI */ +/* { "battery_number", ST_FLAG_RW, voltronic_r_batt_numb, "QMD\r", "", 48, '(', "", 40, 41, "%d", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT, NULL, NULL, voltronic_batt_numb }, *//* redundant with value from QBV */ +/* { "battery.voltage.nominal", 0, NULL, "QMD\r", "", 48, '(', "", 43, 46, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, *//* as *per battery* vs *per pack* reported by QRI */ + + /* Query UPS for ratings + * > [F\r] + * < [#220.0 000 024.0 50.0\r] + * 0123456789012345678901 + * 0 1 2 + */ + + { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + + /* Query UPS for manufacturer + * > [QMF\r] + * < [(#######BOH\r] <- I don't know if it has a fixed length (-> so min length = ( + \r = 2). Hashes may be replaced by spaces + * 012345678901 + * 0 1 + */ + + { "device.mfr", 0, NULL, "QMF\r", "", 2, '(', "", 1, 0, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + + /* Query UPS for firmware version + * > [QVFW\r] + * < [(VERFW:00322.02\r] + * 0123456789012345 + * 0 1 + */ + + { "ups.firmware", 0, NULL, "QVFW\r", "", 16, '(', "", 7, 14, "%s", QX_FLAG_STATIC, NULL, NULL, NULL }, + + /* Query UPS for serial number + * > [QID\r] + * < [(12345679012345\r] <- As far as I know it hasn't a fixed length -> min length = ( + \r = 2 + * 0123456789012345 + * 0 1 + */ + + { "device.serial", 0, NULL, "QID\r", "", 2, '(', "", 1, 0, "%s", QX_FLAG_STATIC, NULL, NULL, voltronic_serial_numb }, + + /* Query UPS for vendor infos + * > [I\r] + * < [#------------- ------ VT12046Q \r] + * 012345678901234567890123456789012345678 + * 0 1 2 3 + */ + + { "device.mfr", 0, NULL, "I\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "device.model", 0, NULL, "I\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "ups.firmware", 0, NULL, "I\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + + /* Query UPS for status + * > [QGS\r] + * < [(234.9 50.0 229.8 50.0 000.0 000 369.1 ---.- 026.5 ---.- 018.8 100000000001\r] + * 0123456789012345678901234567890123456789012345678901234567890123456789012345 + * 0 1 2 3 4 5 6 7 + */ + + { "input.voltage", 0, NULL, "QGS\r", "", 76, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, + { "input.frequency", 0, NULL, "QGS\r", "", 76, '(', "", 7, 10, "%.1f", 0, NULL, NULL, NULL }, + { "output.voltage", 0, NULL, "QGS\r", "", 76, '(', "", 12, 16, "%.1f", 0, NULL, NULL, NULL }, + { "output.frequency", 0, NULL, "QGS\r", "", 76, '(', "", 18, 21, "%.1f", 0, NULL, NULL, NULL }, + { "output.current", 0, NULL, "QGS\r", "", 76, '(', "", 23, 27, "%.1f", 0, NULL, NULL, NULL }, + { "ups.load", 0, NULL, "QGS\r", "", 76, '(', "", 29, 31, "%.0f", 0, NULL, NULL, NULL }, +/* { "unknown.1", 0, NULL, "QGS\r", "", 76, '(', "", 33, 37, "%.1f", 0, NULL, NULL, NULL }, *//* Unknown */ +/* { "unknown.2", 0, NULL, "QGS\r", "", 76, '(', "", 39, 43, "%.1f", 0, NULL, NULL, NULL }, *//* Unknown */ + { "battery.voltage", 0, NULL, "QGS\r", "", 76, '(', "", 45, 49, "%.2f", 0, NULL, NULL, NULL }, +/* { "unknown.3", 0, NULL, "QGS\r", "", 76, '(', "", 51, 55, "%.1f", 0, NULL, NULL, NULL }, *//* Unknown */ + { "ups.temperature", 0, NULL, "QGS\r", "", 76, '(', "", 57, 61, "%.1f", 0, NULL, NULL, NULL }, + { "ups.type", 0, NULL, "QGS\r", "", 76, '(', "", 63, 64, "%s", QX_FLAG_SEMI_STATIC, NULL, NULL, voltronic_status }, + { "ups.status", 0, NULL, "QGS\r", "", 76, '(', "", 65, 65, "%s", QX_FLAG_QUICK_POLL, NULL, NULL, voltronic_status }, /* Utility Fail (Immediate) */ + { "ups.status", 0, NULL, "QGS\r", "", 76, '(', "", 66, 66, "%s", QX_FLAG_QUICK_POLL, NULL, NULL, voltronic_status }, /* Battery Low */ + { "ups.status", 0, NULL, "QGS\r", "", 76, '(', "", 67, 67, "%s", QX_FLAG_QUICK_POLL, NULL, NULL, voltronic_status }, /* Bypass/Boost or Buck Active */ + { "ups.alarm", 0, NULL, "QGS\r", "", 76, '(', "", 67, 67, "%s", 0, NULL, NULL, voltronic_status }, /* Bypass/Boost or Buck Active */ + { "ups.alarm", 0, NULL, "QGS\r", "", 76, '(', "", 68, 68, "%s", 0, NULL, NULL, voltronic_status }, /* UPS Fault */ +/* { "unknown.4", 0, NULL, "QGS\r", "", 76, '(', "", 69, 69, "%s", 0, NULL, NULL, voltronic_status }, *//* Unknown */ + { "ups.status", 0, NULL, "QGS\r", "", 76, '(', "", 70, 70, "%s", QX_FLAG_QUICK_POLL, NULL, NULL, voltronic_status }, /* Test in Progress */ + { "ups.status", 0, NULL, "QGS\r", "", 76, '(', "", 71, 71, "%s", QX_FLAG_QUICK_POLL, NULL, NULL, voltronic_status }, /* Shutdown Active */ + { "ups.beeper.status", 0, NULL, "QGS\r", "", 76, '(', "", 72, 72, "%s", 0, NULL, NULL, voltronic_status }, /* Beeper status - ups.beeper.status */ +/* { "unknown.5", 0, NULL, "QGS\r", "", 76, '(', "", 73, 73, "%s", 0, NULL, NULL, voltronic_status }, *//* Unknown */ +/* { "unknown.6", 0, NULL, "QGS\r", "", 76, '(', "", 74, 74, "%s", 0, NULL, NULL, voltronic_status }, *//* Unknown */ + + /* Query UPS for actual working mode + * > [QMOD\r] + * < [(S\r] + * 012 + * 0 + */ + + { "ups.alarm", 0, NULL, "QMOD\r", "", 3, '(', "", 1, 1, "%s", 0, NULL, NULL, voltronic_mode }, + { "ups.status", 0, NULL, "QMOD\r", "", 3, '(', "", 1, 1, "%s", 0, NULL, NULL, voltronic_mode }, + + /* Query UPS for faults and their type. Unskipped when a fault is found in 12bit flag of QGS, otherwise you'll get a fake reply. + * > [QFS\r] + * < [(OK\r] <- No fault + * 0123 + * 0 + * < [(14 212.1 50.0 005.6 49.9 006 010.6 343.8 ---.- 026.2 021.8 01101100\r] <- Fault type + Short status + * 012345678901234567890123456789012345678901234567890123456789012345678 + * 0 1 2 3 4 5 6 + */ + + { "ups.alarm", 0, NULL, "QFS\r", "", 4, '(', "", 1, 2, "%s", QX_FLAG_SKIP, NULL, NULL, voltronic_fault }, + + /* Query UPS for warnings and their type + * > [QWS\r] + * < [(0000000100000000000000000000000000000000000000000000000000000000\r] + * 012345678901234567890123456789012345678901234567890123456789012345 + * 0 1 2 3 4 5 6 + */ + + { "ups.alarm", 0, NULL, "QWS\r", "", 66, '(', "", 1, 64, "%s", 0, NULL, NULL, voltronic_warning }, + + /* Query UPS for actual infos about battery + * > [QBV\r] + * < [(026.5 02 01 068 255\r] + * 012345678901234567890 + * 0 1 2 + */ + + { "battery.voltage", 0, NULL, "QBV\r", "", 21, '(', "", 1, 5, "%.2f", 0, NULL, NULL, NULL }, + { "battery_number", ST_FLAG_RW, voltronic_r_batt_numb, "QBV\r", "", 21, '(', "", 7, 9, "%d", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT, NULL, NULL, voltronic_batt_numb }, /* Number of batteries that make a pack */ + { "battery.packs", ST_FLAG_RW, voltronic_r_batt_packs, "QBV\r", "", 21, '(', "", 10, 11, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE, NULL, NULL, NULL }, /* Number of battery packs in parallel */ + { "battery.charge", 0, NULL, "QBV\r", "", 21, '(', "", 13, 15, "%.0f", 0, NULL, NULL, NULL }, + { "battery.runtime", 0, NULL, "QBV\r", "", 21, '(', "", 17, 19, "%.0f", 0, NULL, NULL, voltronic_batt_runtime }, + + /* Query UPS for last seen min/max load level + * > [QLDL\r] + * < [(021 023\r] <- minimum load level - maximum load level + * 012345678 + * 0 + */ + + { "output.power.minimum.percent", 0, NULL, "QLDL\r", "", 9, '(', "", 1, 3, "%.0f", 0, NULL, NULL, NULL }, + { "output.power.maximum.percent", 0, NULL, "QLDL\r", "", 9, '(', "", 5, 7, "%.0f", 0, NULL, NULL, NULL }, + + /* Query UPS for multi-phase voltages/frequencies + * > [Q3**\r] + * < [(123.4 123.4 123.4 123.4 123.4 123.4\r] <- Q3PV + * < [(123.4 123.4 123.4 123.4 123.4 123.4\r] <- Q3OV + * < [(123 123 123\r] <- Q3PC + * < [(123 123 123\r] <- Q3OC + * < [(123 123 123\r] <- Q3LD + * < [(123.4 123.4 123.4\r] <- Q3YV - P09 protocol + * < [(123.4 123.4 123.4 123.4 123.4 123.4\r] <- Q3YV - P10/P03 protocols + * 0123456789012345678901234567890123456 + * 0 1 2 3 + * + * P09 = 2-phase input/2-phase output + * Q3PV (Input Voltage L1 | Input Voltage L2 | Input Voltage L3 | Input Voltage L1-L2 | Input Voltage L1-L3 | Input Voltage L2-L3 + * Q3OV (Output Voltage L1 | Output Voltage L2 | Output Voltage L3 | Output Voltage L1-L2 | Output Voltage L1-L3 | Output Voltage L2-L3 + * Q3PC (Input Current L1 | Input Current L2 | Input Current L3 + * Q3OC (Output Current L1 | Output Current L2 | Output Current L3 + * Q3LD (Output Load Level L1 | Output Load Level L2 | Output Load Level L3 + * Q3YV (Output Bypass Voltage L1 | Output Bypass Voltage L2 | Output Bypass Voltage L3 + * + * P10 = 3-phase input/3-phase output / P03 = 3-phase input/ 1-phase output + * Q3PV (Input Voltage L1 | Input Voltage L2 | Input Voltage L3 | Input Voltage L1-L2 | Input Voltage L2-L3 | Input Voltage L1-L3 + * Q3OV (Output Voltage L1 | Output Voltage L2 | Output Voltage L3 | Output Voltage L1-L2 | Output Voltage L2-L3 | Output Voltage L1-L3 + * Q3PC (Input Current L1 | Input Current L2 | Input Current L3 + * Q3OC (Output Current L1 | Output Current L2 | Output Current L3 + * Q3LD (Output Load Level L1 | Output Load Level L2 | Output Load Level L3 + * Q3YV (Output Bypass Voltage L1 | Output Bypass Voltage L2 | Output Bypass Voltage L3 | Output Bypass Voltage L1-L2 | Output Bypass Voltage L2-L3 | Output Bypass Voltage L1-L3 + * + */ + + /* From Q3PV */ + { "input.L1-N.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 1, 5, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "input.L2-N.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 7, 11, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "input.L3-N.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 13, 17, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "input.L1-L2.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 19, 23, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "input.L2-L3.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 25, 29, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "input.L1-L3.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 31, 35, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, +/* { "input.L1-L3.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 25, 29, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, *//* P09 *//* Commented out because P09 should be two-phase input/output UPSes */ +/* { "input.L2-L3.voltage", 0, NULL, "Q3PV\r", "", 37, '(', "", 31, 35, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, *//* P09 *//* Commented out because P09 should be two-phase input/output UPSes */ + + /* From Q3PC */ + { "input.L1.current", 0, NULL, "Q3PC\r", "", 13, '(', "", 1, 3, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "input.L2.current", 0, NULL, "Q3PC\r", "", 13, '(', "", 5, 7, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "input.L3.current", 0, NULL, "Q3PC\r", "", 13, '(', "", 9, 11, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, + + /* From Q3OV */ + { "output.L1-N.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 1, 5, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.L2-N.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 7, 11, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.L3-N.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 13, 17, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.L1-L2.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 19, 23, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.L2-L3.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 25, 29, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.L1-L3.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 31, 35, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, +/* { "output.L1-L3.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 25, 29, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, *//* P09 *//* Commented out because P09 should be two-phase input/output UPSes */ +/* { "output.L2-L3.voltage", 0, NULL, "Q3OV\r", "", 37, '(', "", 31, 35, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, *//* P09 *//* Commented out because P09 should be two-phase input/output UPSes */ + + /* From Q3OC */ + { "output.L1.current", 0, NULL, "Q3OC\r", "", 13, '(', "", 1, 3, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.L2.current", 0, NULL, "Q3OC\r", "", 13, '(', "", 5, 7, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.L3.current", 0, NULL, "Q3OC\r", "", 13, '(', "", 9, 11, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, + + /* From Q3LD */ + { "output.L1.power.percent", 0, NULL, "Q3LD\r", "", 13, '(', "", 1, 3, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.L2.power.percent", 0, NULL, "Q3LD\r", "", 13, '(', "", 5, 7, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.L3.power.percent", 0, NULL, "Q3LD\r", "", 13, '(', "", 9, 11, "%.0f", QX_FLAG_SKIP, NULL, NULL, NULL }, + + /* From Q3YV */ + { "output.bypass.L1-N.voltage", 0, NULL, "Q3YV\r", "", 37, '(', "", 1, 5, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.bypass.L2-N.voltage", 0, NULL, "Q3YV\r", "", 37, '(', "", 7, 11, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.bypass.L3-N.voltage", 0, NULL, "Q3YV\r", "", 37, '(', "", 13, 17, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.bypass.L1-N.voltage", 0, NULL, "Q3YV\r", "", 19, '(', "", 1, 5, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, /* P09 */ + { "output.bypass.L2-N.voltage", 0, NULL, "Q3YV\r", "", 19, '(', "", 7, 11, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, /* P09 */ +/* { "output.bypass.L3-N.voltage", 0, NULL, "Q3YV\r", "", 19, '(', "", 13, 17, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, *//* P09 *//* Commented out because P09 should be two-phase input/output UPSes */ + { "output.bypass.L1-L2.voltage", 0, NULL, "Q3YV\r", "", 37, '(', "", 19, 23, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.bypass.L2-L3.voltage", 0, NULL, "Q3YV\r", "", 37, '(', "", 25, 29, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + { "output.bypass.L1-L3.voltage", 0, NULL, "Q3YV\r", "", 37, '(', "", 31, 35, "%.1f", QX_FLAG_SKIP, NULL, NULL, NULL }, + + /* Query UPS for capability - total options available: 23; only those whom the UPS is capable of are reported as Enabled or Disabled + * > [QFLAG\r] + * < [(EpashcDbroegfl\r] + * 0123456789012345 + * 0 1 * min length = ( + E + D + \r = 4 + */ + + { "ups.start.auto", ST_FLAG_RW, voltronic_e_cap, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM, NULL, NULL, voltronic_capability }, + { "battery.protection", ST_FLAG_RW, voltronic_e_cap, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM, NULL, NULL, voltronic_capability }, + { "battery.energysave", ST_FLAG_RW, voltronic_e_cap, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM, NULL, NULL, voltronic_capability }, + { "ups.start.battery", ST_FLAG_RW, voltronic_e_cap, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM, NULL, NULL, voltronic_capability }, + { "outlet.0.switchable", ST_FLAG_RW, voltronic_e_cap, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM, NULL, NULL, voltronic_capability }, + /* Not available in NUT */ + { "bypass_alarm", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, + { "battery_alarm", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, + { "bypass_when_off", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, + { "alarm_control", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, + { "converter_mode", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, + { "eco_mode", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, + { "battery_open_status_check", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, + { "bypass_forbidding", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, + { "site_fault_detection", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, + { "advanced_eco_mode", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, + { "constant_phase_angle", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, + { "limited_runtime_on_battery", 0, NULL, "QFLAG\r", "", 4, '(', "", 1, 0, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_capability }, + + /* Enable or Disable or Reset to safe default values capability options + * > [PEX\r] > [PDX\r] > [PF\r] + * < [(ACK\r] < [(ACK\r] < [(ACK\r] + * 01234 01234 01234 + * 0 0 0 + */ + + { "ups.start.auto", 0, voltronic_e_cap, "P%sR\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set }, + { "battery.protection", 0, voltronic_e_cap, "P%sS\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set }, + { "battery.energysave", 0, voltronic_e_cap, "P%sG\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set }, + { "ups.start.battery", 0, voltronic_e_cap, "P%sC\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set }, + { "outlet.0.switchable", 0, voltronic_e_cap, "P%sJ\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set }, + /* Not available in NUT */ + { "reset_to_default", 0, NULL, "PF\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_reset }, + { "bypass_alarm", 0, voltronic_e_cap_nonut, "P%sP\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, + { "battery_alarm", 0, voltronic_e_cap_nonut, "P%sB\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, + { "bypass_when_off", 0, voltronic_e_cap_nonut, "P%sO\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, + { "alarm_control", 0, voltronic_e_cap_nonut, "P%sA\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, + { "converter_mode", 0, voltronic_e_cap_nonut, "P%sV\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, + { "eco_mode", 0, voltronic_e_cap_nonut, "P%sE\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, + { "battery_open_status_check", 0, voltronic_e_cap_nonut, "P%sD\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, + { "bypass_forbidding", 0, voltronic_e_cap_nonut, "P%sF\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, + { "site_fault_detection", 0, voltronic_e_cap_nonut, "P%sL\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, + { "advanced_eco_mode", 0, voltronic_e_cap_nonut, "P%sN\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, + { "constant_phase_angle", 0, voltronic_e_cap_nonut, "P%sQ\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, + { "limited_runtime_on_battery", 0, voltronic_e_cap_nonut, "P%sW\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_capability_set_nonut }, + + /* Query UPS for programmable outlet (1-4) status + * > [QSK1\r] + * < [(1\r] <- if outlet is on -> (1 , if off -> (0 ; (NAK -> outlet is not programmable + * 012 + * 0 + */ + + { "outlet.1.switchable", 0, NULL, "QSK1\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, + { "outlet.1.status", 0, NULL, "QSK1\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, + { "outlet.2.switchable", 0, NULL, "QSK2\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, + { "outlet.2.status", 0, NULL, "QSK2\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, + { "outlet.3.switchable", 0, NULL, "QSK3\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, + { "outlet.3.status", 0, NULL, "QSK3\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, + { "outlet.4.switchable", 0, NULL, "QSK4\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, + { "outlet.4.status", 0, NULL, "QSK4\r", "", 3, '(', "", 1, 1, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet }, + + /* Query UPS for programmable outlet n (1-4) delay time before it shuts down the load when on battery mode + * > [QSKT1\r] + * < [(008\r] <- if delay time is set (by PSK[1-4]n) it'll report n (minutes) otherwise it'll report (NAK (also if outlet is not programmable) + * 01234 + * 0 + */ + + { "outlet.1.delay.shutdown", ST_FLAG_RW, voltronic_r_outlet_delay, "QSKT1\r", "", 5, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay }, + { "outlet.2.delay.shutdown", ST_FLAG_RW, voltronic_r_outlet_delay, "QSKT2\r", "", 5, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay }, + { "outlet.3.delay.shutdown", ST_FLAG_RW, voltronic_r_outlet_delay, "QSKT3\r", "", 5, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay }, + { "outlet.4.delay.shutdown", ST_FLAG_RW, voltronic_r_outlet_delay, "QSKT4\r", "", 5, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay }, + + /* Set delay time for programmable outlets + * > [PSK1nnn\r] n = 0..9 + * < [(ACK\r] + * 01234 + * 0 + */ + + { "outlet.1.delay.shutdown", 0, voltronic_r_outlet_delay, "PSK1%03d\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay_set }, + { "outlet.2.delay.shutdown", 0, voltronic_r_outlet_delay, "PSK2%03d\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay_set }, + { "outlet.3.delay.shutdown", 0, voltronic_r_outlet_delay, "PSK3%03d\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay_set }, + { "outlet.4.delay.shutdown", 0, voltronic_r_outlet_delay, "PSK4%03d\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_outlet_delay_set }, + + /* Query UPS for ECO Mode voltage limits + * > [QHE\r] + * < [(242 218\r] + * 012345678 + * 0 + */ + + { "input.transfer.high", ST_FLAG_RW, voltronic_r_eco_volt_max, "QHE\r", "", 9, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_volt }, + { "input.transfer.low", ST_FLAG_RW, voltronic_r_eco_volt_min, "QHE\r", "", 9, '(', "", 5, 7, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_volt }, + { "input.transfer.low.min", 0, NULL, "QHE\r", "", 9, '(', "", 5, 7, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_volt_range }, + { "input.transfer.low.max", 0, NULL, "QHE\r", "", 9, '(', "", 5, 7, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_volt_range }, + { "input.transfer.high.min", 0, NULL, "QHE\r", "", 9, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_volt_range }, + { "input.transfer.high.max", 0, NULL, "QHE\r", "", 9, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_volt_range }, + + /* Set ECO Mode voltage limits + * > [HEHnnn\r] > [HELnnn\r] n = 0..9 + * < [(ACK\r] < [(ACK\r] + * 01234 01234 + * 0 0 + */ + + { "input.transfer.high", 0, voltronic_r_eco_volt_max, "HEH%03.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, + { "input.transfer.low", 0, voltronic_r_eco_volt_min, "HEL%03.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, + + /* Query UPS for ECO Mode frequency limits + * > [QFRE\r] + * < [(53.0 47.0\r] + * 01234567890 + * 0 1 + */ + + { "input.frequency.high", ST_FLAG_RW, voltronic_r_eco_freq_max, "QFRE\r", "", 11, '(', "", 1, 4, "%.1f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_freq }, + { "input.frequency.low", ST_FLAG_RW, voltronic_r_eco_freq_min, "QFRE\r", "", 11, '(', "", 6, 9, "%.1f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_eco_freq }, + + /* Set ECO Mode frequency limits + * > [FREHnn.n\r] > [FRELnn.n\r] n = 0..9 + * < [(ACK\r] < [(ACK\r] + * 01234 01234 + * 0 0 + */ + + { "input.frequency.high", 0, voltronic_r_eco_freq_max, "FREH%04.1f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, + { "input.frequency.low", 0, voltronic_r_eco_freq_min, "FREL%04.1f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, + + /* Query UPS for Bypass Mode voltage limits + * > [QBYV\r] + * < [(264 170\r] + * 012345678 + * 0 + */ + + { "max_bypass_volt", ST_FLAG_RW, voltronic_r_bypass_volt_max, "QBYV\r", "", 9, '(', "", 1, 3, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_bypass }, + { "min_bypass_volt", ST_FLAG_RW, voltronic_r_bypass_volt_min, "QBYV\r", "", 9, '(', "", 5, 7, "%.0f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_bypass }, + + /* Set Bypass Mode voltage limits + * > [PHVnnn\r] > [PLVnnn\r] n = 0..9 + * < [(ACK\r] < [(ACK\r] + * 01234 01234 + * 0 0 + */ + + { "max_bypass_volt", 0, voltronic_r_bypass_volt_max, "PHV%03.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, + { "min_bypass_volt", 0, voltronic_r_bypass_volt_min, "PLV%03.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, + + /* Query UPS for Bypass Mode frequency limits + * > [QBYF\r] + * < [(53.0 47.0\r] + * 01234567890 + * 0 1 + */ + + { "max_bypass_freq", ST_FLAG_RW, voltronic_r_bypass_freq_max, "QBYF\r", "", 11, '(', "", 1, 4, "%.1f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_bypass }, + { "min_bypass_freq", ST_FLAG_RW, voltronic_r_bypass_freq_min, "QBYF\r", "", 11, '(', "", 6, 9, "%.1f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_bypass }, + + /* Set Bypass Mode frequency limits + * > [PGFnn.n\r] > [PSFnn.n\r] n = 0..9 + * < [(ACK\r] < [(ACK\r] + * 01234 01234 + * 0 0 + */ + + { "max_bypass_freq", 0, voltronic_r_bypass_freq_max, "PGF%04.1f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, + { "min_bypass_freq", 0, voltronic_r_bypass_freq_min, "PSF%04.1f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_process_setvar }, + + /* Set number of batteries that make a pack to n (integer, 1-9). NOTE: changing the number of batteries will change the UPS's estimation on battery charge/runtime + * > [BATNn\r] + * < [(ACK\r] + * 01234 + * 0 + */ + + { "battery_number", 0, voltronic_r_batt_numb, "BATN%1.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE | QX_FLAG_NONUT, NULL, NULL, voltronic_process_setvar }, + + /* Set number of battery packs in parallel to n (integer, 01-99). NOTE: changing the number of battery packs will change the UPS's estimation on battery charge/runtime + * > [BATGNn\r] + * < [(ACK\r] + * 01234 + * 0 + */ + + { "battery.packs", 0, voltronic_r_batt_packs, "BATGN%02.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, voltronic_process_setvar }, + + /* Query UPS for battery type (Only P31) + * > [QBT\r] + * < [(01\r] <- 00="Li", 01="Flooded" or 02="AGM" + * 0123 + * 0 + */ + + { "battery.type", ST_FLAG_RW, voltronic_e_batt_type, "QBT\r", "", 4, '(', "", 1, 2, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_p31b }, + + /* Set battery type (Only P31) + * > [PBTnn\r] nn = 00/01/02 + * < [(ACK\r] + * 01234 + * 0 + */ + + { "battery.type", 0, voltronic_e_batt_type, "PBT%02.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_SKIP, NULL, NULL, voltronic_p31b_set }, + + /* Query UPS for device grid working range (Only P31) + * > [QGR\r] + * < [(01\r] <- 00=Appliance, 01=UPS + * 0123 + * 0 + */ + + { "work_range_type", ST_FLAG_RW, voltronic_e_work_range, "QGR\r", "", 4, '(', "", 1, 2, "%s", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_p31g }, + + /* Set device grid working range type (Only P31) + * > [PBTnn\r] nn = 00/01 + * < [(ACK\r] + * 01234 + * 0 + */ + + { "work_range_type", 0, voltronic_e_work_range, "PGR%02.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_p31g_set }, + + /* Query UPS for battery low voltage + * > [RE0\r] + * < [#20\r] + * 012 + * 0 + */ + + { "battery.voltage.low", ST_FLAG_RW, voltronic_r_batt_low, "RE0\r", "", 3, '#', "", 1, 2, "%.1f", QX_FLAG_SEMI_STATIC | QX_FLAG_RANGE, NULL, NULL, NULL }, + + /* Set voltage for battery low to n (integer, 20..24/20..28). NOTE: changing the battery low voltage will change the UPS's estimation on battery charge/runtime + * > [W0En\r] + * < [(ACK\r] + * 01234 + * 0 + */ + + { "battery.voltage.low", 0, voltronic_r_batt_low, "W0E%02.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, voltronic_process_setvar }, + + /* Query UPS for Phase Angle + * > [QPD\r] + * < [(000 120\r] <- Input Phase Angle - Output Phase Angle + * 012345678 + * 0 + */ + + { "input_phase_angle", 0, NULL, "QPD\r", "", 9, '(', "", 1, 3, "%03d", QX_FLAG_SEMI_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_phase }, + { "output_phase_angle", ST_FLAG_RW, voltronic_e_phase, "QPD\r", "", 9, '(', "", 5, 7, "%03d", QX_FLAG_SEMI_STATIC | QX_FLAG_ENUM | QX_FLAG_NONUT, NULL, NULL, voltronic_phase }, + + /* Set output phase angle + * > [PPDn\r] n = (000, 120, 180 or 240) + * < [(ACK\r] + * 01234 + * 0 + */ + + { "output_phase_angle", 0, voltronic_e_phase, "PPD%03.0f\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_SETVAR | QX_FLAG_ENUM | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, voltronic_phase_set }, + + /* Query UPS for master/slave for a system of UPSes in parallel + * > [QPAR\r] + * < [(001\r] <- 001 for master UPS, 002 and 003 for slave UPSes + * 01234 + * 0 + */ + + { "voltronic_parallel", 0, NULL, "QPAR\r", "", 5, '(', "", 1, 3, "%s", QX_FLAG_STATIC | QX_FLAG_NONUT, NULL, NULL, voltronic_parallel }, + + /* Query UPS for ?? + * > [QBDR\r] + * < [(1234\r] <- unknown reply - My UPS (NAK at me + * 012345 + * 0 + */ + + { "unknown.7", 0, NULL, "QBDR\r", "", 5, '(', "", 1, 0, "%s", QX_FLAG_STATIC | QX_FLAG_NONUT | QX_FLAG_SKIP, NULL, NULL, NULL }, + + /* Instant commands */ + { "load.off", 0, NULL, "SOFF\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "SON\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + { "shutdown.return", 0, NULL, "S%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, voltronic_process_command }, + { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, voltronic_process_command }, + { "shutdown.stop", 0, NULL, "CS\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + { "test.battery.start", 0, NULL, "T%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, voltronic_process_command }, + { "test.battery.start.deep", 0, NULL, "TL\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.stop", 0, NULL, "CT\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + { "beeper.toggle", 0, NULL, "BZ%s\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD, NULL, NULL, voltronic_process_command }, + /* Enable/disable beeper: unskipped if the UPS can control alarm (capability) */ + { "beeper.enable", 0, NULL, "PEA\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + { "beeper.disable", 0, NULL, "PDA\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + + /* Outlet control: unskipped if the outlets are manageable */ + { "outlet.1.load.off", 0, NULL, "SKOFF1\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + { "outlet.1.load.on", 0, NULL, "SKON1\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + { "outlet.2.load.off", 0, NULL, "SKOFF2\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + { "outlet.2.load.on", 0, NULL, "SKON2\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + { "outlet.3.load.off", 0, NULL, "SKOFF3\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + { "outlet.3.load.on", 0, NULL, "SKON3\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + { "outlet.4.load.off", 0, NULL, "SKOFF4\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + { "outlet.4.load.on", 0, NULL, "SKON4\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + + /* Bypass: unskipped if the UPS is capable of ECO Mode */ + { "bypass.start", 0, NULL, "PEE\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + { "bypass.stop", 0, NULL, "PDE\r", "", 5, '(', "", 1, 3, NULL, QX_FLAG_CMD | QX_FLAG_SKIP, NULL, NULL, NULL }, + + /* Server-side settable vars */ + { "ups.delay.start", ST_FLAG_RW, voltronic_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, voltronic_process_setvar }, + { "ups.delay.shutdown", ST_FLAG_RW, voltronic_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, voltronic_process_setvar }, + + /* End of structure. */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + + +/* == Testing table == */ +#ifdef TESTING +static testing_t voltronic_testing[] = { + { "QGS\r", "(234.9 50.0 229.8 50.0 000.0 00A 369.1 ---.- 026.5 ---.- 018.8 100000000001\r", -1 }, + { "QPI\r", "(PI01\r", -1 }, + { "QRI\r", "(230.0 004 024.0 50.0\r", -1 }, + { "QMF\r", "(#####VOLTRONIC\r", -1 }, + { "I\r", "#------------- ------ VT12046Q \r", -1 }, + { "F\r", "#220.0 000 024.0 50.0\r", -1 }, + { "QMD\r", "(#######OLHVT1K0 ###1000 80 2/2 230 230 02 12.0\r", -1 }, + { "QFS\r", "(14 212.1 50.0 005.6 49.9 006 010.6 343.8 ---.- 026.2 021.8 01101100\r", -1 }, + { "QMOD\r", "(S\r", -1 }, + { "QVFW\r", "(VERFW:00322.02\r", -1 }, + { "QID\r", "(685653211455\r", -1 }, + { "QBV\r", "(026.5 02 01 068 255\r", -1 }, + { "QFLAG\r", "(EpashcjDbroegfl\r", -1 }, + { "QWS\r", "(0000000000000000000000000000000000000000000000000000000001000001\r", -1 }, + { "QHE\r", "(242 218\r", -1 }, + { "QBYV\r", "(264 170\r", -1 }, + { "QBYF\r", "(53.0 47.0\r", -1 }, + { "QSK1\r", "(1\r", -1 }, + { "QSK2\r", "(0\r", -1 }, + { "QSK3\r", "(1\r", -1 }, + { "QSK4\r", "(NAK\r", -1 }, + { "QSKT1\r", "(008\r", -1 }, + { "QSKT2\r", "(012\r", -1 }, + { "QSKT3\r", "(NAK\r", -1 }, + { "QSKT4\r", "(007\r", -1 }, + { "RE0\r", "#20\r", -1 }, + { "W0E24\r", "(ACK\r", -1 }, + { "PF\r", "(ACK\r", -1 }, + { "PEA\r", "(ACK\r", -1 }, + { "PDR\r", "(NAK\r", -1 }, + { "HEH250\r", "(ACK\r", -1 }, + { "HEL210\r", "(ACK\r", -1 }, + { "PHV260\r", "(NAK\r", -1 }, + { "PLV190\r", "(ACK\r", -1 }, + { "PGF51.0\r", "(NAK\r", -1 }, + { "PSF47.5\r", "(ACK\r", -1 }, + { "BATN2\r", "(ACK\r", -1 }, + { "BATGN04\r", "(ACK\r", -1 }, + { "QBT\r", "(01\r", -1 }, + { "PBT02\r", "(ACK\r", -1 }, + { "QGR\r", "(00\r", -1 }, + { "PGR01\r", "(ACK\r", -1 }, + { "PSK1008\r", "(ACK\r", -1 }, + { "PSK3987\r", "(ACK\r", -1 }, + { "PSK2009\r", "(ACK\r", -1 }, + { "PSK4012\r", "(ACK\r", -1 }, + { "Q3PV\r", "(123.4 456.4 789.4 012.4 323.4 223.4\r", -1 }, + { "Q3OV\r", "(253.4 163.4 023.4 143.4 103.4 523.4\r", -1 }, + { "Q3OC\r", "(109 069 023\r", -1 }, + { "Q3LD\r", "(005 033 089\r", -1 }, + { "Q3YV\r", "(303.4 245.4 126.4 222.4 293.4 321.4\r", -1 }, + { "Q3PC\r", "(002 023 051\r", -1 }, + { "SOFF\r", "(NAK\r", -1 }, + { "SON\r", "(ACK\r", -1 }, + { "T\r", "(NAK\r", -1 }, + { "TL\r", "(ACK\r", -1 }, + { "CS\r", "(ACK\r", -1 }, + { "CT\r", "(NAK\r", -1 }, + { "BZOFF\r", "(ACK\r", -1 }, + { "BZON\r", "(ACK\r", -1 }, + { "S.3R0002\r", "(ACK\r", -1 }, + { "S02R0024\r", "(NAK\r", -1 }, + { "S.5\r", "(ACK\r", -1 }, + { "T.3\r", "(ACK\r", -1 }, + { "T02\r", "(NAK\r", -1 }, + { "SKON1\r", "(ACK\r", -1 }, + { "SKOFF1\r", "(NAK\r", -1 }, + { "SKON2\r", "(ACK\r", -1 }, + { "SKOFF2\r", "(ACK\r", -1 }, + { "SKON3\r", "(NAK\r", -1 }, + { "SKOFF3\r", "(ACK\r", -1 }, + { "SKON4\r", "(NAK\r", -1 }, + { "SKOFF4\r", "(NAK\r", -1 }, + { "QPAR\r", "(003\r", -1 }, + { "QPD\r", "(000 240\r", -1 }, + { "PPD120\r", "(ACK\r", -1 }, + { "QLDL\r", "(005 080\r", -1 }, + { "QBDR\r", "(1234\r", -1 }, + { "QFRE\r", "(50.0 00.0\r", -1 }, + { "FREH54.0\r", "(ACK\r", -1 }, + { "FREL47.0\r", "(ACK\r", -1 }, + { "PEP\r", "(ACK\r", -1 }, + { "PDP\r", "(ACK\r", -1 }, + { "PEB\r", "(ACK\r", -1 }, + { "PDB\r", "(ACK\r", -1 }, + { "PER\r", "(NAK\r", -1 }, + { "PDR\r", "(NAK\r", -1 }, + { "PEO\r", "(ACK\r", -1 }, + { "PDO\r", "(ACK\r", -1 }, + { "PEA\r", "(ACK\r", -1 }, + { "PDA\r", "(ACK\r", -1 }, + { "PES\r", "(ACK\r", -1 }, + { "PDS\r", "(ACK\r", -1 }, + { "PEV\r", "(ACK\r", -1 }, + { "PDV\r", "(ACK\r", -1 }, + { "PEE\r", "(ACK\r", -1 }, + { "PDE\r", "(ACK\r", -1 }, + { "PEG\r", "(ACK\r", -1 }, + { "PDG\r", "(NAK\r", -1 }, + { "PED\r", "(ACK\r", -1 }, + { "PDD\r", "(ACK\r", -1 }, + { "PEC\r", "(ACK\r", -1 }, + { "PDC\r", "(NAK\r", -1 }, + { "PEF\r", "(NAK\r", -1 }, + { "PDF\r", "(ACK\r", -1 }, + { "PEJ\r", "(NAK\r", -1 }, + { "PDJ\r", "(ACK\r", -1 }, + { "PEL\r", "(ACK\r", -1 }, + { "PDL\r", "(ACK\r", -1 }, + { "PEN\r", "(ACK\r", -1 }, + { "PDN\r", "(ACK\r", -1 }, + { "PEQ\r", "(ACK\r", -1 }, + { "PDQ\r", "(ACK\r", -1 }, + { "PEW\r", "(NAK\r", -1 }, + { "PDW\r", "(ACK\r", -1 }, + { NULL } +}; +#endif /* TESTING */ + + +/* == Support functions == */ + +/* This function allows the subdriver to "claim" a device: return 1 if the device is supported by this subdriver, else 0. */ +static int voltronic_claim(void) +{ + + /* We need at least QGS and QPI to run this subdriver */ + + item_t *item = find_nut_info("input.voltage", 0, 0); + + /* Don't know what happened */ + if (!item) + return 0; + + /* No reply/Unable to get value */ + if (qx_process(item, NULL)) + return 0; + + /* Unable to process value */ + if (ups_infoval_set(item) != 1) + return 0; + + /* UPS Protocol */ + item = find_nut_info("ups.firmware.aux", 0, 0); + + /* Don't know what happened */ + if (!item) { + dstate_delinfo("input.voltage"); + return 0; + } + + /* No reply/Unable to get value */ + if (qx_process(item, NULL)) { + dstate_delinfo("input.voltage"); + return 0; + } + + /* Unable to process value/Protocol out of range */ + if (ups_infoval_set(item) != 1) { + dstate_delinfo("input.voltage"); + return 0; + } + + return 1; + +} + +/* Subdriver-specific flags/vars */ +static void voltronic_makevartable(void) +{ + /* Capability vars */ + addvar(VAR_FLAG, "reset_to_default", "Reset capability options and their limits to safe default values"); + addvar(VAR_VALUE, "bypass_alarm", "Alarm (BEEP!) at Bypass Mode [enabled/disabled]"); + addvar(VAR_VALUE, "battery_alarm", "Alarm (BEEP!) at Battery Mode [enabled/disabled]"); + addvar(VAR_VALUE, "bypass_when_off", "Bypass when the UPS is Off [enabled/disabled]"); + addvar(VAR_VALUE, "alarm_control", "Alarm (BEEP!) Control [enabled/disabled]"); + addvar(VAR_VALUE, "converter_mode", "Converter Mode [enabled/disabled]"); + addvar(VAR_VALUE, "eco_mode", "ECO Mode [enabled/disabled]"); + addvar(VAR_VALUE, "battery_open_status_check", "Battery Open Status Check [enabled/disabled]"); + addvar(VAR_VALUE, "bypass_forbidding", "Bypass not allowed (Bypass Forbidding) [enabled/disabled]"); + addvar(VAR_VALUE, "site_fault_detection", "Site fault detection [enabled/disabled]"); + addvar(VAR_VALUE, "advanced_eco_mode", "Advanced ECO Mode [enabled/disabled]"); + addvar(VAR_VALUE, "constant_phase_angle", "Constant Phase Angle Function (Output and input phase angles are not equal) [enabled/disabled]"); + addvar(VAR_VALUE, "limited_runtime_on_battery", "Limited runtime on battery mode [enabled/disabled]"); + + /* Bypass Mode frequency/voltage limits */ + addvar(VAR_VALUE, "max_bypass_volt", "Maximum voltage for Bypass Mode"); + addvar(VAR_VALUE, "min_bypass_volt", "Minimum voltage for Bypass Mode"); + addvar(VAR_VALUE, "max_bypass_freq", "Maximum frequency for Bypass Mode"); + addvar(VAR_VALUE, "min_bypass_freq", "Minimum frequency for Bypass Mode"); + + /* Device grid working range type for P31 UPSes */ + addvar(VAR_VALUE, "work_range_type", "Device grid working range for P31 UPSes [Appliance/UPS]"); + + /* Output phase angle */ + addvar(VAR_VALUE, "output_phase_angle", "Change output phase angle to the provided value [000, 120, 180, 240]°"); + + /* Number of batteries */ + addvar(VAR_VALUE, "battery_number", "Set number of batteries that make a pack to n (integer, 1-9)"); + + /* For testing purposes */ + addvar(VAR_FLAG, "testing", "If invoked the driver will exec also commands that still need testing"); +} + +/* Unskip vars according to protocol used by the UPS */ +static void voltronic_massive_unskip(const long protocol) +{ + item_t *item; + + for (item = voltronic_qx2nut; item->info_type != NULL; item++) { + + if (!item->command) + continue; + + if ( /* == Multiphase UPSes == */ + /* P09 */ + (protocol == 9 && ( + /* (!strcasecmp(item->info_type, "input.L1-L3.voltage") && item->from == 25) || *//* Not unskipped because P09 should be 2-phase input/output */ + /* (!strcasecmp(item->info_type, "input.L2-L3.voltage") && item->from == 31) || *//* Not unskipped because P09 should be 2-phase input/output */ + /* (!strcasecmp(item->info_type, "output.L1-L3.voltage") && item->from == 25) || *//* Not unskipped because P09 should be 2-phase input/output */ + /* (!strcasecmp(item->info_type, "output.L2-L3.voltage") && item->from == 31) || *//* Not unskipped because P09 should be 2-phase input/output */ + (!strcasecmp(item->info_type, "output.bypass.L1-N.voltage") && item->answer_len == 19) || + (!strcasecmp(item->info_type, "output.bypass.L2-N.voltage") && item->answer_len == 19)/* || + (!strcasecmp(item->info_type, "output.bypass.L3-N.voltage") && item->answer_len == 19) *//* Not unskipped because P09 should be 2-phase input/output */ + )) || + /* P10 */ + (protocol == 10 && ( + !strcasecmp(item->info_type, "output.L3-N.voltage") || + (!strcasecmp(item->info_type, "output.L2-L3.voltage") && item->from == 25) || + (!strcasecmp(item->info_type, "output.L1-L3.voltage") && item->from == 31) || + (!strcasecmp(item->info_type, "output.bypass.L1-N.voltage") && item->answer_len == 37) || + (!strcasecmp(item->info_type, "output.bypass.L2-N.voltage") && item->answer_len == 37) || + (!strcasecmp(item->info_type, "output.bypass.L3-N.voltage") && item->answer_len == 37) || + !strcasecmp(item->info_type, "output.bypass.L1-L2.voltage") || + !strcasecmp(item->info_type, "output.bypass.L2-L3.voltage") || + !strcasecmp(item->info_type, "output.bypass.L1-L3.voltage") || + !strcasecmp(item->info_type, "output.L3.current") || + !strcasecmp(item->info_type, "output.L3.power.percent") + )) || + /* P09-P10 */ + ((protocol == 9 || protocol == 10) && ( + !strcasecmp(item->info_type, "output.L1-N.voltage") || + !strcasecmp(item->info_type, "output.L2-N.voltage") ||/* + !strcasecmp(item->info_type, "output.L3-N.voltage") || *//* Not unskipped because P09 should be 2-phase input/output */ + !strcasecmp(item->info_type, "output.L1-L2.voltage") || + !strcasecmp(item->info_type, "output.L1.current") || + !strcasecmp(item->info_type, "output.L2.current") ||/* + !strcasecmp(item->info_type, "output.L3.current") || *//* Not unskipped because P09 should be 2-phase input/output */ + !strcasecmp(item->info_type, "output.L1.power.percent") || + !strcasecmp(item->info_type, "output.L2.power.percent")/* || + !strcasecmp(item->info_type, "output.L3.power.percent") *//* Not unskipped because P09 should be 2-phase input/output */ + )) || + /* P03-P09-P10 */ + ((protocol == 3 || protocol == 9 || protocol == 10) && ( + !strcasecmp(item->info_type, "input.L1-N.voltage") || + !strcasecmp(item->info_type, "input.L2-N.voltage") ||/* + !strcasecmp(item->info_type, "input.L3-N.voltage") ||*//* Not unskipped because P09 should be 2-phase input/output */ + !strcasecmp(item->info_type, "input.L1-L2.voltage") || + !strcasecmp(item->info_type, "input.L1.current") || + !strcasecmp(item->info_type, "input.L2.current")/* || + !strcasecmp(item->info_type, "input.L3.current")*//* Not unskipped because P09 should be 2-phase input/output */ + )) || + /* P03-P10 */ + ((protocol == 3 || protocol == 10) && ( + !strcasecmp(item->info_type, "input.L3-N.voltage") || + (!strcasecmp(item->info_type, "input.L2-L3.voltage") && item->from == 25) || + (!strcasecmp(item->info_type, "input.L1-L3.voltage") && item->from == 31) || + !strcasecmp(item->info_type, "input.L3.current") + )) || + /* == P31 battery type/device grid working range == */ + (protocol == 31 && ( + !strcasecmp(item->info_type, "battery.type") || + (!strcasecmp(item->info_type, "work_range_type") && !(item->qxflags & QX_FLAG_SETVAR)) || + (!strcasecmp(item->info_type, "work_range_type") && (item->qxflags & QX_FLAG_SETVAR) && getval(item->info_type)) + )) || + /* == ByPass limits: all but P00/P08/P31 == */ + (protocol != 0 && protocol != 8 && protocol != 31 && ( + (!strcasecmp(item->info_type, "max_bypass_volt") && !(item->qxflags & QX_FLAG_SETVAR)) || + (!strcasecmp(item->info_type, "min_bypass_volt") && !(item->qxflags & QX_FLAG_SETVAR)) || + (!strcasecmp(item->info_type, "max_bypass_freq") && !(item->qxflags & QX_FLAG_SETVAR)) || + (!strcasecmp(item->info_type, "min_bypass_freq") && !(item->qxflags & QX_FLAG_SETVAR)) + )) || + /* == Reset capabilities options to safe default values == */ + (!strcasecmp(item->info_type, "reset_to_default") && testvar("reset_to_default")) || + /* == QBDR (unknown) == */ + (!strcasecmp(item->info_type, "unknown.7") && testvar("testing")) + ) { + + item->qxflags &= ~QX_FLAG_SKIP; + + } + + } +} + + +/* == Preprocess functions == */ + +/* *SETVAR(/NONUT)* Preprocess setvars */ +static int voltronic_process_setvar(item_t *item, char *value, const size_t valuelen) +{ + double val; + + if (!strlen(value)) { + upsdebugx(2, "%s: value not given for %s", __func__, item->info_type); + return -1; + } + + val = strtod(value, NULL); + + if (!strcasecmp(item->info_type, "ups.delay.start")) { + + /* Truncate to minute */ + val -= ((int)val % 60); + + snprintf(value, valuelen, "%.0f", val); + + return 0; + + } else if (!strcasecmp(item->info_type, "ups.delay.shutdown")) { + + /* Truncate to nearest setable value */ + if (val < 60) { + val -= ((int)val % 6); + } else { + val -= ((int)val % 60); + } + + snprintf(value, valuelen, "%.0f", val); + + return 0; + + } else if (!strcasecmp(item->info_type, "max_bypass_freq")) { + + if (d_equal(val, max_bypass_freq)) { + upslogx(LOG_INFO, "%s is already set to %.1f", item->info_type, val); + return -1; + } + + } else if (!strcasecmp(item->info_type, "min_bypass_freq")) { + + if (d_equal(val, min_bypass_freq)) { + upslogx(LOG_INFO, "%s is already set to %.1f", item->info_type, val); + return -1; + } + + } else if (!strcasecmp(item->info_type, "max_bypass_volt")) { + + if (d_equal(val, max_bypass_volt)) { + upslogx(LOG_INFO, "%s is already set to %.0f", item->info_type, val); + return -1; + } + + } else if (!strcasecmp(item->info_type, "min_bypass_volt")) { + + if (d_equal(val, min_bypass_volt)) { + upslogx(LOG_INFO, "%s is already set to %.0f", item->info_type, val); + return -1; + } + + } else if (!strcasecmp(item->info_type, "battery_number")) { + + if (d_equal(val, battery_number)) { + upslogx(LOG_INFO, "%s is already set to %.0f", item->info_type, val); + return -1; + } + + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->command, val); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* *CMD* Preprocess instant commands */ +static int voltronic_process_command(item_t *item, char *value, const size_t valuelen) +{ + char buf[SMALLBUF] = ""; + + if (!strcasecmp(item->info_type, "shutdown.return")) { + + /* Sn: Shutdown after n minutes and then turn on when mains is back + * SnRm: Shutdown after n minutes and then turn on after m minutes + * Accepted values for n: .2 -> .9 , 01 -> 99 + * Accepted values for m: 0001 -> 9999 */ + + long offdelay = strtol(dstate_getinfo("ups.delay.shutdown"), NULL, 10), + ondelay = strtol(dstate_getinfo("ups.delay.start"), NULL, 10) / 60; + + if (ondelay == 0) { + + if (offdelay < 60) { + snprintf(buf, sizeof(buf), ".%ld", offdelay / 6); + } else { + snprintf(buf, sizeof(buf), "%02ld", offdelay / 60); + } + + } else if (offdelay < 60) { + + snprintf(buf, sizeof(buf), ".%ldR%04ld", offdelay / 6, ondelay); + + } else { + + snprintf(buf, sizeof(buf), "%02ldR%04ld", offdelay / 60, ondelay); + + } + + } else if (!strcasecmp(item->info_type, "shutdown.stayoff")) { + + /* SnR0000 + * Shutdown after n minutes and stay off + * Accepted values for n: .2 -> .9 , 01 -> 99 */ + + long offdelay = strtol(dstate_getinfo("ups.delay.shutdown"), NULL, 10); + + if (offdelay < 60) { + snprintf(buf, sizeof(buf), ".%ld", offdelay / 6); + } else { + snprintf(buf, sizeof(buf), "%02ld", offdelay / 60); + } + + } else if (!strcasecmp(item->info_type, "test.battery.start")) { + + /* Accepted values for test time: .2 -> .9 (.2=12sec ..), 01 -> 99 (minutes) + * -> you have to invoke test.battery.start + number of seconds [12..5940] */ + + long delay; + + if (strlen(value) != strspn(value, "0123456789")) { + upslogx(LOG_ERR, + "%s: non numerical value [%s]", + item->info_type, value); + return -1; + } + + delay = (strlen(value) > 0) ? strtol(value, NULL, 10) : 600; + + if ((delay < 12) || (delay > 5940)) { + upslogx(LOG_ERR, + "%s: battery test time '%ld' out of range [12..5940] seconds", + item->info_type, delay); + return -1; + } + + /* test time < 1 minute */ + if (delay < 60) { + + delay = delay / 6; + snprintf(buf, sizeof(buf), ".%ld", delay); + + /* test time > 1 minute */ + } else { + + delay = delay / 60; + snprintf(buf, sizeof(buf), "%02ld", delay); + + } + + } else if (!strcasecmp(item->info_type, "beeper.toggle")) { + + const char *beeper_status = dstate_getinfo("ups.beeper.status"); + + /* If the UPS is beeping then we can call BZOFF; if we previously set BZOFF we can call BZON, provided that the beeper is not disabled */ + + /* The UPS can disable/enable alarm (from UPS capability) */ + if (alarm_control) { + + if (!strcmp(beeper_status, "enabled")) { + + snprintf(buf, sizeof(buf), "OFF"); + + } else if (!strcmp(beeper_status, "muted")) { + + snprintf(buf, sizeof(buf), "ON"); + + /* Beeper disabled */ + } else { + + upslogx(LOG_INFO, "The beeper is already disabled"); + return -1; + + } + + /* The UPS can't disable/enable alarm (from UPS capability) */ + } else { + + if (!strcmp(beeper_status, "enabled")) { + + snprintf(buf, sizeof(buf), "OFF"); + + } else if (!strcmp(beeper_status, "disabled")) { + + snprintf(buf, sizeof(buf), "ON"); + + } + + } + + } else { + + /* Don't know what happened */ + return -1; + + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->command, buf); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* UPS capabilities */ +static int voltronic_capability(item_t *item, char *value, const size_t valuelen) +{ + char rawval[SMALLBUF], *enabled, *disabled, *val = NULL, *saveptr = NULL; + item_t *unskip; + + snprintf(rawval, sizeof(rawval), "%s", item->value); + + enabled = strtok_r(rawval+1, "D", &saveptr); + disabled = strtok_r(NULL, "\0", &saveptr); + + if (!enabled && !disabled) { + upsdebugx(2, "%s: parsing failed", __func__); + return -1; + } + + enabled = enabled ? enabled : ""; + disabled = disabled ? disabled : ""; + + /* NONUT items */ + if (!strcasecmp(item->info_type, "bypass_alarm")) { + + if (strchr(enabled, 'p')) { + val = bypass_alarm = "enabled"; + } else if (strchr(disabled, 'p')) { + val = bypass_alarm = "disabled"; + } + + } else if (!strcasecmp(item->info_type, "battery_alarm")) { + + if (strchr(enabled, 'b')) { + val = battery_alarm = "enabled"; + } else if (strchr(disabled, 'b')) { + val = battery_alarm = "disabled"; + } + + } else if (!strcasecmp(item->info_type, "bypass_when_off")) { + + if (strchr(enabled, 'o')) { + val = bypass_when_off = "enabled"; + } else if (strchr(disabled, 'o')) { + val = bypass_when_off = "disabled"; + } + + } else if (!strcasecmp(item->info_type, "alarm_control")) { + + if (strchr(item->value, 'a')) { + + if (strchr(enabled, 'a')) { + + const char *beeper = dstate_getinfo("ups.beeper.status"); + + val = alarm_control = "enabled"; + + if (!beeper || strcmp(beeper, "muted")) { + dstate_setinfo("ups.beeper.status", "enabled"); + } + + } else if (strchr(disabled, 'a')) { + + val = alarm_control = "disabled"; + dstate_setinfo("ups.beeper.status", "disabled"); + + } + + /* Unskip beeper.{enable,disable} instcmds */ + unskip = find_nut_info("beeper.enable", QX_FLAG_CMD, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + unskip = find_nut_info("beeper.disable", QX_FLAG_CMD, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + } + + } else if (!strcasecmp(item->info_type, "converter_mode")) { + + if (strchr(enabled, 'v')) { + val = converter_mode = "enabled"; + } else if (strchr(disabled, 'v')) { + val = converter_mode = "disabled"; + } + + } else if (!strcasecmp(item->info_type, "eco_mode")) { + + if (strchr(item->value, 'e')) { + + if (strchr(enabled, 'e')) { + val = eco_mode = "enabled"; + } else if (strchr(disabled, 'e')) { + val = eco_mode = "disabled"; + } + + /* Unskip bypass.{start,stop} instcmds */ + unskip = find_nut_info("bypass.start", QX_FLAG_CMD, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + unskip = find_nut_info("bypass.stop", QX_FLAG_CMD, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + /* Unskip input.transfer.{high,low} */ + unskip = find_nut_info("input.transfer.high", QX_FLAG_SEMI_STATIC, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + unskip = find_nut_info("input.transfer.low", QX_FLAG_SEMI_STATIC, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + /* Unskip input.frequency.{high,low} */ + unskip = find_nut_info("input.frequency.high", QX_FLAG_SEMI_STATIC, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + unskip = find_nut_info("input.frequency.low", QX_FLAG_SEMI_STATIC, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + } + + } else if (!strcasecmp(item->info_type, "battery_open_status_check")) { + + if (strchr(enabled, 'd')) { + val = battery_open_status_check = "enabled"; + } else if (strchr(disabled, 'd')) { + val = battery_open_status_check = "disabled"; + } + + } else if (!strcasecmp(item->info_type, "bypass_forbidding")) { + + if (strchr(enabled, 'f')) { + val = bypass_forbidding = "enabled"; + } else if (strchr(disabled, 'f')) { + val = bypass_forbidding = "disabled"; + } + + } else if (!strcasecmp(item->info_type, "site_fault_detection")) { + + if (strchr(enabled, 'l')) { + val = site_fault_detection = "enabled"; + } else if (strchr(disabled, 'l')) { + val = site_fault_detection = "disabled"; + } + + } else if (!strcasecmp(item->info_type, "advanced_eco_mode")) { + + if (strchr(enabled, 'n')) { + val = advanced_eco_mode = "enabled"; + } else if (strchr(disabled, 'n')) { + val = advanced_eco_mode = "disabled"; + } + + } else if (!strcasecmp(item->info_type, "constant_phase_angle")) { + + if (strchr(enabled, 'q')) { + val = constant_phase_angle = "enabled"; + } else if (strchr(disabled, 'q')) { + val = constant_phase_angle = "disabled"; + } + + } else if (!strcasecmp(item->info_type, "limited_runtime_on_battery")) { + + if (strchr(enabled, 'w')) { + val = limited_runtime_on_battery = "enabled"; + } else if (strchr(disabled, 'w')) { + val = limited_runtime_on_battery = "disabled"; + } + +/* } else if (!strcasecmp(item->info_type, "")) { + + if (strchr(enabled, 'h')) { unknown/unused + } else if (strchr(disabled, 'h')) { } + + } else if (!strcasecmp(item->info_type, "")) { + + if (strchr(enabled, 't')) { unknown/unused + } else if (strchr(disabled, 't')) { } + + } else if (!strcasecmp(item->info_type, "")) { + + if (strchr(enabled, 'k')) { unknown/unused + } else if (strchr(disabled, 'k')) { } + + } else if (!strcasecmp(item->info_type, "")) { + + if (strchr(enabled, 'i')) { unknown/unused + } else if (strchr(disabled, 'i')) { } + + } else if (!strcasecmp(item->info_type, "")) { + + if (strchr(enabled, 'm')) { unknown/unused + } else if (strchr(disabled, 'm')) { } + + } else if (!strcasecmp(item->info_type, "")) { + + if (strchr(enabled, 'z')) { unknown/unused + } else if (strchr(disabled, 'z')) { } +*/ + /* Items with a NUT variable */ + } else if (!strcasecmp(item->info_type, "ups.start.auto")) { + + if (strchr(enabled, 'r')) { + val = "yes"; + } else if (strchr(disabled, 'r')) { + val = "no"; + } + + } else if (!strcasecmp(item->info_type, "battery.protection")) { + + if (strchr(enabled, 's')) { + val = "yes"; + } else if (strchr(disabled, 's')) { + val = "no"; + } + + } else if (!strcasecmp(item->info_type, "battery.energysave")) { + + if (strchr(enabled, 'g')) { + val = "yes"; + } else if (strchr(disabled, 'g')) { + val = "no"; + } + + } else if (!strcasecmp(item->info_type, "ups.start.battery")) { + + if (strchr(enabled, 'c')) { + val = "yes"; + } else if (strchr(disabled, 'c')) { + val = "no"; + } + + } else if (!strcasecmp(item->info_type, "outlet.0.switchable")) { + + if (strchr(enabled, 'j')) { + + int i; + char buf[SMALLBUF]; + + val = "yes"; + + /* Unskip outlet.n.{switchable,status} */ + for (i = 1; i <= 4; i++) { + + snprintf(buf, sizeof(buf), "outlet.%d.switchable", i); + + unskip = find_nut_info(buf, 0, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + snprintf(buf, sizeof(buf), "outlet.%d.status", i); + + unskip = find_nut_info(buf, 0, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + } + + } else if (strchr(disabled, 'j')) { + val = "no"; + } + + } + + /* UPS doesn't have that capability */ + if (!val) + return -1; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, val); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + /* This item doesn't have a NUT var and we were not asked by the user to change its value */ + if ((item->qxflags & QX_FLAG_NONUT) && !getval(item->info_type)) + return 0; + + /* Unskip setvar */ + unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + return 0; +} + +/* *SETVAR* Set UPS capability options */ +static int voltronic_capability_set(item_t *item, char *value, const size_t valuelen) +{ +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + if (!strcasecmp(value, "yes")) { + snprintf(value, valuelen, item->command, "E"); + return 0; + } + + if (!strcasecmp(value, "no")) { + snprintf(value, valuelen, item->command, "D"); + return 0; + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + /* At this point value should have been already checked against enum so this shouldn't happen.. however.. */ + upslogx(LOG_ERR, "%s: given value [%s] is not acceptable. Enter either 'yes' or 'no'.", item->info_type, value); + + return -1; +} + +/* *SETVAR/NONUT* Change UPS capability according to user configuration in ups.conf */ +static int voltronic_capability_set_nonut(item_t *item, char *value, const size_t valuelen) +{ + const char *match = NULL; + int i; + const struct { + const char *type; /* Name of the option */ + const char *match; /* Value reported by the UPS */ + } capability[] = { + { "bypass_alarm", bypass_alarm }, + { "battery_alarm", battery_alarm }, + { "bypass_when_off", bypass_when_off }, + { "alarm_control", alarm_control }, + { "converter_mode", converter_mode }, + { "eco_mode", eco_mode }, + { "battery_open_status_check", battery_open_status_check }, + { "bypass_forbidding", bypass_forbidding }, + { "site_fault_detection", site_fault_detection }, + { "advanced_eco_mode", advanced_eco_mode }, + { "constant_phase_angle", constant_phase_angle }, + { "limited_runtime_on_battery", limited_runtime_on_battery }, + { NULL, NULL } + }; + + for (i = 0; capability[i].type; i++) { + + if (strcasecmp(item->info_type, capability[i].type)) + continue; + + match = capability[i].match; + + break; + + } + + /* UPS doesn't have that capability */ + if (!match) + return -1; + + /* At this point value should have been already checked by nutdrv_qx's own setvar so this shouldn't happen.. however.. */ + if (!strcasecmp(value, match)) { + upslogx(LOG_INFO, "%s is already %s", item->info_type, match); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + if (!strcasecmp(value, "disabled")) { + snprintf(value, valuelen, item->command, "D"); + } else if (!strcasecmp(value, "enabled")) { + snprintf(value, valuelen, item->command, "E"); + } else { + /* At this point value should have been already checked against enum so this shouldn't happen.. however.. */ + upslogx(LOG_ERR, "%s: [%s] is not within acceptable values [enabled/disabled]", item->info_type, value); + return -1; + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* *SETVAR/NONUT* Reset capability options and their limits to safe default values */ +static int voltronic_capability_reset(item_t *item, char *value, const size_t valuelen) +{ + /* Nothing to do */ + if (!testvar("reset_to_default")) + return -1; + + /* UPS capability options can be reset only when the UPS is in 'Standby Mode' (=OFF) (from QMOD) */ + if (!((unsigned int)(qx_status()) & STATUS(OFF))) { + upslogx(LOG_ERR, + "%s: UPS capability options can be reset only when the " + "UPS is in Standby Mode (i.e. ups.status = 'OFF').", + item->info_type); + return -1; + } + + snprintf(value, valuelen, "%s", item->command); + + return 0; +} + +/* Voltage limits for ECO Mode */ +static int voltronic_eco_volt(item_t *item, char *value, const size_t valuelen) +{ + const long protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10); + int ovn; + const char *outvoltnom; + char buf[SMALLBUF]; + item_t *unskip; + /* Range of accepted values for maximum voltage for ECO Mode */ + struct { + int lower; /* Lower limit */ + int upper; /* Upper limit */ + } max; + /* Range of accepted values for minimum voltage for ECO Mode */ + struct { + int lower; /* Lower limit */ + int upper; /* Upper limit */ + } min; + + if (strspn(item->value, "0123456789 .") != strlen(item->value)) { + upsdebugx(2, + "%s: non numerical value [%s: %s]", + __func__, item->info_type, item->value); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, strtod(item->value, NULL)); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + outvoltnom = dstate_getinfo("output.voltage.nominal"); + + /* Since query for ratings (QRI) is not mandatory to run this driver, skip next steps if we can't get the value of output voltage nominal */ + if (!outvoltnom) { + upsdebugx(2, "%s: unable to get output voltage nominal", __func__); + /* We return 0 since we have the value and all's ok: simply we can't set its range so we won't unskip SETVAR item and .{min,max} */ + return 0; + } + + { /* scoping */ + long l = strtol(outvoltnom, NULL, 10); + if (l > INT_MAX || l < 0) { + /* See comments above */ + upsdebugx(2, "%s: unable to get output voltage nominal: %ld", __func__, l); + return 0; + } + ovn = (int)l; + } + + /* For P01/P09 */ + if (protocol == 1 || protocol == 9) { + + if (ovn >= 200) { + min.lower = ovn - 24; + min.upper = ovn - 7; + max.lower = ovn + 7; + max.upper = ovn + 24; + } else { + min.lower = ovn - 12; + min.upper = ovn - 3; + max.lower = ovn + 3; + max.upper = ovn + 12; + + } + + /* For P02/P03/P10/P13/P14/P99 */ + } else if (protocol == 2 || protocol == 3 || protocol == 10 || protocol == 13 || protocol == 14 || protocol == 99) { + + if (ovn >= 200) { + min.lower = ovn - 24; + min.upper = ovn - 11; + max.lower = ovn + 11; + max.upper = ovn + 24; + } else { + min.lower = ovn - 12; + min.upper = ovn - 5; + max.lower = ovn + 5; + max.upper = ovn + 12; + } + + /* ECO mode not supported */ + } else { + upsdebugx(2, "%s: the UPS doesn't seem to support ECO Mode", __func__); + /* We return 0 since we have the value and all's ok: simply we can't set its range so we won't unskip SETVAR item and .{min,max} */ + return 0; + } + + if (!strcasecmp(item->info_type, "input.transfer.high")) { + + /* Fill voltronic_r_eco_volt_max */ + snprintf(item->info_rw[0].value, sizeof(item->info_rw[0].value), "%d", max.lower); + snprintf(item->info_rw[1].value, sizeof(item->info_rw[1].value), "%d", max.upper); + + + } else if (!strcasecmp(item->info_type, "input.transfer.low")) { + + /* Fill voltronic_r_eco_volt_min */ + snprintf(item->info_rw[0].value, sizeof(item->info_rw[0].value), "%d", min.lower); + snprintf(item->info_rw[1].value, sizeof(item->info_rw[1].value), "%d", min.upper); + + } + + /* Unskip input.transfer.{high,low}.{min,max} */ + snprintf(buf, sizeof(buf), "%s.min", item->info_type); + + unskip = find_nut_info(buf, QX_FLAG_SEMI_STATIC, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + snprintf(buf, sizeof(buf), "%s.max", item->info_type); + + unskip = find_nut_info(buf, QX_FLAG_SEMI_STATIC, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + /* Unskip input.transfer.{high,low} setvar */ + unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + return 0; +} + +/* Voltage limits for ECO Mode (max, min) */ +static int voltronic_eco_volt_range(item_t *item, char *value, const size_t valuelen) +{ + char *buf; + int i; + item_t *from; + + if (!strcasecmp(item->info_type, "input.transfer.low.min")) { + + buf = "input.transfer.low"; + i = 0; + + } else if (!strcasecmp(item->info_type, "input.transfer.low.max")) { + + buf = "input.transfer.low"; + i = 1; + + } else if (!strcasecmp(item->info_type, "input.transfer.high.min")) { + + buf = "input.transfer.high"; + i = 0; + + } else if (!strcasecmp(item->info_type, "input.transfer.high.max")) { + + buf = "input.transfer.high"; + i = 1; + + } else { + + /* Don't know what happened */ + return -1; + + } + + from = find_nut_info(buf, QX_FLAG_SEMI_STATIC, 0); + + /* Don't know what happened */ + if (!from) + return -1; + + /* Value is set at runtime by voltronic_eco_volt, so if it's still unset something went wrong */ + if (!strlen(from->info_rw[i].value)) + return -1; + + snprintf(value, valuelen, "%s", from->info_rw[i].value); + + return 0; +} + +/* Frequency limits for ECO Mode */ +static int voltronic_eco_freq(item_t *item, char *value, const size_t valuelen) +{ + item_t *unskip; + + if (strspn(item->value, "0123456789 .") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, strtod(item->value, NULL)); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + /* Unskip input.transfer.{high,low} setvar */ + unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + return 0; +} + +/* *NONUT* Voltage/frequency limits for Bypass Mode */ +static int voltronic_bypass(item_t *item, char *value, const size_t valuelen) +{ + item_t *unskip; + double val; + + if (strspn(item->value, "0123456789 .") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + + if (!strcasecmp(item->info_type, "max_bypass_volt")) { + + val = max_bypass_volt = strtol(item->value, NULL, 10); + + } else if (!strcasecmp(item->info_type, "min_bypass_volt")) { + + val = min_bypass_volt = strtol(item->value, NULL, 10); + + } else if (!strcasecmp(item->info_type, "max_bypass_freq")) { + + val = max_bypass_freq = strtod(item->value, NULL); + + } else if (!strcasecmp(item->info_type, "min_bypass_freq")) { + + val = min_bypass_freq = strtod(item->value, NULL); + + } else { + + /* Don't know what happened */ + return -1; + + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, val); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + /* No user-provided value to change.. */ + if (!getval(item->info_type)) + return 0; + + /* Unskip {min,max}_bypass_volt setvar */ + unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + return 0; +} + +/* *NONUT* Number of batteries */ +static int voltronic_batt_numb(item_t *item, char *value, const size_t valuelen) +{ + item_t *unskip; + + if (strspn(item->value, "0123456789 .") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", + __func__, item->info_type, item->value); + return -1; + } + + battery_number = strtol(item->value, NULL, 10); + + if (battery_number > INT_MAX) { + upsdebugx(2, "%s: battery number out of range [%s: %s]", + __func__, item->info_type, item->value); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, (int)battery_number); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + /* No user-provided value to change.. */ + if (!getval(item->info_type)) + return 0; + + /* Unskip battery_number setvar */ + unskip = find_nut_info("battery_number", QX_FLAG_SETVAR, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + return 0; +} + +/* Battery runtime */ +static int voltronic_batt_runtime(item_t *item, char *value, const size_t valuelen) +{ + double runtime; + + if (strspn(item->value, "0123456789 .") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + + /* Battery runtime is reported by the UPS in minutes, NUT expects seconds */ + runtime = strtod(item->value, NULL) * 60; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, runtime); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Protocol used by the UPS */ +static int voltronic_protocol(item_t *item, char *value, const size_t valuelen) +{ + long protocol; + + if (strncasecmp(item->value, "PI", 2)) { + upsdebugx(2, "%s: invalid start characters [%.2s]", __func__, item->value); + return -1; + } + + /* Here we exclude non numerical value and other non accepted protocols (hence the restricted comparison target) */ + if (strspn(item->value+2, "0123489") != strlen(item->value+2)) { + upslogx(LOG_ERR, "Protocol [%s] is not supported by this driver", item->value); + return -1; + } + + protocol = strtol(item->value+2, NULL, 10); + + switch (protocol) + { + case 0: + case 1: + case 2: + case 3: + case 8: + case 9: + case 10: + case 13: + case 14: + case 31: + case 99: + + break; + + default: + + upslogx(LOG_ERR, "Protocol [PI%02ld] is not supported by this driver", protocol); + return -1; + + } + + snprintf(value, valuelen, "P%02ld", protocol); + + /* Unskip vars according to protocol */ + voltronic_massive_unskip(protocol); + + return 0; +} + +/* Fault reported by the UPS: + * When the UPS is queried for status (QGS), if it reports a fault (6th bit of 12bit flag of the reply to QGS set to 1), the driver unskips the QFS item in qx2nut array: this function processes the reply to QFS query */ +static int voltronic_fault(item_t *item, char *value, const size_t valuelen) +{ + long protocol = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10); + + char alarm[LARGEBUF]; /* can sprintf() SMALLBUF plus markup into here */ + + upslogx(LOG_INFO, "Checking for faults.."); + + if (!strcasecmp(item->value, "OK")) { +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, "No fault found"); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + upslogx(LOG_INFO, "%s", value); + item->qxflags |= QX_FLAG_SKIP; + return 0; + } + + if ((strspn(item->value, "0123456789ABC") != 2) || ((item->value[0] != '1') && (strspn(item->value+1, "0123456789") != 1))) { + + snprintf(alarm, sizeof(alarm), "Unknown fault [%s]", item->value); + + /* P31 UPSes */ + } else if (protocol == 31) { + + if (strpbrk(item->value+1, "ABC")) { + + snprintf(alarm, sizeof(alarm), "Unknown fault [%s]", item->value); + + } else { + + switch (strtol(item->value, NULL, 10)) + { + case 1: + + strcpy(alarm, "Fan failure."); + break; + + case 2: + + strcpy(alarm, "Over temperature fault."); + break; + + case 3: + + strcpy(alarm, "Battery voltage is too high."); + break; + + case 4: + + strcpy(alarm, "Battery voltage too low."); + break; + + case 5: + + strcpy(alarm, "Inverter relay short-circuited."); + break; + + case 6: + + strcpy(alarm, "Inverter voltage over maximum value."); + break; + + case 7: + + strcpy(alarm, "Overload fault."); + update_status("OVER"); + break; + + case 8: + + strcpy(alarm, "Bus voltage exceeds its upper limit."); + break; + + case 9: + + strcpy(alarm, "Bus soft start fail."); + break; + + case 10: + + strcpy(alarm, "Unknown fault [Fault code: 10]"); + break; + + case 51: + + strcpy(alarm, "Over current fault."); + break; + + case 52: + + strcpy(alarm, "Bus voltage below its under limit."); + break; + + case 53: + + strcpy(alarm, "Inverter soft start fail."); + break; + + case 54: + + strcpy(alarm, "Self test fail."); + break; + + case 55: + + strcpy(alarm, "Output DC voltage exceeds its upper limit."); + break; + + case 56: + + strcpy(alarm, "Battery open fault."); + break; + + case 57: + + strcpy(alarm, "Current sensor fault."); + break; + + case 58: + + strcpy(alarm, "Battery short."); + break; + + case 59: + + strcpy(alarm, "Inverter voltage below its lower limit."); + break; + + default: + + snprintf(alarm, sizeof(alarm), "Unknown fault [%s]", item->value); + break; + + } + + } + + /* All other UPSes */ + } else { + + switch (strtol(item->value, NULL, 10)) + { + case 1: + + switch (item->value[1]) + { + case 'A': + + strcpy(alarm, "L1 inverter negative power out of acceptable range."); + break; + + case 'B': + + strcpy(alarm, "L2 inverter negative power out of acceptable range."); + break; + + case 'C': + + strcpy(alarm, "L3 inverter negative power out of acceptable range."); + break; + + default: + + strcpy(alarm, "Bus voltage not within default setting."); + break; + + } + + break; + + case 2: + + strcpy(alarm, "Bus voltage over maximum value."); + break; + + case 3: + + strcpy(alarm, "Bus voltage below minimum value."); + break; + + case 4: + + strcpy(alarm, "Bus voltage differences out of acceptable range."); + break; + + case 5: + + strcpy(alarm, "Bus voltage of slope rate drops too fast."); + break; + + case 6: + + strcpy(alarm, "Over current in PFC input inductor."); + break; + + case 11: + + strcpy(alarm, "Inverter voltage not within default setting."); + break; + + case 12: + + strcpy(alarm, "Inverter voltage over maximum value."); + break; + + case 13: + + strcpy(alarm, "Inverter voltage below minimum value."); + break; + + case 14: + + strcpy(alarm, "Inverter short-circuited."); + break; + + case 15: + + strcpy(alarm, "L2 phase inverter short-circuited."); + break; + + case 16: + + strcpy(alarm, "L3 phase inverter short-circuited."); + break; + + case 17: + + strcpy(alarm, "L1L2 inverter short-circuited."); + break; + + case 18: + + strcpy(alarm, "L2L3 inverter short-circuited."); + break; + + case 19: + + strcpy(alarm, "L3L1 inverter short-circuited."); + break; + + case 21: + + strcpy(alarm, "Battery SCR short-circuited."); + break; + + case 22: + + strcpy(alarm, "Line SCR short-circuited."); + break; + + case 23: + + strcpy(alarm, "Inverter relay open fault."); + break; + + case 24: + + strcpy(alarm, "Inverter relay short-circuited."); + break; + + case 25: + + strcpy(alarm, "Input and output wires oppositely connected."); + break; + + case 26: + + strcpy(alarm, "Battery oppositely connected."); + break; + + case 27: + + strcpy(alarm, "Battery voltage is too high."); + break; + + case 28: + + strcpy(alarm, "Battery voltage too low."); + break; + + case 29: + + strcpy(alarm, "Failure for battery fuse being open-circuited."); + break; + + case 31: + + strcpy(alarm, "CAN-bus communication fault."); + break; + + case 32: + + strcpy(alarm, "Host signal circuit fault."); + break; + + case 33: + + strcpy(alarm, "Synchronous signal circuit fault."); + break; + + case 34: + + strcpy(alarm, "Synchronous pulse signal circuit fault."); + break; + + case 35: + + strcpy(alarm, "Parallel cable disconnected."); + break; + + case 36: + + strcpy(alarm, "Load unbalanced."); + break; + + case 41: + + strcpy(alarm, "Over temperature fault."); + break; + + case 42: + + strcpy(alarm, "Communication failure between CPUs in control board."); + break; + + case 43: + + strcpy(alarm, "Overload fault."); + update_status("OVER"); + break; + + case 44: + + strcpy(alarm, "Fan failure."); + break; + + case 45: + + strcpy(alarm, "Charger failure."); + break; + + case 46: + + strcpy(alarm, "Model fault."); + break; + + case 47: + + strcpy(alarm, "MCU communication fault."); + break; + + default: + + snprintf(alarm, sizeof(alarm), "Unknown fault [%s]", item->value); + break; + + } + + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, alarm); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + upslogx(LOG_INFO, "Fault found: %s", alarm); + + item->qxflags |= QX_FLAG_SKIP; + + return 0; +} + +/* Warnings reported by the UPS */ +static int voltronic_warning(item_t *item, char *value, const size_t valuelen) +{ + char warn[SMALLBUF] = "", unk[SMALLBUF] = "", bitwarns[SMALLBUF] = "", warns[4096] = ""; + int i; + + if (strspn(item->value, "01") != strlen(item->value)) { + upsdebugx(2, "%s: invalid reply from the UPS [%s]", __func__, item->value); + return -1; + } + + /* No warnings */ + if (strspn(item->value, "0") == strlen(item->value)) { + return 0; + } + + snprintf(value, valuelen, "UPS warnings:"); + + for (i = 0; i < (int)strlen(item->value); i++) { + + int u = 0; + + if (item->value[i] == '1') { + + switch (i) + { + case 0: + + strcpy(warn, "Battery disconnected."); + break; + + case 1: + + strcpy(warn, "Neutral not connected."); + break; + + case 2: + + strcpy(warn, "Site fault."); + break; + + case 3: + + strcpy(warn, "Phase sequence incorrect."); + break; + + case 4: + + strcpy(warn, "Phase sequence incorrect in bypass."); + break; + + case 5: + + strcpy(warn, "Input frequency unstable in bypass."); + break; + + case 6: + + strcpy(warn, "Battery overcharged."); + break; + + case 7: + + strcpy(warn, "Low battery."); + update_status("LB"); + break; + + case 8: + + strcpy(warn, "Overload alarm."); + update_status("OVER"); + break; + + case 9: + + strcpy(warn, "Fan alarm."); + break; + + case 10: + + strcpy(warn, "EPO enabled."); + break; + + case 11: + + strcpy(warn, "Unable to turn on UPS."); + break; + + case 12: + + strcpy(warn, "Over temperature alarm."); + break; + + case 13: + + strcpy(warn, "Charger alarm."); + break; + + case 14: + + strcpy(warn, "Remote auto shutdown."); + break; + + case 15: + + strcpy(warn, "L1 input fuse not working."); + break; + + case 16: + + strcpy(warn, "L2 input fuse not working."); + break; + + case 17: + + strcpy(warn, "L3 input fuse not working."); + break; + + case 18: + + strcpy(warn, "Positive PFC abnormal in L1."); + break; + + case 19: + + strcpy(warn, "Negative PFC abnormal in L1."); + break; + + case 20: + + strcpy(warn, "Positive PFC abnormal in L2."); + break; + + case 21: + + strcpy(warn, "Negative PFC abnormal in L2."); + break; + + case 22: + + strcpy(warn, "Positive PFC abnormal in L3."); + break; + + case 23: + + strcpy(warn, "Negative PFC abnormal in L3."); + break; + + case 24: + + strcpy(warn, "Abnormal in CAN-bus communication."); + break; + + case 25: + + strcpy(warn, "Abnormal in synchronous signal circuit."); + break; + + case 26: + + strcpy(warn, "Abnormal in synchronous pulse signal circuit."); + break; + + case 27: + + strcpy(warn, "Abnormal in host signal circuit."); + break; + + case 28: + + strcpy(warn, "Male connector of parallel cable not connected well."); + break; + + case 29: + + strcpy(warn, "Female connector of parallel cable not connected well."); + break; + + case 30: + + strcpy(warn, "Parallel cable not connected well."); + break; + + case 31: + + strcpy(warn, "Battery connection not consistent in parallel systems."); + break; + + case 32: + + strcpy(warn, "AC connection not consistent in parallel systems."); + break; + + case 33: + + strcpy(warn, "Bypass connection not consistent in parallel systems."); + break; + + case 34: + + strcpy(warn, "UPS model types not consistent in parallel systems."); + break; + + case 35: + + strcpy(warn, "Capacity of UPSes not consistent in parallel systems."); + break; + + case 36: + + strcpy(warn, "Auto restart setting not consistent in parallel systems."); + break; + + case 37: + + strcpy(warn, "Battery cell over charge."); + break; + + case 38: + + strcpy(warn, "Battery protection setting not consistent in parallel systems."); + break; + + case 39: + + strcpy(warn, "Battery detection setting not consistent in parallel systems."); + break; + + case 40: + + strcpy(warn, "Bypass not allowed setting not consistent in parallel systems."); + break; + + case 41: + + strcpy(warn, "Converter setting not consistent in parallel systems."); + break; + + case 42: + + strcpy(warn, "High loss point for frequency in bypass mode not consistent in parallel systems."); + break; + + case 43: + + strcpy(warn, "Low loss point for frequency in bypass mode not consistent in parallel systems."); + break; + + case 44: + + strcpy(warn, "High loss point for voltage in bypass mode not consistent in parallel systems."); + break; + + case 45: + + strcpy(warn, "Low loss point for voltage in bypass mode not consistent in parallel systems."); + break; + + case 46: + + strcpy(warn, "High loss point for frequency in AC mode not consistent in parallel systems."); + break; + + case 47: + + strcpy(warn, "Low loss point for frequency in AC mode not consistent in parallel systems."); + break; + + case 48: + + strcpy(warn, "High loss point for voltage in AC mode not consistent in parallel systems."); + break; + + case 49: + + strcpy(warn, "Low loss point for voltage in AC mode not consistent in parallel systems."); + break; + + case 50: + + strcpy(warn, "Warning for locking in bypass mode after 3 consecutive overloads within 30 min."); + break; + + case 51: + + strcpy(warn, "Warning for three-phase AC input current unbalance."); + break; + + case 52: + + strcpy(warn, "Warning for a three-phase input current unbalance detected in battery mode."); + break; + + case 53: + + strcpy(warn, "Warning for Inverter inter-current unbalance."); + break; + + case 54: + + strcpy(warn, "Programmable outlets cut off pre-alarm."); + break; + + case 55: + + strcpy(warn, "Warning for Battery replace."); + update_status("RB"); + break; + + case 56: + + strcpy(warn, "Abnormal warning on input phase angle."); + break; + + case 57: + + strcpy(warn, "Warning!! Cover of maintain switch is open."); + break; + + case 61: + + strcpy(warn, "EEPROM operation error."); + break; + + default: + + snprintf(warn, sizeof(warn), "Unknown warning from UPS [bit: #%02d]", i + 1); + u++; + break; + + } + + upslogx(LOG_INFO, "Warning from UPS: %s", warn); + + if (u) { /* Unknown warnings */ + + snprintfcat(unk, sizeof(unk), ", #%02d", i + 1); + + } else { /* Known warnings */ + + if (strlen(warns) > 0) { + + /* For too long warnings (total) */ + snprintfcat(bitwarns, sizeof(bitwarns), ", #%02d", i + 1); + + /* For warnings (total) not too long */ + snprintfcat(warns, sizeof(warns), " %s", warn); + + } else { + + snprintf(bitwarns, sizeof(bitwarns), "Known (see log or manual) [bit: #%02d", i + 1); + snprintf(warns, sizeof(warns), "%s", warn); + + } + + } + } + } + + /* There's some known warning, at least */ + if (strlen(warns) > 0) { + + /* We have both known and unknown warnings */ + if (strlen(unk) > 0) { + + /* Appending unknown ones to known ones; removing leading comma from unk - 'explicit' */ + snprintfcat(warns, sizeof(warns), " Unknown warnings [bit:%s]", unk+1); + + /* Appending unknown ones to known ones; removing leading comma from unk - 'cryptic' */ + snprintfcat(bitwarns, sizeof(bitwarns), "]; Unknown warnings [bit:%s]", unk+1); + + /* We have only known warnings */ + } else { + + snprintfcat(bitwarns, sizeof(bitwarns), "]"); + + } + + /* We have only unknown warnings */ + } else if (strlen(unk) > 0) { + + /* Removing leading comma from unk */ + snprintf(warns, sizeof(warns), "Unknown warnings [bit:%s]", unk+1); + strcpy(bitwarns, warns); + + } else { + + /* Don't know what happened */ + upsdebugx(2, "%s: failed to process warnings", __func__); + return -1; + + } + + /* If grand total of warnings doesn't exceed value of alarm (=ST_MAX_VALUE_LEN) minus some space (32) for other alarms.. */ + if ((ST_MAX_VALUE_LEN - 32) > strlen(warns)) { + /* ..then be explicit.. */ + snprintfcat(value, valuelen, " %s", warns); + /* ..otherwise.. */ + } else { + /* ..be cryptic */ + snprintfcat(value, valuelen, " %s", bitwarns); + } + + return 0; +} + +/* Working mode reported by the UPS */ +static int voltronic_mode(item_t *item, char *value, const size_t valuelen) +{ + char *status = NULL, *alarm = NULL; + + switch (item->value[0]) + { + case 'P': + + alarm = "UPS is going ON"; + break; + + case 'S': + + status = "OFF"; + break; + + case 'Y': + + status = "BYPASS"; + break; + + case 'L': + + status = "OL"; + break; + + case 'B': + + status = "!OL"; + break; + + case 'T': + + status = "CAL"; + break; + + case 'F': + + alarm = "Fault reported by UPS."; + break; + + case 'E': + + alarm = "UPS is in ECO Mode."; + break; + + case 'C': + + alarm = "UPS is in Converter Mode."; + break; + + case 'D': + + alarm = "UPS is shutting down!"; + status = "FSD"; + break; + + default: + + upsdebugx(2, "%s: invalid reply from the UPS [%s]", __func__, item->value); + return -1; + + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + if (alarm && !strcasecmp(item->info_type, "ups.alarm")) { + snprintf(value, valuelen, item->dfl, alarm); + } else if (status && !strcasecmp(item->info_type, "ups.status")) { + snprintf(value, valuelen, item->dfl, status); + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Process status bits */ +static int voltronic_status(item_t *item, char *value, const size_t valuelen) +{ + char *val = ""; + + if (strspn(item->value, "01") != strlen(item->value)) { + upsdebugx(3, "%s: unexpected value %s@%d->%s", + __func__, item->value, item->from, item->value); + return -1; + } + + switch (item->from) + { + case 63: /* UPS Type - ups.type */ + + { + long type = strtol(item->value, NULL, 10); + + if (!type) /* 00 -> Offline */ + val = "offline"; + else if (type == 1) /* 01 -> Line-interactive */ + val = "line-interactive"; + else if (type == 10) /* 10 -> Online */ + val = "online"; + else { + upsdebugx(2, "%s: invalid type [%s: %s]", + __func__, item->info_type, item->value); + return -1; + } + } + + break; + + case 65: /* Utility Fail (Immediate) - ups.status */ + + if (item->value[0] == '1') + val = "!OL"; + else + val = "OL"; + break; + + case 66: /* Battery Low - ups.status */ + + if (item->value[0] == '1') + val = "LB"; + else + val = "!LB"; + break; + + case 67: /* Bypass/Boost or Buck Active - ups.{status,alarm} */ + + if (item->value[0] == '1') { + + double vi, vo; + + vi = strtod(dstate_getinfo("input.voltage"), NULL); + vo = strtod(dstate_getinfo("output.voltage"), NULL); + + if (vo < 0.5 * vi) { + upsdebugx(2, "%s: output voltage too low", __func__); + return -1; + } + + if (vo < 0.95 * vi) { + val = "TRIM"; + } else if (vo < 1.05 * vi) { + + long prot = strtol(dstate_getinfo("ups.firmware.aux")+1, NULL, 10); + + if (!prot || prot == 8) { /* ups.alarm */ + + if (!strcasecmp(item->info_type, "ups.alarm")) + val = "UPS is in AVR Mode."; + + } else { /* ups.status */ + + if (!strcasecmp(item->info_type, "ups.status")) + val = "BYPASS"; + + } + + } else if (vo < 1.5 * vi) { + val = "BOOST"; + } else { + upsdebugx(2, "%s: output voltage too high", __func__); + return -1; + } + + } + + break; + + case 68: /* UPS Fault - ups.alarm */ + + if (item->value[0] == 1) { + + item_t *faultitem; + + for (faultitem = voltronic_qx2nut; faultitem->info_type != NULL; faultitem++) { + + if (!faultitem->command) + continue; + + if (!strcasecmp(faultitem->command, "QFS\r")) { + faultitem->qxflags &= ~QX_FLAG_SKIP; + break; + } + + } + + val = "UPS Fault!"; + + } + + break; + +/* case 69: *//* unknown */ +/* break;*/ + + case 70: /* Test in Progress - ups.status */ + + if (item->value[0] == '1') + val = "CAL"; + else + val = "!CAL"; + break; + + case 71: /* Shutdown Active - ups.status */ + + if (item->value[0] == '1') + val = "FSD"; + else + val = "!FSD"; + break; + + case 72: /* Beeper status - ups.beeper.status */ + + /* The UPS has the ability to enable/disable the alarm (from UPS capability) */ + if (alarm_control) { + + const char *beeper = dstate_getinfo("ups.beeper.status"); + + if (!beeper || strcasecmp(beeper, "disabled")) { + + if (item->value[0] == '0') /* Beeper On */ + val = "enabled"; + else + val = "muted"; + + } + + /* The UPS lacks the ability to enable/disable the alarm (from UPS capability) */ + } else { + + if (item->value[0] == '0') /* Beeper On */ + val = "enabled"; + else + val = "disabled"; + + } + + break; + +/* case 73: *//* unknown */ +/* break;*/ +/* case 74: *//* unknown */ +/* break;*/ + + default: + /* Don't know what happened */ + return -1; + } + + snprintf(value, valuelen, "%s", val); + + return 0; +} + +/* Output power factor */ +static int voltronic_output_powerfactor(item_t *item, char *value, const size_t valuelen) +{ + double opf; + + if (strspn(item->value, "0123456789 .") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + + /* UPS report a value expressed in % so -> output.powerfactor*100 e.g. opf = 0,8 -> ups = 80 */ + opf = strtod(item->value, NULL) * 0.01; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, opf); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* UPS serial number */ +static int voltronic_serial_numb(item_t *item, char *value, const size_t valuelen) +{ + /* If the UPS report a 00..0 serial we'll log it but we won't store it in device.serial */ + if (strspn(item->value, "0") == strlen(item->value)) { + upslogx(LOG_INFO, "%s: UPS reported a non-significant serial [%s]", item->info_type, item->value); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, item->value); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Outlet status */ +static int voltronic_outlet(item_t *item, char *value, const size_t valuelen) +{ + const char *status, *switchable; + char number = item->info_type[7], + buf[SMALLBUF]; + item_t *outlet_item; + + switch (item->value[0]) + { + case '1': + + switchable = "yes"; + status = "on"; + break; + + case '0': + + switchable = "yes"; + status = "off"; + break; + + default: + + upsdebugx(2, "%s: invalid reply from the UPS [%s: %s]", __func__, item->info_type, item->value); + return -1; + + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + if (strstr(item->info_type, "switchable")) { + snprintf(value, valuelen, item->dfl, switchable); + } else if (strstr(item->info_type, "status")) { + snprintf(value, valuelen, item->dfl, status); + } else { + /* Don't know what happened */ + return -1; + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + /* Unskip outlet.n.delay.shutdown */ + snprintf(buf, sizeof(buf), "outlet.%c.delay.shutdown", number); + + outlet_item = find_nut_info(buf, QX_FLAG_SEMI_STATIC, 0); + + /* Don't know what happened*/ + if (!outlet_item) + return -1; + + outlet_item->qxflags &= ~QX_FLAG_SKIP; + + /* Unskip outlet.n.load.on */ + snprintf(buf, sizeof(buf), "outlet.%c.load.on", number); + + outlet_item = find_nut_info(buf, QX_FLAG_CMD, 0); + + /* Don't know what happened*/ + if (!outlet_item) + return -1; + + outlet_item->qxflags &= ~QX_FLAG_SKIP; + + /* Unskip outlet.n.load.off */ + snprintf(buf, sizeof(buf), "outlet.%c.load.off", number); + + outlet_item = find_nut_info(buf, QX_FLAG_CMD, 0); + + /* Don't know what happened*/ + if (!outlet_item) + return -1; + + outlet_item->qxflags &= ~QX_FLAG_SKIP; + + return 0; +} + +/* Outlet delay time */ +static int voltronic_outlet_delay(item_t *item, char *value, const size_t valuelen) +{ + char number = item->info_type[7], + buf[SMALLBUF]; + double val; + item_t *setvar_item; + + if (strspn(item->value, "0123456789 .") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + + /* UPS reports minutes, NUT expects seconds */ + val = strtod(item->value, NULL) * 60; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, val); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + /* Unskip outlet.n.delay.shutdown setvar */ + snprintf(buf, sizeof(buf), "outlet.%c.delay.shutdown", number); + + setvar_item = find_nut_info(buf, QX_FLAG_SETVAR, 0); + + /* Don't know what happened*/ + if (!setvar_item) + return -1; + + setvar_item->qxflags &= ~QX_FLAG_SKIP; + + return 0; +} + +/* *SETVAR* Outlet delay time */ +static int voltronic_outlet_delay_set(item_t *item, char *value, const size_t valuelen) +{ + long delay = strtol(value, NULL, 10); + + if ((delay/60) > INT_MAX) { + upsdebugx(2, "%s: invalid delay %ld sec set for UPS [%s]", + __func__, delay, item->value); + return -1; + } + + /* From seconds to minute */ + delay = delay / 60; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->command, (int)delay); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* Type of battery */ +static int voltronic_p31b(item_t *item, char *value, const size_t valuelen) +{ + long val; + + if ((item->value[0] != '0') || (strspn(item->value+1, "012") != 1)) { + upsdebugx(2, "%s: invalid battery type reported by the UPS [%s]", + __func__, item->value); + return -1; + } + + val = strtol(item->value, NULL, 10); + + if (val < 0 || (uintmax_t)val > SIZE_MAX) { + upsdebugx(2, "%s: invalid battery type reported by the UPS [%s]", + __func__, item->value); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, item->info_rw[(size_t)val].value); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* *SETVAR* Type of battery */ +static int voltronic_p31b_set(item_t *item, char *value, const size_t valuelen) +{ + int i; + + if (!item->info_rw) + return -1; + + for (i = 0; strlen(item->info_rw[i].value) > 0; i++) { + + if (!strcasecmp(item->info_rw[i].value, value)) + break; + + } + + /* At this point value should already be checked against enum so this shouldn't happen.. however.. */ + if (!strlen(item->info_rw[i].value)) { + upslogx(LOG_ERR, "%s: value [%s] out of range", item->info_type, value); + return -1; + } + + snprintf(value, valuelen, "%d", i); + + return voltronic_process_setvar(item, value, valuelen); +} + +/* *NONUT* Actual device grid working range type for P31 UPSes */ +static int voltronic_p31g(item_t *item, char *value, const size_t valuelen) +{ + long val; + + if ((item->value[0] != '0') || (strspn(item->value+1, "01") != 1)) { + upsdebugx(2, "%s: invalid device grid working range reported by the UPS [%s]", + __func__, item->value); + return -1; + } + + val = strtol(item->value, NULL, 10); + + if (val < 0 || (uintmax_t)val > SIZE_MAX) { + upsdebugx(2, "%s: invalid device grid working range reported by the UPS [%s]", + __func__, item->value); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, item->info_rw[(size_t)val].value); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + work_range_type = val; + + return 0; +} + +/* *SETVAR/NONUT* Device grid working range type for P31 UPSes */ +static int voltronic_p31g_set(item_t *item, char *value, const size_t valuelen) +{ + int i; + + if (!item->info_rw) + return -1; + + for (i = 0; strlen(item->info_rw[i].value) > 0; i++) { + + if (!strcasecmp(item->info_rw[i].value, value)) + break; + + } + + /* At this point value should have been already checked against enum so this shouldn't happen.. however.. */ + if (!strlen(item->info_rw[i].value)) { + upslogx(LOG_ERR, "%s: value [%s] out of range", item->info_type, value); + return -1; + } + + if (i == work_range_type) { + upslogx(LOG_INFO, "%s is already set to %s", item->info_type, item->info_rw[i].value); + return -1; + } + + snprintf(value, valuelen, "%d", i); + + return voltronic_process_setvar(item, value, valuelen); +} + +/* *NONUT* UPS actual input/output phase angles */ +static int voltronic_phase(item_t *item, char *value, const size_t valuelen) +{ + long angle; + + if (strspn(item->value, "0123456789 .") != strlen(item->value)) { + upsdebugx(2, "%s: non numerical value [%s: %s]", + __func__, item->info_type, item->value); + return -1; + } + + angle = strtol(item->value, NULL, 10); + + if (!strcasecmp(item->info_type, "output_phase_angle")) { + + output_phase_angle = angle; + + /* User-provided value to change.. */ + if (getval(item->info_type)) { + + item_t *unskip; + + /* Unskip output_phase_angle setvar */ + unskip = find_nut_info(item->info_type, QX_FLAG_SETVAR, 0); + + /* Don't know what happened */ + if (!unskip) + return -1; + + unskip->qxflags &= ~QX_FLAG_SKIP; + + } + + } + + if (angle < 0 || angle > INT_MAX) { + upsdebugx(2, "%s: phase angle out of range [%s: %ld]", + __func__, item->value, angle); + return -1; + } + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(value, valuelen, item->dfl, (int)angle); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + return 0; +} + +/* *SETVAR/NONUT* Output phase angle */ +static int voltronic_phase_set(item_t *item, char *value, const size_t valuelen) +{ + int i; + + if (!item->info_rw) + return -1; + + for (i = 0; strlen(item->info_rw[i].value) > 0; i++) { + + if (!strcasecmp(item->info_rw[i].value, value)) + break; + + } + + /* At this point value should have been already checked against enum so this shouldn't happen.. however.. */ + if (!strlen(item->info_rw[i].value)) { + upslogx(LOG_ERR, "%s: value [%s] out of range", item->info_type, value); + return -1; + } + + if (strtol(item->info_rw[i].value, NULL, 10) == output_phase_angle) { + upslogx(LOG_INFO, "%s is already set to %s", item->info_type, item->info_rw[i].value); + return -1; + } + + snprintf(value, valuelen, "%d", i); + + return voltronic_process_setvar(item, value, valuelen); +} + +/* *NONUT* UPS is master/slave in a system of UPSes in parallel */ +static int voltronic_parallel(item_t *item, char *value, const size_t valuelen) +{ + char *type; + + if (strlen(item->value) != strspn(item->value, "0123456789")) { + upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, item->value); + return -1; + } + + /* 001 for master UPS, 002 and 003 for slave UPSes */ + switch (strtol(item->value, NULL, 10)) + { + case 1: + + type = "master"; + break; + + case 2: + case 3: + + type = "slave"; + break; + + default: + + upsdebugx(2, "%s: invalid reply from the UPS [%s]", __func__, item->value); + return -1; + + } + + snprintf(value, valuelen, "This UPS is *%s* in a system of UPSes in parallel", type); + + return 0; +} + + +/* == Subdriver interface == */ +subdriver_t voltronic_subdriver = { + VOLTRONIC_VERSION, + voltronic_claim, + voltronic_qx2nut, + NULL, + NULL, + voltronic_makevartable, + "ACK", + "(NAK\r", +#ifdef TESTING + voltronic_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_voltronic.h b/drivers/nutdrv_qx_voltronic.h new file mode 100644 index 0000000..f9922e2 --- /dev/null +++ b/drivers/nutdrv_qx_voltronic.h @@ -0,0 +1,29 @@ +/* nutdrv_qx_voltronic.h - Subdriver for Voltronic Power UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_VOLTRONIC_H +#define NUTDRV_QX_VOLTRONIC_H + +#include "nutdrv_qx.h" + +extern subdriver_t voltronic_subdriver; + +#endif /* NUTDRV_QX_VOLTRONIC_H */ diff --git a/drivers/nutdrv_qx_zinto.c b/drivers/nutdrv_qx_zinto.c new file mode 100644 index 0000000..d039ccd --- /dev/null +++ b/drivers/nutdrv_qx_zinto.c @@ -0,0 +1,138 @@ +/* nutdrv_qx_zinto.c - Subdriver for Zinto protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include "nutdrv_qx.h" +#include "nutdrv_qx_blazer-common.h" + +#include "nutdrv_qx_zinto.h" + +#define ZINTO_VERSION "Zinto 0.06" + +/* qx2nut lookup table */ +static item_t zinto_qx2nut[] = { + + /* + * > [Q1\r] + * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] + * 01234567890123456789012345678901234567890123456 + * 0 1 2 3 4 + */ + + { "input.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 1, 5, "%.1f", 0, NULL, NULL, NULL }, + { "input.voltage.fault", 0, NULL, "Q1\r", "", 47, '(', "", 7, 11, "%.1f", 0, NULL, NULL, NULL }, + { "output.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 13, 17, "%.1f", 0, NULL, NULL, NULL }, + { "ups.load", 0, NULL, "Q1\r", "", 47, '(', "", 19, 21, "%.0f", 0, NULL, NULL, NULL }, + { "input.frequency", 0, NULL, "Q1\r", "", 47, '(', "", 23, 26, "%.1f", 0, NULL, NULL, NULL }, + { "battery.voltage", 0, NULL, "Q1\r", "", 47, '(', "", 28, 31, "%.2f", 0, NULL, NULL, NULL }, + { "ups.temperature", 0, NULL, "Q1\r", "", 47, '(', "", 33, 36, "%.1f", 0, NULL, NULL, NULL }, + /* Status bits */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 38, 38, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Utility Fail (Immediate) */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 39, 39, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Battery Low */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 40, 40, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Bypass/Boost or Buck Active */ + { "ups.alarm", 0, NULL, "Q1\r", "", 47, '(', "", 41, 41, NULL, 0, NULL, NULL, blazer_process_status_bits }, /* UPS Failed */ + { "ups.type", 0, NULL, "Q1\r", "", 47, '(', "", 42, 42, "%s", QX_FLAG_STATIC, NULL, NULL, blazer_process_status_bits }, /* UPS Type */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 43, 43, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Test in Progress */ + { "ups.status", 0, NULL, "Q1\r", "", 47, '(', "", 44, 44, NULL, QX_FLAG_QUICK_POLL, NULL, NULL, blazer_process_status_bits }, /* Shutdown Active */ + { "ups.beeper.status", 0, NULL, "Q1\r", "", 47, '(', "", 45, 45, "%s", 0, NULL, NULL, blazer_process_status_bits }, /* Beeper status */ + + /* + * > [F\r] + * < [#220.0 000 024.0 50.0\r] + * 0123456789012345678901 + * 0 1 2 + */ + + { "input.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 1, 5, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.current.nominal", 0, NULL, "F\r", "", 22, '#', "", 7, 9, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "battery.voltage.nominal", 0, NULL, "F\r", "", 22, '#', "", 11, 15, "%.1f", QX_FLAG_STATIC, NULL, NULL, NULL }, + { "input.frequency.nominal", 0, NULL, "F\r", "", 22, '#', "", 17, 20, "%.0f", QX_FLAG_STATIC, NULL, NULL, NULL }, + + /* + * > [FW?\r] + * < [#------------- ------ VT12046Q \r] + * 012345678901234567890123456789012345678 + * 0 1 2 3 + */ + + { "device.mfr", 0, NULL, "FW?\r", "", 39, '#', "", 1, 15, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "device.model", 0, NULL, "FW?\r", "", 39, '#', "", 17, 26, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + { "ups.firmware", 0, NULL, "FW?\r", "", 39, '#', "", 28, 37, "%s", QX_FLAG_STATIC | QX_FLAG_TRIM, NULL, NULL, NULL }, + + /* Instant commands */ + { "beeper.toggle", 0, NULL, "Q\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.off", 0, NULL, "S00R0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "load.on", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "shutdown.return", 0, NULL, "S%s\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stayoff", 0, NULL, "S%sR0000\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "shutdown.stop", 0, NULL, "C\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start", 0, NULL, "T%02d\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, blazer_process_command }, + { "test.battery.start.deep", 0, NULL, "TL\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.start.quick", 0, NULL, "T\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + { "test.battery.stop", 0, NULL, "CT\r", "", 0, 0, "", 0, 0, NULL, QX_FLAG_CMD, NULL, NULL, NULL }, + + /* Server-side settable vars */ + { "ups.delay.start", ST_FLAG_RW, blazer_r_ondelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_ONDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + { "ups.delay.shutdown", ST_FLAG_RW, blazer_r_offdelay, NULL, "", 0, 0, "", 0, 0, DEFAULT_OFFDELAY, QX_FLAG_ABSENT | QX_FLAG_SETVAR | QX_FLAG_RANGE, NULL, NULL, blazer_process_setvar }, + + /* End of structure. */ + { NULL, 0, NULL, NULL, "", 0, 0, "", 0, 0, NULL, 0, NULL, NULL, NULL } +}; + +/* Testing table */ +#ifdef TESTING +static testing_t zinto_testing[] = { + { "Q1\r", "(215.0 195.0 230.0 014 49.0 22.7 30.0 00000000\r", -1 }, + { "F\r", "#230.0 000 024.0 50.0\r", -1 }, + { "FW?\r", "#NOT_A_LIVE_UPS TESTING TESTING \r", -1 }, + { "Q\r", "", -1 }, + { "S03\r", "", -1 }, + { "C\r", "", -1 }, + { "S02R0005\r", "", -1 }, + { "S.5R0000\r", "", -1 }, + { "T04\r", "", -1 }, + { "TL\r", "", -1 }, + { "T\r", "", -1 }, + { "CT\r", "", -1 }, + { NULL } +}; +#endif /* TESTING */ + +/* Subdriver-specific initups */ +static void zinto_initups(void) +{ + blazer_initups(zinto_qx2nut); +} + +/* Subdriver interface */ +subdriver_t zinto_subdriver = { + ZINTO_VERSION, + blazer_claim, + zinto_qx2nut, + zinto_initups, + NULL, + blazer_makevartable, + "ACK", + NULL, +#ifdef TESTING + zinto_testing, +#endif /* TESTING */ +}; diff --git a/drivers/nutdrv_qx_zinto.h b/drivers/nutdrv_qx_zinto.h new file mode 100644 index 0000000..6e9f40c --- /dev/null +++ b/drivers/nutdrv_qx_zinto.h @@ -0,0 +1,29 @@ +/* nutdrv_qx_zinto.h - Subdriver for Zinto protocol based UPSes + * + * Copyright (C) + * 2013 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUTDRV_QX_ZINTO_H +#define NUTDRV_QX_ZINTO_H + +#include "nutdrv_qx.h" + +extern subdriver_t zinto_subdriver; + +#endif /* NUTDRV_QX_ZINTO_H */ diff --git a/drivers/nutdrv_siemens_sitop.c b/drivers/nutdrv_siemens_sitop.c new file mode 100644 index 0000000..1c1e9ef --- /dev/null +++ b/drivers/nutdrv_siemens_sitop.c @@ -0,0 +1,298 @@ +/* + * nutdrv_siemens_sitop.c - model specific routines for the Siemens SITOP UPS500 series + * + * + * Copyright (C) 2018 Matthijs H. ten Berge + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Notes: + * These UPSes operate at 24V DC (both input and output), instead of 110V/230V AC. + * Therefore, the line input is also referred to by Siemens as 'DC'. + * + * The device is configured via DIP-switches. + * For correct functioning in combination with NUT, set the DIP-switches to the following: + * switch 1-4: choose whatever suits your situation. Any combination will work with NUT. + * switch 5 ('=>' / 't'): set to OFF ('t') + * switch 6-10 (delay): set to OFF (no additional delay) + * switch 11 (INTERR.): set to ON + * switch 12 (ON/OFF): set to ON + * + * These UPSes are available with serial or USB port. + * Both are supported by this driver. The version with USB port simply contains + * a serial-over-USB chip, so as far as this driver is concerned, all models are + * actually serial models. + * + * The FTDI USB-to-serial converters in the USB-models are programmed with a non-standard + * Product ID (mine had 0403:e0e3), but can be used with the normal ftdi_sio driver: + * # modprobe ftdi_sio + * # echo 0403 e0e3 > /sys/bus/usb-serial/drivers/ftdi_sio/new_id + * + * This can also be automated via a udev rule: + * ACTION=="add", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="e0e3", \ + * RUN+="/sbin/modprobe ftdi_sio", \ + * RUN+="/bin/sh -c 'echo 0403 e0e3 > /sys/bus/usb-serial/drivers/ftdi_sio/new_id'" + * + * Use the following udev rule to create a persistent device name, for example /dev/ttyUPS: + * SUBSYSTEM=="tty" ATTRS{idVendor}=="0403", ATTRS{idProduct}=="e0e3" SYMLINK+="ttyUPS" + */ + +#include "main.h" +#include "serial.h" +#include "nut_stdint.h" + +#define DRIVER_NAME "Siemens SITOP UPS500 series driver" +#define DRIVER_VERSION "0.03" + +#define RX_BUFFER_SIZE 100 + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Matthijs H. ten Berge ", + DRV_EXPERIMENTAL, + { NULL } +}; + +/* The maximum number of consecutive polls in which the UPS does not provide any data: */ +static unsigned int max_polls_without_data; +/* The current number: */ +static unsigned int nr_polls_without_data; +/* receive buffer */ +static char rx_buffer[RX_BUFFER_SIZE]; +static size_t rx_count; + +static struct { + int battery_alarm; + int dc_input_low; + int on_battery; + int battery_above_85_percent; +} current_ups_status; + +/* remove n bytes from the head of rx_buffer, shift the remaining bytes to the start */ +static void rm_buffer_head(unsigned int n) { + if (rx_count <= n) { + /* nothing left */ + rx_count = 0; + return; + } + rx_count -= n; + memmove(rx_buffer, rx_buffer + n, rx_count); +} + +/* parse incoming data from the UPS. + * return true if something new was received. + */ +static int check_for_new_data() { + int new_data_received = 0; + int done = 0; + ssize_t num_received; + + while (!done) { + /* Get new data from the serial port. + * No extra delay, just get the chars that were already buffered. + */ + num_received = ser_get_buf(upsfd, rx_buffer + rx_count, RX_BUFFER_SIZE - rx_count, 0, 0); + if (num_received < 0) { + /* comm error */ + ser_comm_fail("error %zd while reading", num_received); + /* discard any remaining old data from the receive buffer: */ + rx_count = 0; + /* try to re-open the serial port: */ + if (upsfd) { + ser_close(upsfd, device_path); + upsfd = 0; + } + upsfd = ser_open_nf(device_path); + ser_set_speed_nf(upsfd, device_path, B9600); + done = 1; + } else if (num_received == 0) { + /* no (more) new data */ + done = 1; + } else { + rx_count += (unsigned int)num_received; + + /* parse received input data: */ + while (rx_count >= 5) { /* all valid input messages are strings of 5 characters */ + if (strncmp(rx_buffer, "BUFRD", 5) == 0) { + current_ups_status.battery_alarm = 0; + } else if (strncmp(rx_buffer, "ALARM", 5) == 0) { + current_ups_status.battery_alarm = 1; + } else if (strncmp(rx_buffer, "DC_OK", 5) == 0) { + current_ups_status.dc_input_low = 0; + } else if (strncmp(rx_buffer, "DC_LO", 5) == 0) { + current_ups_status.dc_input_low = 1; + } else if (strncmp(rx_buffer, "*****", 5) == 0) { + current_ups_status.on_battery = 0; + } else if (strncmp(rx_buffer, "*BAT*", 5) == 0) { + current_ups_status.on_battery = 1; + } else if (strncmp(rx_buffer, "BA>85", 5) == 0) { + current_ups_status.battery_above_85_percent = 1; + } else if (strncmp(rx_buffer, "BA<85", 5) == 0) { + current_ups_status.battery_above_85_percent = 0; + } else { + /* nothing sensible found at the start of the rx_buffer. */ + rm_buffer_head(1); + continue; /* skip the code below */ + } + rm_buffer_head(5); + new_data_received = 1; + } + } + } + return new_data_received; +} + + +static int instcmd(const char *cmdname, const char *extra) { + /* Note: the UPS does not really like to receive data. + * For example, sending an "R" without \n hangs the serial port. + * In that situation, the UPS will no longer send any status updates. + * For this reason, an additional \n is appended here. + * The commands are sent twice, because the first command is sometimes + * lost as well. + */ + if (!strcasecmp(cmdname, "shutdown.return")) { + upslogx(LOG_NOTICE, "instcmd: sending command R"); + ser_send_pace(upsfd, 200000, "\n\nR\n\n"); + ser_send_pace(upsfd, 200000, "R\n\n"); + return STAT_INSTCMD_HANDLED; + } + if (!strcasecmp(cmdname, "shutdown.stayoff")) { + upslogx(LOG_NOTICE, "instcmd: sending command S"); + ser_send_pace(upsfd, 200000, "\n\nS\n\n"); + ser_send_pace(upsfd, 200000, "S\n\n"); + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; +} + + +void upsdrv_initinfo(void) { + int max_attempts = 5; + int found = 0; + while (!found && max_attempts > 0) { + if (check_for_new_data()) { + found = 1; + } else { + sleep(1); /* Sleep a while, then try again */ + } + max_attempts--; + } + if (!found) { + fatalx(EXIT_FAILURE, "No data received from the UPS"); + } + + dstate_setinfo("device.mfr", "Siemens"); + dstate_setinfo("device.model", "SITOP UPS500 series"); + + /* supported commands: */ + dstate_addcmd("shutdown.stayoff"); + dstate_addcmd("shutdown.return"); + + upsh.instcmd = instcmd; +} + + +void upsdrv_updateinfo(void) { + if (check_for_new_data()) { + nr_polls_without_data = 0; + } else { + nr_polls_without_data++; + /* With unsigned int type, we can limit half-way like this: */ + if (nr_polls_without_data > INT_MAX) + nr_polls_without_data = INT_MAX; + } + + if (nr_polls_without_data > max_polls_without_data) { + /* data is stale */ + dstate_datastale(); + return; + } + + /* This is all we know about the charge level... */ + dstate_setinfo("battery.charge.approx", + (current_ups_status.battery_above_85_percent) ? ">85" : "<85"); + + status_init(); + + if (current_ups_status.dc_input_low || current_ups_status.on_battery) { + status_set("OB"); + } else { + status_set("OL"); + } + if (current_ups_status.battery_alarm) { + status_set("LB"); + } + + status_commit(); + dstate_dataok(); + ser_comm_good(); +} + +void upsdrv_shutdown(void) { + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ + instcmd("shutdown.return", NULL); +} + + +void upsdrv_help(void) { +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) { + /* allow '-x max_polls_without_data=' */ + addvar(VAR_VALUE, "max_polls_without_data", "The maximum number of consecutive polls in which the UPS does not provide any data."); +} + +void upsdrv_initups(void) { + char * maxPollsString; + unsigned int parsed; + + upsfd = ser_open(device_path); + ser_set_speed(upsfd, device_path, B9600); + + /* + * Fast polling is preferred, because + * A) the UPS spits out new data every 75 msec, + * B) some models in this SITOP series have a _very_ small capacity + * (< 10sec runtime), so every second might count. + */ + if (poll_interval > 5) { + upslogx(LOG_NOTICE, + "Option poll_interval is recommended to be lower than 5 (found: %jd)", + (intmax_t)poll_interval); + } + + /* option max_polls_without_data: */ + max_polls_without_data = 2; + maxPollsString = getval("max_polls_without_data"); + if (maxPollsString) { + if (str_to_uint(maxPollsString, &parsed, 10) == 1) { + max_polls_without_data = parsed; + } else { + upslog_with_errno(LOG_ERR, "Cannot parse option max_polls_without_data"); + } + } +} + +void upsdrv_cleanup(void) { + ser_close(upsfd, device_path); +} diff --git a/drivers/oneac.c b/drivers/oneac.c index 22a73ab..916fc72 100644 --- a/drivers/oneac.c +++ b/drivers/oneac.c @@ -1,57 +1,120 @@ /*vim ts=4*/ - -/* - * NUT Oneac EG and ON model specific drivers for UPS units using - * the Oneac Advanced Interface. If your UPS is equipped with the - * Oneac Basic Interface, use the genericups driver -*/ - -/* - Copyright (C) 2003 by Eric Lawson - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -/* 28 November 2003. Eric Lawson - * More or less complete re-write for NUT 1.5.9 - * This was somewhat easier than trying to beat the old driver code +/* oneac.c - Driver for Oneac UPS using the Advanced Interface. + * + * Supported Oneac UPS families in this driver: + * EG (late 80s, early 90s, plug-in serial interface card) + * ON (early and mid-90s, plug-in serial interface card) + * OZ (mid-90s on, DB-25 std., interface slot) + * OB (early 2000's on, big cabinet, DB-25 std., interface slot) + * + * Copyright (C) + * 2003 by Eric Lawson + * 2012 by Bill Elliot + * + * This program was sponsored by MGE UPS SYSTEMS, and now Eaton + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * History: + * - 7 February 2012. Bill Elliot + * Enhancing the driver for additional capabilities and later units. + * + * - 28 November 2003. Eric Lawson + * More or less complete re-write for NUT 1.5.9 + * This was somewhat easier than trying to beat the old driver code * into submission -*/ + * + */ #include "main.h" #include "serial.h" #include "oneac.h" -#define DRIVER_NAME "Oneac EG/ON UPS driver" -#define DRIVER_VERSION "0.51" +/* Prototypes to allow setting pointer before function is defined */ +int setcmd(const char* varname, const char* setvalue); +int instcmd(const char *cmdname, const char *extra); + +#define DRIVER_NAME "Oneac EG/ON/OZ/OB UPS driver" +#define DRIVER_VERSION "0.81" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, + "Bill Elliot \n" "Eric Lawson ", - DRV_EXPERIMENTAL, + DRV_STABLE, { NULL } }; -#define SECS 2 /*wait time*/ -#define USEC 0 /*rest of wait time*/ +#define SECS 0 /* Serial function wait time*/ +#define USEC 500000 /* Rest of serial function wait time*/ -void do_battery_test(void) +#define COMM_TRIES 3 /* Serial retries before "stale" */ + +static char UpsFamily [3]; + +/**************************************************************** + * Below are functions used only in this oneac driver * + ***************************************************************/ + +/* Since an installed network card may delay responses from the UPS + * allow for a repeat of the get request. Also confirm that + * the correct number of characters are returned. + */ + +static ssize_t OneacGetResponse (char* chBuff, const size_t BuffSize, int ExpectedCount) { - char buffer[256]; + int Retries = 10; /* x/2 seconds max with 500000 USEC */ + ssize_t return_val; + + do + { + return_val = ser_get_line(upsfd, + chBuff, BuffSize, ENDCHAR, IGNCHARS, SECS, USEC); + + if (return_val == ExpectedCount) + break; + + upsdebugx (3, + "!OneacGetResponse retry (%zd, %d)...", + return_val, Retries); + + } while (--Retries > 0); + + upsdebugx (4,"OneacGetResponse buffer: %s",chBuff); + + if (Retries == 0) + { + upsdebugx (2,"!!OneacGetResponse timeout..."); + return_val = 1; /* Comms error */ + } + else + { + if (Retries < 10) + upsdebugx (2,"OneacGetResponse recovered (%d)...", Retries); + + return_val = 0; /* Good comms */ + } + + return return_val; +} + +static void do_battery_test(void) +{ + char buffer[32]; if (getval("testtime") == NULL) snprintf(buffer, 3, "%s", DEFAULT_BAT_TEST_TIME); @@ -69,180 +132,444 @@ void do_battery_test(void) ser_send(upsfd,"%s%s%s",BAT_TEST_PREFIX,buffer,COMMAND_END); } - -/**************************************************************** - *below are the commands that are called by main (part of the * - *Above, are functions used only in this oneac driver * - ***************************************************************/ - -int instcmd(const char *cmdname, const char *extra) +static int SetOutputAllow(const char* lowval, const char* highval) { - if (!strcasecmp(cmdname, "test.failure.start")) { - ser_send(upsfd,"%s%s",SIM_PWR_FAIL,COMMAND_END); - return STAT_INSTCMD_HANDLED; + char buffer[32]; + + snprintf(buffer, 4, "%.3s", lowval); + + /*the UPS wants this value to always be three characters long*/ + /*so put a zero in front of the string, if needed.... */ + + if (strlen(buffer) < 3) + { + buffer[3] = '\0'; + buffer[2] = buffer[1]; + buffer[1] = buffer[0]; + buffer[0] = '0'; } - if (!strcasecmp(cmdname, "test.battery.start")) { - do_battery_test(); - return STAT_INSTCMD_HANDLED; + upsdebugx (2,"SetOutputAllow sending %s%.3s,%.3s...", + SETX_OUT_ALLOW, buffer, highval); + + ser_send(upsfd,"%s%.3s,%.3s%s", SETX_OUT_ALLOW, buffer, highval, + COMMAND_END); + ser_get_line(upsfd,buffer,sizeof(buffer), ENDCHAR, IGNCHARS,SECS,USEC); + + if(buffer[0] == DONT_UNDERSTAND) + { + upsdebugx (2,"SetOutputAllow got asterisk back..."); + + return 1; /* Invalid command */ } - if (!strcasecmp(cmdname, "test.battery.stop")) { - ser_send(upsfd,"%s00%s",BAT_TEST_PREFIX,COMMAND_END); - return STAT_INSTCMD_HANDLED; + return 0; /* Valid command */ +} + +static void EliminateLeadingZeroes (const char* buff1, int StringSize, char* buff2, + const size_t buff2size) +{ + int i = 0; + int j = 0; + + memset(buff2, '\0', buff2size); /* Fill with nulls */ + + /* Find first non-'0' */ + while ((i < (StringSize - 1) && (buff1[i] == '0'))) + { + i++; } - if (!strcasecmp(cmdname, "reset.input.minmax")) { - ser_send(upsfd,"%c%s",RESET_MIN_MAX, COMMAND_END); - return STAT_INSTCMD_HANDLED; + while (i < StringSize) /* Move rest of string */ + { + buff2[j++] = buff1[i++]; } - - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); - return STAT_INSTCMD_UNKNOWN; } +/**************************************************************** + * Below are the commands that are called by main * + ***************************************************************/ + +void upsdrv_initups(void) +{ + upsfd = ser_open(device_path); + ser_set_speed(upsfd, device_path, B9600); + + /*get the UPS in the right frame of mind*/ + ser_send_pace(upsfd, 100, "%s", COMMAND_END); + ser_send_pace(upsfd, 100, "%s", COMMAND_END); + sleep (1); +} + void upsdrv_initinfo(void) { - int i; + int i,j, k; + int VRange=0; + int timevalue; + ssize_t RetValue; char buffer[256], buffer2[32]; + + /* All families should reply to this request so we can confirm that it is + * an ONEAC UPS + */ + ser_flush_in(upsfd,"",0); - ser_send(upsfd,"%c%s",GET_MFR,COMMAND_END); - ser_get_line(upsfd, buffer, sizeof(buffer),ENDCHAR,IGNCHARS,SECS,USEC); - if(strncmp(buffer,MFGR, sizeof(MFGR))) - fatalx(EXIT_FAILURE, "Unable to connect to ONEAC UPS on %s\n",device_path); - - dstate_setinfo("ups.mfr", "%s", buffer); - dstate_addcmd("test.battery.start"); - dstate_addcmd("test.battery.stop"); - dstate_addcmd("test.failure.start"); - dstate_addcmd("reset.input.minmax"); + ser_send(upsfd,"%c%s",GET_FAMILY,COMMAND_END); - - upsh.instcmd = instcmd; - - /*set some stuff that shouldn't change after initialization*/ - /*this stuff is common to both the EG and ON family of UPS */ - - /*firmware revision*/ - ser_send(upsfd,"%c%s", GET_VERSION, COMMAND_END); - ser_get_line(upsfd, buffer, sizeof(buffer),ENDCHAR,IGNCHARS,SECS,USEC); - dstate_setinfo("ups.firmware", "%.3s",buffer); - - /*nominal AC frequency setting --either 50 or 60*/ - ser_send(upsfd,"%c%s", GET_NOM_FREQ, COMMAND_END); - ser_get_line(upsfd,buffer,sizeof(buffer),ENDCHAR,IGNCHARS,SECS,USEC); - dstate_setinfo("input.frequency", "%.2s", buffer); - - - /*UPS Model (either ON, or EG series of UPS)*/ - - ser_send(upsfd,"%c%s", GET_FAMILY,COMMAND_END); - ser_get_line(upsfd,buffer,sizeof(buffer),ENDCHAR,IGNCHARS,SECS,USEC); - dstate_setinfo("ups.model", "%.2s",buffer); - printf("Found %.2s family of Oneac UPS\n", buffer); - - if ((strncmp(buffer,FAMILY_ON,2) != 0 && - strncmp(buffer,FAMILY_ON_EXT,2) != 0) || - strncmp(buffer,FAMILY_EG,2) == 0) - printf("Unknown family of UPS. Assuming EG capabilities.\n"); - - /*Get the actual model string for ON UPS reported as OZ family*/ - if (strncmp (dstate_getinfo("ups.model"), FAMILY_ON_EXT, 2) == 0) { - ser_flush_in(upsfd,"",0); - ser_send(upsfd,"%c%s",GET_ALL_EXT_2,COMMAND_END); - ser_get_line(upsfd, buffer, sizeof(buffer),ENDCHAR,IGNCHARS,SECS,USEC); - - /*UPS Model (full string)*/ - memset(buffer2, '\0', 32); - strncpy(buffer2,&buffer[5], 10); - for (i = 9; i >= 0 && buffer2[i] == ' '; --i) { - buffer2[i] = '\0'; + if(OneacGetResponse (buffer, sizeof(buffer), 2)) + { + fatalx(EXIT_FAILURE, "Serial timeout with ONEAC UPS on %s\n", + device_path); + } + else + { + if (strncmp(buffer,FAMILY_ON,FAMILY_SIZE) != 0 && + strncmp(buffer,FAMILY_OZ,FAMILY_SIZE) != 0 && + strncmp(buffer,FAMILY_OB,FAMILY_SIZE) != 0 && + strncmp(buffer,FAMILY_EG,FAMILY_SIZE) != 0) + { + fatalx(EXIT_FAILURE, "Did not find an ONEAC UPS on %s\n", + device_path); } - - dstate_setinfo("ups.model", "%s", buffer2); - printf("Found %.10s UPS\n", buffer2); } - /*The ON (OZ) series of UPS supports more stuff than does the EG. - *Take care of the ON (OZ) only stuff here - */ - if(strncmp (dstate_getinfo("ups.model"), FAMILY_ON, 2) == 0 || - strncmp (dstate_getinfo("ups.model"), FAMILY_ON_EXT, 2) == 0) { - /*now set the ON specific "static" parameters*/ + /* UPS Model (either EG, ON, OZ or OB series of UPS) */ + strncpy(UpsFamily, buffer, FAMILY_SIZE); + UpsFamily[2] = '\0'; + dstate_setinfo("device.model", "%s",UpsFamily); + printf("Found %s family of Oneac UPS\n", UpsFamily); - /*nominal input voltage*/ + dstate_setinfo("ups.type", "%s", "Line Interactive"); - ser_send(upsfd,"%c%s",GET_NOM_VOLTAGE,COMMAND_END); - ser_get_line(upsfd,buffer,sizeof(buffer),ENDCHAR,IGNCHARS,SECS,USEC); + dstate_addcmd("test.battery.start.quick"); + dstate_addcmd("test.battery.stop"); + dstate_addcmd("test.failure.start"); + dstate_addcmd("shutdown.return"); + dstate_addcmd("shutdown.stop"); + dstate_addcmd("shutdown.reboot"); - switch (buffer[0]) { + upsh.setvar = setcmd; + upsh.instcmd = instcmd; + + /* set some stuff that shouldn't change after initialization */ + /* this stuff is common to all families of UPS */ + + ser_send(upsfd,"%c%s",GET_ALL,COMMAND_END); + + if (strncmp(UpsFamily, FAMILY_EG, FAMILY_SIZE) == 0) + { + RetValue = OneacGetResponse (buffer, sizeof(buffer), + GETALL_EG_RESP_SIZE); + } + else + { + RetValue = OneacGetResponse (buffer, sizeof(buffer), GETALL_RESP_SIZE); + } + + if(RetValue) + { + fatalx(EXIT_FAILURE, "Serial timeout(2) with ONEAC UPS on %s\n", + device_path); + } + + /* Manufacturer */ + dstate_setinfo("device.mfr", "%.5s", buffer); + + /*firmware revision*/ + dstate_setinfo("ups.firmware", "%.3s",buffer+7); + + /*nominal AC frequency setting --either 50 or 60*/ + dstate_setinfo("input.frequency.nominal", "%.2s", buffer+20); + dstate_setinfo("output.frequency.nominal", "%.2s", buffer+20); + + /* Shutdown delay in seconds...can be changed by user */ + if (getval("offdelay") == NULL) + dstate_setinfo("ups.delay.shutdown", "0"); + else + dstate_setinfo("ups.delay.shutdown", "%s", getval("offdelay")); + + dstate_setflags("ups.delay.shutdown", ST_FLAG_STRING | ST_FLAG_RW); + dstate_setaux("ups.delay.shutdown", GET_SHUTDOWN_RESP_SIZE); + + /* Setup some ON/OZ/OB only stuff ... i.e. not EG */ + + if (strncmp(UpsFamily, FAMILY_EG, FAMILY_SIZE) != 0) + { + dstate_addcmd("reset.input.minmax"); + + /*nominal input voltage*/ + + VRange = buffer[26]; /* Keep for later use also */ + + switch (VRange) /* Will be '1' or '2' */ + { case V120AC: - dstate_setinfo("output.voltage.nominal", - "120"); + dstate_setinfo("input.voltage.nominal", "120"); + dstate_setinfo("output.voltage.nominal", "120"); break; case V230AC: - dstate_setinfo("output.voltage.nominal", - "240"); + dstate_setinfo("input.voltage.nominal", "230"); + dstate_setinfo("output.voltage.nominal", "230"); break; default: upslogx(LOG_INFO,"Oneac: " - "Invalid voltage parameter from UPS"); + "Invalid nom voltage parameter from UPS [%c]", VRange); + } + } + + /* Setup some OZ/OB only stuff */ + + if ((strncmp (UpsFamily, FAMILY_OZ, FAMILY_SIZE) == 0) || + (strncmp (UpsFamily, FAMILY_OB, FAMILY_SIZE) == 0)) + { + dstate_addcmd("test.panel.start"); + dstate_addcmd("test.battery.start.deep"); + dstate_addcmd("beeper.enable"); + dstate_addcmd("beeper.disable"); + dstate_addcmd("beeper.mute"); + + dstate_setaux("ups.delay.shutdown", GETX_SHUTDOWN_RESP_SIZE); + + ser_flush_in(upsfd,"",0); + ser_send(upsfd,"%c%s",GETX_ALL_2,COMMAND_END); + if(OneacGetResponse (buffer, sizeof(buffer), GETX_ALL2_RESP_SIZE)) + { + fatalx(EXIT_FAILURE, "Serial timeout(3) with ONEAC UPS on %s\n", + device_path); + } + + /* Low and high output trip points */ + EliminateLeadingZeroes (buffer+73, 3, buffer2, sizeof(buffer2)); + dstate_setinfo("input.transfer.low", "%s", buffer2); + dstate_setflags("input.transfer.low", ST_FLAG_STRING | ST_FLAG_RW ); + dstate_setaux("input.transfer.low", 3); + + EliminateLeadingZeroes (buffer+76, 3, buffer2, sizeof(buffer2)); + dstate_setinfo("input.transfer.high", "%s", buffer2); + dstate_setflags("input.transfer.high", ST_FLAG_STRING | ST_FLAG_RW); + dstate_setaux("input.transfer.high", 3); + + /* Restart delay */ + EliminateLeadingZeroes (buffer+84, 4, buffer2, sizeof(buffer2)); + dstate_setinfo("ups.delay.start", "%s", buffer2); + dstate_setflags("ups.delay.start", ST_FLAG_STRING | ST_FLAG_RW); + dstate_setaux("ups.delay.start", 4); + + /* Low Batt at time */ + strncpy(buffer2, buffer+82, 2); + buffer2[2]='\0'; + timevalue = atoi(buffer2) * 60; /* Change minutes to seconds */ + dstate_setinfo("battery.runtime.low", "%d",timevalue); + dstate_setflags("battery.runtime.low", ST_FLAG_STRING | ST_FLAG_RW); + dstate_setaux("battery.runtime.low", 2); + + /*Get the actual model string for ON UPS reported as OZ/OB family*/ + + /*UPS Model (full string)*/ + memset(buffer2, '\0', 32); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic ignored "-Wformat-truncation" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRINGOP_TRUNCATION +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif + strncpy(buffer2, buffer + 5, 10); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic pop +#endif + for (i = 9; i >= 0 && buffer2[i] == ' '; --i) + { + buffer2[i] = '\0'; + } + + dstate_setinfo("device.model", "%s", buffer2); + + /* Serial number */ + dstate_setinfo("device.serial", "%.4s-%.4s", buffer+18, buffer+22); + printf("Found %.10s UPS with serial number %.4s-%.4s\n", + buffer2, buffer+18, buffer+22); + + /* Manufacture Date */ + dstate_setinfo("ups.mfr.date", "%.6s (yymmdd)", buffer+38); + + /* Battery Replace Date */ + dstate_setinfo("battery.date", "%.6s (yymmdd)", buffer+44); + dstate_setflags("battery.date", ST_FLAG_STRING | ST_FLAG_RW); + dstate_setaux("battery.date", 6); + + /* Real power nominal */ + EliminateLeadingZeroes (buffer+55, 5, buffer2, sizeof(buffer2)); + dstate_setinfo("ups.realpower.nominal", "%s", buffer2); + + /* Set up ups.start.auto to be writable */ + dstate_setinfo("ups.start.auto", "yes"); + dstate_setflags("ups.start.auto", ST_FLAG_STRING | ST_FLAG_RW); + dstate_setaux("ups.start.auto", 3); + + /* Get output window min/max points from OB or OZ v1.9 or later */ + if ((strncmp (UpsFamily, FAMILY_OB, FAMILY_SIZE) == 0) || + (strcmp (dstate_getinfo("ups.firmware"), MIN_ALLOW_FW) >= 0 )) + { + upsdebugx (2,"Can get output window min/max! (%s)", + dstate_getinfo("ups.firmware")); + + ser_send(upsfd,"%s%s",GETX_ALLOW_RANGE,COMMAND_END); + if(OneacGetResponse (buffer, sizeof(buffer), GETX_RANGE_RESP_SIZE)) + { + fatalx(EXIT_FAILURE, + "Serial timeout(4) with ONEAC UPS on %s\n",device_path); + } + + strncpy(buffer2, buffer, 3); + buffer2[3]='\0'; + i = atoi(buffer2); /* Minimum voltage */ + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic ignored "-Wformat-truncation" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRINGOP_TRUNCATION +#pragma GCC diagnostic ignored "-Wstringop-truncation" +#endif + strncpy(buffer2, buffer + 4, 3); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION +#pragma GCC diagnostic pop +#endif + j = atoi(buffer2); /* Maximum voltage */ + + strncpy(buffer2, buffer+8, 2); + buffer2[2]='\0'; + k = atoi(buffer2); /* Spread between */ + + dstate_setinfo("input.transfer.low.min", "%3d", i); + dstate_setinfo("input.transfer.low.max", "%3d", j-k); + dstate_setinfo("input.transfer.high.min", "%3d", i+k); + dstate_setinfo("input.transfer.high.max", "%3d", j); + + } + else + { + /* Use default values from firmware */ + upsdebugx (2,"Using trip defaults (%s)...", + dstate_getinfo("ups.firmware")); + + switch (VRange) /* Held from initial use */ + { + case V120AC: + dstate_setinfo("input.transfer.low.min", "90"); + dstate_setinfo("input.transfer.low.max", "120"); + dstate_setinfo("input.transfer.high.min", "110"); + dstate_setinfo("input.transfer.high.max", "140"); + break; + + case V230AC: + dstate_setinfo("input.transfer.low.min", "172"); + dstate_setinfo("input.transfer.low.max", "228"); + dstate_setinfo("input.transfer.high.min", "212"); + dstate_setinfo("input.transfer.high.max", "268"); + break; + + default: + ; + + } } } } void upsdrv_updateinfo(void) { - char buffer[256]; - int ret_value; - + static int CommTry = COMM_TRIES; /* Comm loss counter */ + char buffer[256]; /* Main response buffer */ + char buffer2[32]; /* Conversion buffer */ + char s; + ssize_t RetValue; + int timevalue; + /* Start with EG/ON information */ ser_flush_in(upsfd,"",0); /*just in case*/ - ser_send (upsfd,"%c%s",GET_ALL,COMMAND_END); - ret_value = ser_get_line(upsfd,buffer,sizeof(buffer),ENDCHAR, - IGNCHARS,SECS,USEC); - - upsdebugx (2,"upsrecv_updateinfo: upsrecv returned: %s\n",buffer); - if (ret_value < 1) + ser_send (upsfd,"%c%s", GET_ALL, COMMAND_END); + + if (strncmp(UpsFamily, FAMILY_EG, FAMILY_SIZE) == 0) { - ser_comm_fail("Oneac UPS Comm failure on port %s",device_path); - dstate_datastale(); + RetValue = OneacGetResponse (buffer,sizeof(buffer),GETALL_EG_RESP_SIZE); } else { + RetValue = OneacGetResponse (buffer, sizeof(buffer), GETALL_RESP_SIZE); + } + + if ((RetValue != 0) && (CommTry == 0)) + { + ser_comm_fail("Oneac UPS Comm failure continues on port %s", + device_path); + } + else if (RetValue != 0) + { + if (--CommTry == 0) + { + ser_comm_fail("Oneac UPS Comm failure on port %s",device_path); + dstate_datastale(); + } + upsdebugx(2,"Oneac: Update serial comm retry value: %d", CommTry); + + return; + } + else + { + CommTry = COMM_TRIES; /* Reset serial retries */ + + s = buffer[12]; + status_init(); + alarm_init(); + /*take care of the UPS status information*/ - switch (buffer[12]) { - case NORMAL : - status_set("OL"); - break; - case ON_BAT_LOW_LINE : - case ON_BAT_HI_LINE : + if (s == '@') + { + status_set("OL"); + } + else + { + if (s & 0x01) /* On Battery */ + { status_set("OB"); - break; - case LO_BAT_LOW_LINE : - case LO_BAT_HI_LINE : - status_set("OB LB"); - break; - case TOO_HOT : - status_set("OVER OB LB"); - break; - case FIX_ME : - dstate_setinfo("ups.test.result","UPS Internal Failure"); - break; - case BAD_BAT : - status_set("RB"); - break; - default : /*cry for attention, fake a status*/ - /*Would another status be better?*/ - upslogx (LOG_ERR, "Oneac: Unknown UPS status"); + } + else + { status_set("OL"); + } + + if (s & 0x02) /* Low Battery */ + status_set("LB"); + + if (s & 0x04) /* General fault */ + { + dstate_setinfo("ups.test.result","UPS Internal Failure"); + } + else + { + dstate_setinfo("ups.test.result","Normal"); + } + + if (s & 0x08) /* Replace Battery */ + status_set("RB"); + +/* if (s & 0x10) */ /* High Line */ + + if (s & 0x20) /* Unit is hot */ + alarm_set("OVERHEAT"); } - /*take care of the reason why the UPS last transfered to battery*/ + /*take care of the reason why the UPS last transferred to battery*/ switch (buffer[13]) { case XFER_BLACKOUT : dstate_setinfo("input.transfer.reason", "Blackout"); @@ -256,66 +583,470 @@ void upsdrv_updateinfo(void) "High Input Voltage"); break; case NO_VALUE_YET : - dstate_setinfo("input.transfer.reason", + dstate_setinfo("input.transfer.reason", "No transfer yet."); break; default : upslogx(LOG_INFO,"Oneac: Unknown reason for UPS battery" - " transfer"); + " transfer [%c]", buffer[13]); } - /* now update info for only the ON family of UPS*/ - if (strncmp(dstate_getinfo("ups.model"), FAMILY_ON, 2) == 0) { + /* now update info for only the non-EG families of UPS*/ + + if (strncmp(UpsFamily, FAMILY_EG, FAMILY_SIZE) != 0) + { dstate_setinfo("ups.load", "0%.2s",buffer+31); + /* Output ON or OFF? */ + if(buffer[27] == NO_VALUE_YET) + status_set("OFF"); + /*battery charge*/ if(buffer[10] == YES) dstate_setinfo("battery.charge", "0%.2s",buffer+33); - else dstate_setinfo("battery.charge", "100"); + else + dstate_setinfo("battery.charge", "100"); - dstate_setinfo("input.voltage", "%.3s",buffer+35); - dstate_setinfo("input.voltage.minimum", "%.3s",buffer+38); - dstate_setinfo("input.voltage.maximum", "%.3s",buffer+41); - dstate_setinfo("output.voltage", "%.3s",buffer+44); - if (buffer[47] == YES) status_set("BOOST"); + EliminateLeadingZeroes (buffer+35, 3, buffer2, sizeof(buffer2)); + dstate_setinfo("input.voltage", "%s",buffer2); + + EliminateLeadingZeroes (buffer+38, 3, buffer2, sizeof(buffer2)); + dstate_setinfo("input.voltage.minimum", "%s",buffer2); + + EliminateLeadingZeroes (buffer+41, 3, buffer2, sizeof(buffer2)); + dstate_setinfo("input.voltage.maximum", "%s",buffer2); + + EliminateLeadingZeroes (buffer+44, 3, buffer2, sizeof(buffer2)); + dstate_setinfo("output.voltage", "%s",buffer2); + + if (buffer[15] == NO_VALUE_YET) + { + dstate_delinfo("ups.timer.shutdown"); + } + else + { + /* A shutdown is underway! */ + status_set("FSD"); + + if(buffer[15] != HIGH_COUNT) + { + EliminateLeadingZeroes (buffer+15, 3, buffer2, + sizeof(buffer2)); + dstate_setinfo("ups.timer.shutdown", "%s", buffer2); + } + else + { + dstate_setinfo("ups.timer.shutdown", "999"); + } + } + + if (buffer[47] == YES) + status_set("BOOST"); } + + /* Now update info for only the OZ/OB families of UPS */ + + if ((strncmp(UpsFamily, FAMILY_OZ, FAMILY_SIZE) == 0) || + (strncmp(UpsFamily, FAMILY_OB, FAMILY_SIZE) == 0)) + { + ser_flush_in(upsfd,"",0); /*just in case*/ + ser_send (upsfd,"%c%s",GETX_ALL_1,COMMAND_END); + RetValue = OneacGetResponse (buffer, sizeof(buffer), + GETX_ALL1_RESP_SIZE); + + if(RetValue) + { + if (--CommTry == 0) + { + ser_comm_fail("Oneac (OZ) UPS Comm failure on port %s", + device_path); + dstate_datastale(); + } + + upsdebugx(2,"Oneac: " + "Update (OZ) serial comm retry value: %d", CommTry); + } + else + { + CommTry = COMM_TRIES; /* Reset count */ + + EliminateLeadingZeroes (buffer+57, 5, buffer2, sizeof(buffer2)); + dstate_setinfo("ups.realpower", "%s",buffer2); + + dstate_setinfo("input.frequency", "%.2s.%c", + buffer+42,buffer[44]); + dstate_setinfo("output.frequency", "%.2s.%c", + buffer+76, buffer[78]); + + EliminateLeadingZeroes (buffer+29, 3, buffer2, sizeof(buffer2)); + dstate_setinfo("battery.voltage", "%s.%c",buffer2, buffer[32]); + + dstate_setinfo("ups.temperature", "%.2s",buffer+13); + dstate_setinfo("ups.load", "%.3s",buffer+73); + + strncpy(buffer2, buffer+19, 4); + buffer2[4]='\0'; + timevalue = atoi(buffer2) * 60; /* Change mins to secs */ + dstate_setinfo("battery.runtime", "%d",timevalue); + + /* Now some individual requests... */ + + /* Battery replace date */ + ser_send (upsfd,"%c%s",GETX_BATT_REPLACED,COMMAND_END); + if(!OneacGetResponse (buffer, sizeof(buffer), + GETX_DATE_RESP_SIZE)) + dstate_setinfo("battery.date", "%.6s (yymmdd)", buffer); + + /* Low and high output trip points */ + ser_send (upsfd,"%c%s",GETX_LOW_OUT_ALLOW,COMMAND_END); + if(!OneacGetResponse (buffer, sizeof(buffer), + GETX_ALLOW_RESP_SIZE)) + { + EliminateLeadingZeroes (buffer, 3, buffer2,sizeof(buffer2)); + dstate_setinfo("input.transfer.low", "%s", buffer2); + } + + ser_send (upsfd,"%c%s",GETX_HI_OUT_ALLOW,COMMAND_END); + if(!OneacGetResponse (buffer, sizeof(buffer), + GETX_ALLOW_RESP_SIZE)) + dstate_setinfo("input.transfer.high", "%s", buffer); + + /* Restart delay */ + ser_send (upsfd,"%c%s",GETX_RESTART_DLY,COMMAND_END); + if(!OneacGetResponse (buffer, sizeof(buffer), + GETX_RSTRT_RESP_SIZE)) + { + EliminateLeadingZeroes (buffer, 4, buffer2, + sizeof(buffer2)); + dstate_setinfo("ups.delay.start", "%s", buffer2); + } + + /* Buzzer state */ + ser_send (upsfd,"%s%s",GETX_BUZZER_WHAT,COMMAND_END); + if(!OneacGetResponse (buffer, sizeof(buffer), 1)) + { + switch (buffer[0]) + { + case BUZZER_ENABLED : + dstate_setinfo("ups.beeper.status", "enabled"); + break; + case BUZZER_DISABLED : + dstate_setinfo("ups.beeper.status", "disabled"); + break; + case BUZZER_MUTED : + dstate_setinfo("ups.beeper.status", "muted"); + break; + default : + dstate_setinfo("ups.beeper.status", "enabled"); + } + } + + /* Auto start setting */ + ser_send (upsfd,"%s%s",GETX_AUTO_START,COMMAND_END); + if(!OneacGetResponse (buffer, sizeof(buffer), 1)) + { + if (buffer[0] == '0') + dstate_setinfo("ups.start.auto", "yes"); + else + dstate_setinfo("ups.start.auto", "no"); + } + + /* Low Batt at time */ + ser_send (upsfd,"%c%s",GETX_LOW_BATT_TIME,COMMAND_END); + if(!OneacGetResponse (buffer, sizeof(buffer), 2)) + { + strncpy(buffer2, buffer, 2); + buffer2[2]='\0'; + timevalue = atoi(buffer2) * 60; /* Mins to secs */ + dstate_setinfo("battery.runtime.low", "%d",timevalue); + } + + /* Shutdown timer */ + ser_send (upsfd,"%c%s",GETX_SHUTDOWN,COMMAND_END); + if(!OneacGetResponse (buffer, sizeof(buffer), + GETX_SHUTDOWN_RESP_SIZE)) + { + /* ON would have handled NO_VALUE_YET and setting FSD + * above so only deal with counter value here. + */ + if (buffer[0] != NO_VALUE_YET) + { + EliminateLeadingZeroes (buffer, 5, buffer2, + sizeof(buffer2)); + dstate_setinfo("ups.timer.shutdown", "%s", buffer2); + } + } + + /* Restart timer */ + ser_send (upsfd,"%s%s",GETX_RESTART_COUNT,COMMAND_END); + if(!OneacGetResponse (buffer, sizeof(buffer), + GETX_RSTRT_RESP_SIZE)) + { + if (atoi(buffer) == 0) + { + dstate_delinfo("ups.timer.start"); + } + else + { + EliminateLeadingZeroes (buffer, 4, buffer2, + sizeof(buffer2)); + dstate_setinfo("ups.timer.start", "%s", buffer2); + } + } + } + } + + alarm_commit(); status_commit(); - dstate_dataok(); - ser_comm_good(); + + /* If the comm retry counter is zero then datastale has been set. + * We don't want to set dataok or ser_comm_good if that is the case. + */ + + if (CommTry != 0) + { + dstate_dataok(); + ser_comm_good(); + } } } + void upsdrv_shutdown(void) { ser_send(upsfd,"%s",SHUTDOWN); } - void upsdrv_help(void) { printf("\n---------\nNOTE:\n"); - printf("You must set the UPS interface card DIP switch to 9600BPS\n"); -} - -void upsdrv_makevartable(void) -{ - addvar(VAR_VALUE,"testtime", - "Change battery test time from 2 minute default."); -} - -void upsdrv_initups(void) -{ - upsfd = ser_open(device_path); - ser_set_speed(upsfd, device_path, B9600); - -/*get the UPS in the right frame of mind*/ - - ser_send(upsfd,"%s", COMMAND_END); - sleep (1); - ser_send(upsfd,"%s", COMMAND_END); - sleep (1); + printf("You must set the UPS interface card DIP switch to 9600 BPS\n"); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } + +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE,"testtime", + "Change battery test time from the 2 minute default."); + + addvar(VAR_VALUE,"offdelay", + "Change shutdown delay time from 0 second default."); +} + +int instcmd(const char *cmdname, const char *extra) +{ + int i; + + upsdebugx(2, "In instcmd with %s and extra %s.", cmdname, extra); + + if (!strcasecmp(cmdname, "test.failure.start")) { + ser_send(upsfd,"%s%s",SIM_PWR_FAIL,COMMAND_END); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "shutdown.return")) { + + i = atoi(dstate_getinfo("ups.delay.shutdown")); + + if ((strncmp (UpsFamily, FAMILY_OZ, FAMILY_SIZE) == 0) || + (strncmp (UpsFamily, FAMILY_OB, FAMILY_SIZE) == 0)) + { + upsdebugx(3, "Shutdown using %c%d...", DELAYED_SHUTDOWN_PREFIX, i); + ser_send(upsfd,"%c%d%s",DELAYED_SHUTDOWN_PREFIX, i, COMMAND_END); + } + else + { + upsdebugx(3, "Shutdown using %c%03d...",DELAYED_SHUTDOWN_PREFIX, i); + ser_send(upsfd,"%c%03d%s",DELAYED_SHUTDOWN_PREFIX, i, COMMAND_END); + } + + return STAT_INSTCMD_HANDLED; + } + + if(!strcasecmp(cmdname, "shutdown.reboot")) { + ser_send(upsfd, "%s", SHUTDOWN); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "shutdown.stop")) { + ser_send(upsfd,"%c%s",DELAYED_SHUTDOWN_PREFIX,COMMAND_END); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "test.battery.start.quick")) { + do_battery_test(); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "test.battery.start.deep")) { + ser_send(upsfd, "%s%s", TEST_BATT_DEEP, COMMAND_END); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "test.battery.stop")) + { + if ((strncmp (UpsFamily, FAMILY_EG, FAMILY_SIZE) == 0) || + (strncmp (UpsFamily, FAMILY_ON, FAMILY_SIZE) == 0)) + { + ser_send(upsfd,"%s00%s",BAT_TEST_PREFIX,COMMAND_END); + } + else + { + ser_send(upsfd,"%c%s",TEST_ABORT,COMMAND_END); + } + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "reset.input.minmax")) { + ser_send(upsfd,"%c%s",RESET_MIN_MAX, COMMAND_END); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "beeper.enable")) { + ser_send(upsfd,"%c%c%s",SETX_BUZZER_PREFIX, BUZZER_ENABLED,COMMAND_END); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "beeper.disable")) { + ser_send(upsfd,"%c%c%s",SETX_BUZZER_PREFIX,BUZZER_DISABLED,COMMAND_END); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "beeper.mute")) { + ser_send(upsfd,"%c%c%s",SETX_BUZZER_PREFIX, BUZZER_MUTED, COMMAND_END); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "test.panel.start")) { + ser_send(upsfd,"%s%s",TEST_INDICATORS, COMMAND_END); + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + return STAT_INSTCMD_UNKNOWN; +} + + +int setcmd(const char* varname, const char* setvalue) +{ + upsdebugx(2, "In setcmd for %s with %s...", varname, setvalue); + + if (!strcasecmp(varname, "ups.delay.shutdown")) + { + if ((strncmp (UpsFamily, FAMILY_OZ, FAMILY_SIZE) == 0) || + (strncmp (UpsFamily, FAMILY_OB, FAMILY_SIZE) == 0)) + { + if (atoi(setvalue) > 65535) + { + upsdebugx(2, "Too big for OZ/OB (>65535)...(%s)", setvalue); + return STAT_SET_UNKNOWN; + } + } + else + { + if (atoi(setvalue) > 999) + { + upsdebugx(2, "Too big for EG/ON (>999)...(%s)", setvalue); + return STAT_SET_UNKNOWN; + } + } + + dstate_setinfo("ups.delay.shutdown", "%s", setvalue); + return STAT_SET_HANDLED; + } + + if (!strcasecmp(varname, "input.transfer.low")) + { + if (SetOutputAllow(setvalue, dstate_getinfo("input.transfer.high"))) + { + return STAT_SET_UNKNOWN; + } + else + { + dstate_setinfo("input.transfer.low" , "%s", setvalue); + return STAT_SET_HANDLED; + } + } + + if (!strcasecmp(varname, "input.transfer.high")) + { + if (SetOutputAllow(dstate_getinfo("input.transfer.low"), setvalue)) + { + return STAT_SET_UNKNOWN; + } + else + { + dstate_setinfo("input.transfer.high" , "%s", setvalue); + return STAT_SET_HANDLED; + } + } + + if (!strcasecmp(varname, "battery.date")) + { + if(strlen(setvalue) == GETX_DATE_RESP_SIZE) /* yymmdd (6 chars) */ + { + ser_send(upsfd, "%s%s%s", SETX_BATTERY_DATE, setvalue, COMMAND_END); + dstate_setinfo("battery.date", "%s (yymmdd)", setvalue); + return STAT_SET_HANDLED; + } + else + { + return STAT_SET_UNKNOWN; + } + } + + if (!strcasecmp(varname, "ups.delay.start")) + { + if (atoi(setvalue) <= 9999) + { + ser_send(upsfd,"%s%s%s",SETX_RESTART_DELAY, setvalue, COMMAND_END); + + dstate_setinfo("ups.delay.start", "%s", setvalue); + return STAT_SET_HANDLED; + } + else + { + return STAT_SET_UNKNOWN; + } + } + + if (!strcasecmp(varname, "battery.runtime.low")) + { + if (atoi(setvalue) <= 99) + { + ser_send(upsfd,"%s%s%s",SETX_LOWBATT_AT, setvalue, COMMAND_END); + + dstate_setinfo("battery.runtime.low", "%s", setvalue); + return STAT_SET_HANDLED; + } + else + { + return STAT_SET_UNKNOWN; + } + } + + if (!strcasecmp(varname, "ups.start.auto")) + { + if (!strcasecmp(setvalue, "yes")) + { + ser_send(upsfd,"%c0%s",SETX_AUTO_START, COMMAND_END); + dstate_setinfo("ups.start.auto", "yes"); + return STAT_SET_HANDLED; + } + else if (!strcasecmp(setvalue, "no")) + { + ser_send(upsfd,"%c1%s",SETX_AUTO_START, COMMAND_END); + dstate_setinfo("ups.start.auto", "no"); + return STAT_SET_HANDLED; + } + + return STAT_SET_UNKNOWN; + } + + upslogx(LOG_NOTICE, "setcmd: unknown command [%s]", varname); + + return STAT_SET_UNKNOWN; +} diff --git a/drivers/oneac.h b/drivers/oneac.h index c90d74b..7be4b02 100644 --- a/drivers/oneac.h +++ b/drivers/oneac.h @@ -1,43 +1,57 @@ -/* -Copyright (C) 2002 Eric Lawson - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +/* oneac.h - Driver for Oneac UPS using the Advanced Interface. + * + * Copyright (C) + * 2003 by Eric Lawson + * 2012 by Bill Elliot + * + * This program was sponsored by MGE UPS SYSTEMS, and now Eaton + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * */ +#ifndef NUT_ONEAC_H_SEEN +#define NUT_ONEAC_H_SEEN 1 + /*misc stuff*/ #define ENDCHAR '\r' #define IGNCHARS "\n" #define COMMAND_END "\r\n" #define DEFAULT_BAT_TEST_TIME "02" -/*Information requests*/ +/*Information requests -- EG level */ -#define GET_ALL '%' -#define GET_ALL_EXT_2 '^' -#define GET_ALL_EXT_1 '&' -#define GET_MFR 'M' -#define GET_FAMILY 'F' -#define GET_VERSION 'N' -#define GET_ON_INVERTER 'G' -#define GET_BATLOW 'K' -#define GET_STATUS 'X' -#define GET_LAST_XFER 'W' +#define GET_ALL '%' +#define GETALL_EG_RESP_SIZE 22 +#define GETALL_RESP_SIZE 48 + +#define GET_MFR 'M' +#define GET_FAMILY 'F' +#define GET_VERSION 'N' +#define GET_ON_INVERTER 'G' +#define GET_BATLOW 'K' +#define GET_STATUS 'X' +#define GET_LAST_XFER 'W' #define GET_INVERTER_RDY 'I' -#define GET_TEST_TIME 'Q' -#define GET_NOM_FREQ 'H' -#define GET_NOM_VOLTAGE 'V' +#define GET_SHUTDOWN 'O' +#define GET_TEST_TIME 'Q' +#define GET_NOM_FREQ 'H' + +/*Information requests -- ON level (EG plus these) */ + +#define GET_NOM_VOLTAGE 'V' #define GET_DISPLAY_CODE 'D' #define GET_CONDITION_CODE 'C' #define GET_PERCENT_LOAD 'P' @@ -45,48 +59,130 @@ Copyright (C) 2002 Eric Lawson #define GET_INPUT_LINE_VOLT 'L' #define GET_MIN_INPUT_VOLT 'A' #define GET_MAX_INPUT_VOLT 'E' -#define GET_OUTPUT_VOLT 'S' -#define GET_BOOSTING 'B' +#define GET_OUTPUT_VOLT 'S' +#define GET_BOOSTING 'B' -/*Control functions*/ -#define SIM_PWR_FAIL "\x02\x15" /*^B^U 15 second battery test*/ -#define SHUTDOWN "\x0f\x06" /*^O^F (a letter O)*/ +#define GET_SHUTDOWN_RESP_SIZE 3 + +/*Information requests -- OZ/OB level (ON plus these) */ + +#define GETX_ALL_1 '&' +#define GETX_ALL1_RESP_SIZE 79 + +#define GETX_OUTSOURCE 'a' +#define GETX_FRONTDISP 'b' +#define GETX_INT_TEMP 'g' /* Degrees C */ +#define GETX_BATT_STAT 'h' /* Unknown(1), Normal, Low, Depleted */ +#define GETX_BATT_CHG_PERC 'i' /* 0 - 100 */ +#define GETX_EST_MIN_REM 'j' +#define GETX_ONBATT_TIME 'k' /* In seconds */ +#define GETX_BATT_VOLTS 'l' /* Read as xxx.x */ +#define GETX_INP_FREQ 'p' +#define GETX_MIN_IN_VOLTS 'q' +#define GETX_MAX_IN_VOLTS 'r' +#define GETX_IN_VOLTS 's' +#define GETX_IN_WATTS 'u' +#define GETX_OUT_VOLTS 'v' +#define GETX_OUT_WATTS 'x' +#define GETX_OUT_LOAD_PERC 'y' +#define GETX_OUT_FREQ 'z' + +#define GETX_ALL_2 '^' +#define GETX_ALL2_RESP_SIZE 92 + +#define GETX_MODEL 'J' +#define GETX_FW_REV 'U' /* Read as xx.x */ +#define GETX_SERIAL_NUM 'Y' + +#define GETX_MAN_DATE '$' /* yymmdd */ +#define GETX_BATT_REPLACED '+' /* yymmdd */ +#define GETX_DATE_RESP_SIZE 6 + +/* FIXME: Both of the following constants are unused, and the first is not + * valid C syntax (breaks LLVM). */ +#if 0 +#define GETX_UNIT_KVA '''' /* Read as xxx.xx */ +#define GETX_UNIT_WATTS "''" /* 2-character string request */ +#endif +#define GETX_LOW_OUT_ALLOW '[' /* Tap up or inverter at this point */ +#define GETX_HI_OUT_ALLOW ']' /* Tap down or inverter at this point */ +#define GETX_NOTIFY_DELAY ',' /* Secs of delay for power fail alert */ +#define GETX_ALLOW_RESP_SIZE 3 + +#define GETX_LOW_BATT_TIME '"' /* Low batt alarm at xx minutes */ + +#define GETX_RESTART_DLY '_' /* Restart delay */ +#define GETX_RESTART_COUNT "_?" /* Returns actual counter value */ +#define GETX_RSTRT_RESP_SIZE 4 + +/*Other requests */ +#define GETX_SHUTDOWN '}' /* Shutdown counter (..... for none) */ +#define GETX_SHUTDOWN_RESP_SIZE 5 + +#define GETX_BATT_TEST_DAYS "\x02\x1A" /* Days between battery tests */ +#define GETX_BUZZER_WHAT "\x07?" /* What is buzzer state */ +#define GETX_AUTO_START " 999 on OZ */ +#define MIN_ALLOW_FW "1.9" /* At or above provides output allow range */ /*responses*/ -#define MFGR "ONEAC" -#define FAMILY_ON "ON" -#define FAMILY_ON_EXT "OZ" -#define FAMILY_EG "EG" -#define YES 'Y' -#define NO 'N' -#define NORMAL '@' -#define ON_BAT_LOW_LINE 'A' -#define ON_BAT_HI_LINE 'Q' -#define LO_BAT_LOW_LINE 'C' -#define LO_BAT_HI_LINE 'S' -#define TOO_HOT '`' -#define FIX_ME 'D' -#define BAD_BAT 'H' -#define V230AC '2' -#define V120AC '1' -#define XFER_BLACKOUT 'B' -#define XFER_LOW_VOLT 'L' -#define XFER_HI_VOLT 'H' +#define FAMILY_EG "EG" /* 3 tri-color LEDs and big ON/OFF on front */ +#define FAMILY_ON "ON" /* Serial port avail only on interface card */ +#define FAMILY_OZ "OZ" /* DB-25 std., plus interface slot */ +#define FAMILY_OB "OB" /* Lg. cab with removable modules */ +#define FAMILY_SIZE 2 +#define YES 'Y' +#define NO 'N' +#define V230AC '2' +#define V120AC '1' +#define XFER_BLACKOUT 'B' +#define XFER_LOW_VOLT 'L' +#define XFER_HI_VOLT 'H' +#define BUZZER_ENABLED '1' +#define BUZZER_DISABLED '0' +#define BUZZER_MUTED '2' /*front panel alarm codes*/ -#define CODE_BREAKER_OPEN "c1" /*input circuit breaker open*/ -#define CODE_BAT_FUSE_OPEN "c2" /*battery not connected. Open fuse?*/ -#define CODE_TOO_HOT "c3" /*UPS too hot*/ -#define CODE_CHARGING "c4" /*recharging battery pack*/ -#define CODE_LOW_BAT_CAP "c5" /*batteries getting too old*/ -#define CODE_OVERLOAD "c8" /*"slight" overload*/ -#define CODE_GROSS_OVLE "c9" /*gross overload 1 minute to power off*/ +#define CODE_BREAKER_OPEN "c1" /*input circuit breaker open*/ +#define CODE_BAT_FUSE_OPEN "c2" /*battery not connected. Open fuse?*/ +#define CODE_TOO_HOT "c3" /*UPS too hot*/ +#define CODE_CHARGING "c4" /*recharging battery pack*/ +#define CODE_LOW_BAT_CAP "c5" /*batteries getting too old*/ +#define CODE_OVERLOAD "c8" /*"slight" overload*/ +#define CODE_GROSS_OVLE "c9" /*gross overload 1 minute to power off*/ #define CODE_CHRGR_FUSE_OPEN "u1" /*battery charger fuse probably open*/ + +#endif /* NUT_ONEAC_H_SEEN */ diff --git a/drivers/openups-hid.c b/drivers/openups-hid.c new file mode 100644 index 0000000..d8be70d --- /dev/null +++ b/drivers/openups-hid.c @@ -0,0 +1,399 @@ +/* openups-hid.c - subdriver to monitor Minibox openUPS USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2012 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2012 Nicu Pavel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" /* must be first */ + +#include "usbhid-ups.h" +#include "openups-hid.h" +#include "main.h" /* for getval() */ +#include "usb-common.h" + +#define OPENUPS_HID_VERSION "openUPS HID 0.5" + +/* Minibox */ +#define OPENUPS_VENDORID 0x04d8 + +/* constants for converting HID read values to real values */ +static const double vin_scale_d004 = 0.03545 * 100; +static const double vout_scale_d004 = 0.02571 * 100; +/* static const double vbat_scale = 0.00857 * 100; */ +static const double ccharge_scale_d004 = 0.8274 / 10; +static const double cdischarge_scale_d004 = 16.113 / 10; + +static double vin_scale = 1; +static double vout_scale= 1; +static double ccharge_scale = 1; +static double cdischarge_scale = 1; + +static char openups_scratch_buf[20]; + +static void *get_voltage_multiplier(USBDevice_t *device) +{ + + switch(device->ProductID) { + case 0xd004: + vin_scale = vin_scale_d004; + vout_scale= vout_scale_d004; + ccharge_scale= ccharge_scale_d004; + cdischarge_scale= cdischarge_scale_d004; + break; + case 0xd005: + vin_scale = 0.1; + vout_scale = 0.1; + ccharge_scale = 0.1; /* unverified */ + cdischarge_scale = 0.1; /* unverified */ + break; + } + + upsdebugx(1, "vin_scale = %g; vout_scale = %g\n", vin_scale, vout_scale); + return NULL; +} + + +/* USB IDs device table */ +static /* const */ usb_device_id_t openups_usb_device_table[] = { + /* openUPS Intelligent UPS (minimum required firmware 1.4) */ + {USB_DEVICE(OPENUPS_VENDORID, 0xd004), get_voltage_multiplier}, + {USB_DEVICE(OPENUPS_VENDORID, 0xd005), get_voltage_multiplier}, + + /* Terminating entry */ + { 0, 0, NULL } +}; + +/* Thermistor table used for temperature lookups + * taken from the windows monitoring application + */ +static const unsigned int therm_tbl[] = +{ + (unsigned int)0x31, + (unsigned int)0x40, + (unsigned int)0x53, + (unsigned int)0x68, + (unsigned int)0x82, + (unsigned int)0xA0, + (unsigned int)0xC3, + (unsigned int)0xE9, + (unsigned int)0x113, + (unsigned int)0x13F, + (unsigned int)0x16E, + (unsigned int)0x19F, + (unsigned int)0x1CF, + (unsigned int)0x200, + (unsigned int)0x22F, + (unsigned int)0x25C, + (unsigned int)0x286, + (unsigned int)0x2AE, + (unsigned int)0x2D3, + (unsigned int)0x2F4, + (unsigned int)0x312, + (unsigned int)0x32D, + (unsigned int)0x345, + (unsigned int)0x35A, + (unsigned int)0x36D, + (unsigned int)0x37E, + (unsigned int)0x38C, + (unsigned int)0x399, + (unsigned int)0x3A5, + (unsigned int)0x3AF, + (unsigned int)0x3B7, + (unsigned int)0x3BF, + (unsigned int)0x3C6, + (unsigned int)0x3CC +}; + +static const unsigned int therm_tbl_size = sizeof(therm_tbl)/sizeof(therm_tbl[0]); + +static const char *openups_charging_fun(double value); +static const char *openups_discharging_fun(double value); +static const char *openups_online_fun(double value); +static const char *openups_nobattery_fun(double value); +static const char *openups_off_fun(double value); + +static const char *openups_scale_vin_fun(double value); +static const char *openups_scale_vout_fun(double value); +/* static const char *openups_scale_vbat_fun(double value); */ +static const char *openups_scale_ccharge_fun(double value); +static const char *openups_scale_cdischarge_fun(double value); +static const char *openups_temperature_fun(double value); + +static info_lkp_t openups_charging_info[] = { + {0, NULL, openups_charging_fun, NULL } +}; + +static info_lkp_t openups_discharging_info[] = { + {0, NULL, openups_discharging_fun, NULL } +}; + +static info_lkp_t openups_online_info[] = { + {0, NULL, openups_online_fun, NULL } +}; + +static info_lkp_t openups_nobattery_info[] = { + {0, NULL, openups_nobattery_fun, NULL } +}; + +static info_lkp_t openups_off_info[] = { + {0, NULL, openups_off_fun, NULL } +}; + +static info_lkp_t openups_vin_info[] = { + {0, NULL, openups_scale_vin_fun, NULL } +}; + +static info_lkp_t openups_vout_info[] = { + {0, NULL, openups_scale_vout_fun, NULL } +}; + +/* static info_lkp_t openups_vbat_info[] = { + {0, NULL, openups_scale_vbat_fun, NULL } +};*/ + +static info_lkp_t openups_ccharge_info[] = { + {0, NULL, openups_scale_ccharge_fun, NULL } +}; + +static info_lkp_t openups_cdischarge_info[] = { + {0, NULL, openups_scale_cdischarge_fun, NULL } +}; + +static info_lkp_t openups_temperature_info[] = { + {0, NULL, openups_temperature_fun, NULL } +}; + +static const char *openups_charging_fun(double value) +{ + return value ? "chrg" : "!chrg"; +} + +static const char *openups_discharging_fun(double value) +{ + return value ? "dischrg" : "!dischrg"; +} + +static const char *openups_online_fun(double value) +{ + return value ? "online" : "!online"; +} + +static const char *openups_nobattery_fun(double value) +{ + return value ? "nobattery" : "!nobattery"; +} + +static const char *openups_off_fun(double value) +{ + return value ? "!off" : "off"; +} + +static const char *openups_scale_vin_fun(double value) +{ + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", value * vin_scale); + return openups_scratch_buf; +} + +static const char *openups_scale_vout_fun(double value) +{ + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", value * vout_scale); + return openups_scratch_buf; +} + +/* static const char *openups_scale_vbat_fun(double value) +{ + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", value * vbat_scale); + return openups_scratch_buf; +}*/ + +static const char *openups_scale_ccharge_fun(double value) +{ + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.3f", value * ccharge_scale); + return openups_scratch_buf; +} + +static const char *openups_scale_cdischarge_fun(double value) +{ + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.3f", value * cdischarge_scale); + return openups_scratch_buf; +} + +static const char *openups_temperature_fun(double value) +{ + int i; + int pos = 0; + unsigned int thermistor = value * 100; + + if (thermistor <= therm_tbl[0]) { + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%d", -40); + } else { + if (thermistor >= therm_tbl[therm_tbl_size - 1]) { + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%d", 125); + } else { + for (i = therm_tbl_size - 1; i >= 0; i--) { + if (thermistor >= therm_tbl[i]) { + pos = i; + break; + } + } + + if (thermistor == therm_tbl[pos]) { + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%d", pos * 5 - 40); + } else { + int t1 = pos * 5 - 40; + int t2 = (pos + 1) * 5 - 40; + + unsigned int d1 = therm_tbl[pos]; + unsigned int d2 = therm_tbl[pos + 1]; + + double temp = (double) (thermistor - d1) * (t2 - t1) / (d2 - d1) + t1; + snprintf(openups_scratch_buf, sizeof(openups_scratch_buf), "%.2f", temp); + } + } + } + + return openups_scratch_buf; +} + +/* --------------------------------------------------------------- */ +/* Vendor-specific usage table */ +/* --------------------------------------------------------------- */ + +/* OPENUPS usage table */ +static usage_lkp_t openups_usage_lkp[] = { + {"Cell1", 0x00000001}, /* Battery cell 1 on J6 pin 1 */ + {"Cell2", 0x00000002}, /* Battery cell 2 on J6 pin 2 */ + {"Cell3", 0x00000003}, /* Battery cell 3 on J6 pin 3 */ + {"Cell4", 0x00000004}, /* Battery cell 4 on J6 pin 4 */ + {"Cell5", 0x00000005}, /* Battery cell 5 on J6 pin 5 */ + {"Cell6", 0x00000006}, /* Battery cell 6 on J4 pin 1 */ + /* Usage table for windows monitoring app only updates when + * certain request codes are written to USB endpoint */ + /*{ "OpenUPSExtra", 0xff000001 }, */ + {NULL, 0} +}; + +static usage_tables_t openups_utab[] = { + openups_usage_lkp, + hid_usage_lkp, + NULL, +}; + +/* --------------------------------------------------------------- */ +/* HID2NUT lookup table */ +/* --------------------------------------------------------------- */ + +static hid_info_t openups_hid2nut[] = { + {"ups.serial", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%s", 0, stringid_conversion}, + + /* Battery */ + {"battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", HU_FLAG_STATIC, stringid_conversion}, + {"battery.mfr.date", 0, 0, "UPS.PowerSummary.iOEMInformation", NULL, "%s", HU_FLAG_STATIC, stringid_conversion}, + {"battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.2f", HU_FLAG_QUICK_POLL, NULL}, + /* { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info }, */ + {"battery.current", 0, 0, "UPS.PowerSummary.Current", NULL, "%.3f", HU_FLAG_QUICK_POLL, NULL}, + {"battery.capacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", HU_FLAG_STATIC, NULL}, + {"battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, + {"battery.charge.low", 0, 0, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, + {"battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", 0, NULL}, + {"battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, + {"battery.temperature", 0, 0, "UPS.PowerSummary.Temperature", NULL, NULL, HU_FLAG_QUICK_POLL, openups_temperature_info}, +/* {"battery.cell1.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell1", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, + {"battery.cell2.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell2", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, + {"battery.cell3.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell3", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, + {"battery.cell4.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell4", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, + {"battery.cell5.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell5", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, + {"battery.cell6.voltage", 0, 0, "UPS.PowerSummary.Battery.Cell6", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vbat_info}, +*/ + /* Output */ + {"output.voltage", 0, 0, "UPS.PowerSummary.Output.Voltage", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vout_info}, + {"output.current", 0, 0, "UPS.PowerSummary.Output.Current", NULL, NULL, HU_FLAG_QUICK_POLL, openups_cdischarge_info}, + + /* Input */ + {"input.voltage", 0, 0, "UPS.PowerSummary.Input.Voltage", NULL, NULL, HU_FLAG_QUICK_POLL, openups_vin_info}, + {"input.current", 0, 0, "UPS.PowerSummary.Input.Current", NULL, NULL, HU_FLAG_QUICK_POLL, openups_ccharge_info}, + + /* Status */ + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Good", NULL, NULL, HU_FLAG_QUICK_POLL, openups_off_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.InternalFailure", NULL, NULL, HU_FLAG_QUICK_POLL, commfault_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, HU_FLAG_QUICK_POLL, overload_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.OverTemperature", NULL, NULL, HU_FLAG_QUICK_POLL, overheat_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, HU_FLAG_QUICK_POLL, shutdownimm_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, HU_FLAG_QUICK_POLL, timelimitexpired_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, openups_charging_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, openups_discharging_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, openups_online_info}, + {"BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BatteryPresent", NULL, NULL, HU_FLAG_QUICK_POLL, openups_nobattery_info}, + + /* end of structure. */ + {NULL, 0, 0, NULL, NULL, NULL, 0, NULL} +}; + +static const char *openups_format_model(HIDDevice_t * hd) +{ + return hd->Product; +} + +static const char *openups_format_mfr(HIDDevice_t * hd) +{ + return hd->Vendor ? hd->Vendor : "openUPS"; +} + +static const char *openups_format_serial(HIDDevice_t * hd) +{ + return hd->Serial; +} + +/* this function allows the subdriver to "claim" a device: return 1 if + * the device is supported by this subdriver, else 0. */ +static int openups_claim(HIDDevice_t * hd) +{ + int status = is_usb_device_supported(openups_usb_device_table, hd); + + switch (status) { + case POSSIBLY_SUPPORTED: + /* by default, reject, unless the productid option is given */ + if (getval("productid")) { + return 1; + } + possibly_supported("openUPS", hd); + return 0; + + case SUPPORTED: + return 1; + + case NOT_SUPPORTED: + default: + return 0; + } +} + +subdriver_t openups_subdriver = { + OPENUPS_HID_VERSION, + openups_claim, + openups_utab, + openups_hid2nut, + openups_format_model, + openups_format_mfr, + openups_format_serial, + fix_report_desc, +}; diff --git a/drivers/openups-hid.h b/drivers/openups-hid.h new file mode 100644 index 0000000..ee8e06e --- /dev/null +++ b/drivers/openups-hid.h @@ -0,0 +1,33 @@ +/* openups-hid.h - subdriver to monitor Minibox openUPS USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2009 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2012 Nicu Pavel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef OPENUPS_HID_H +#define OPENUPS_HID_H + +#include "usbhid-ups.h" + +/* Don't put non-extern definitions here - this file gets included by usbhid-ups.c */ + +extern subdriver_t openups_subdriver; + +#endif /* OPENUPS_HID_H */ diff --git a/drivers/optiups.c b/drivers/optiups.c index 26b7f47..13eedd5 100644 --- a/drivers/optiups.c +++ b/drivers/optiups.c @@ -27,7 +27,7 @@ #include "serial.h" #define DRIVER_NAME "Opti-UPS driver" -#define DRIVER_VERSION "1.01" +#define DRIVER_VERSION "1.02" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -82,34 +82,36 @@ static char _buf[256]; static int optimodel = 0; enum { OPTIMODEL_DEFAULT = 0, - OPTIMODEL_ZINTO =1 + OPTIMODEL_ZINTO = 1 }; /* Status bits returned by the "AG" command */ enum { - OPTISBIT_NOOUTPUT = 2, - OPTISBIT_OVERLOAD = 8, - OPTISBIT_REPLACE_BATTERY = 16, - OPTISBIT_ON_BATTERY_POWER = 32, - OPTISBIT_LOW_BATTERY = 64 + OPTISBIT_NOOUTPUT = 2L, + OPTISBIT_OVERLOAD = 8L, + OPTISBIT_REPLACE_BATTERY = 16L, + OPTISBIT_ON_BATTERY_POWER = 32L, + OPTISBIT_LOW_BATTERY = 64L }; /* Helper struct for the optifill() function */ typedef struct ezfill_s { const char *cmd; const char *var; - const float scale; + const float scale; /* if 0, no conversion is done and the string + is passed to dstate as is, otherwise a float + conversion with single decimal is applied */ } ezfill_t; /* These can be polled right into a string usable by NUT. * Others such as "AG" and "BV" require some transformation of the return value */ static ezfill_t _pollv[] = { - { "NV", "input.voltage" }, + { "NV", "input.voltage", 0 }, { "OL", "ups.load", 1.0 }, - { "OV", "output.voltage" }, + { "OV", "output.voltage", 0 }, { "FF", "input.frequency", 0.1 }, - { "BT", "ups.temperature" }, + { "BT", "ups.temperature", 0 }, }; static ezfill_t _pollv_zinto[] = { { "NV", "input.voltage", 2.0 }, @@ -117,22 +119,22 @@ static ezfill_t _pollv_zinto[] = { { "OV", "output.voltage", 2.0 }, { "OF", "output.frequency", 0.1 }, { "NF", "input.frequency", 0.1 }, - { "BT", "ups.temperature" }, + { "BT", "ups.temperature", 0 }, }; /* model "IO" is parsed differently in upsdrv_initinfo() */ static ezfill_t _initv[] = { - { "IM", "ups.mfr" }, - { "IZ", "ups.serial" }, - { "IS", "ups.firmware" }, + { "IM", "ups.mfr", 0 }, + { "IZ", "ups.serial", 0 }, + { "IS", "ups.firmware", 0 }, }; /* All serial reads of the OPTI-UPS go through here. We always expect a CR/LF terminated * response. Unknown/Unimplemented commands return ^U (0x15). Actions that complete * successfully return ^F (0x06). */ -static inline int optireadline(void) +static inline ssize_t optireadline(void) { - int r; + ssize_t r; usleep(150000); r = ser_get_line(upsfd, _buf, sizeof(_buf), ENDCHAR, IGNCHARS, 0, 500000 ); _buf[sizeof(_buf)-1] = 0; @@ -155,7 +157,7 @@ static inline int optireadline(void) } } else - upsdebugx(1, "READ ERROR: %d", r ); + upsdebugx(1, "READ ERROR: %zd", r ); return r; } @@ -165,7 +167,7 @@ static inline int optireadline(void) * -1 serial timeout * -2 unknown/unimplemented command */ -static inline int optiquery( const char *cmd ) +static inline ssize_t optiquery( const char *cmd ) { upsdebugx(2, "SEND: \"%s\"", cmd ); ser_send( upsfd, "%s", cmd ); @@ -175,9 +177,10 @@ static inline int optiquery( const char *cmd ) } /* Uses the ezfill_t structure to map UPS commands to the NUT variable destinations */ -static void optifill( ezfill_t *a, int len ) +static void optifill( ezfill_t *a, size_t len ) { - int i, r; + size_t i; + ssize_t r; /* Some things are easy to poll and store */ for ( i=0; i * OV - output stage voltage <118> * OL - output stage load <027> - * + * * FV - Input voltage <120> * FF - Input Frequency (0.1Hz) <600> * FO - Output volts <120> @@ -590,7 +594,7 @@ void upsdrv_cleanup(void) * bit 3: 1 = overload * bit 4: 1 = replace battery * bit 5: 1 = on battery, 0 = on line - * bit 6: 1 = low battery + * bit 6: 1 = low battery * TR - Test results <00> * 00 = Unknown * 01 = Passed @@ -598,7 +602,7 @@ void upsdrv_cleanup(void) * 03 = Error * 04 = Aborted * 05 = In Progress - * 06 = No test init + * 06 = No test init * ******************************************* * ACTIONS diff --git a/drivers/phoenixcontact_modbus.c b/drivers/phoenixcontact_modbus.c new file mode 100644 index 0000000..fb4b24e --- /dev/null +++ b/drivers/phoenixcontact_modbus.c @@ -0,0 +1,192 @@ +/* phoenixcontact_modbus.c - Driver for PhoenixContact-QUINT UPS + * + * Copyright (C) + * 2017 Spiros Ioannou + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include + +#define DRIVER_NAME "NUT PhoenixContact Modbus driver" +#define DRIVER_VERSION "0.02" + +#define CHECK_BIT(var,pos) ((var) & (1<<(pos))) +#define MODBUS_SLAVE_ID 192 + +/* Variables */ +static modbus_t *modbus_ctx = NULL; +static int errcount = 0; + +static int mrir(modbus_t * arg_ctx, int addr, int nb, uint16_t * dest); + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Spiros Ioannou \n", + DRV_BETA, + {NULL} +}; + +void upsdrv_initinfo(void) +{ + upsdebugx(2, "upsdrv_initinfo"); + + dstate_setinfo("device.mfr", "Phoenix Contact"); + dstate_setinfo("device.model", "QUINT-UPS/24DC"); + + /* upsh.instcmd = instcmd; */ + /* upsh.setvar = setvar; */ +} + +void upsdrv_updateinfo(void) +{ + errcount = 0; + + upsdebugx(2, "upsdrv_updateinfo"); + + uint16_t tab_reg[64]; + + mrir(modbus_ctx, 29697, 3, tab_reg); + + status_init(); + + if (tab_reg[0]) + status_set("OB"); + else + status_set("OL"); + + if (tab_reg[2]) { + status_set("CHRG"); + } + + if (tab_reg[1]) { + status_set("LB"); /* LB is actually called "shutdown event" on this ups */ + } + + mrir(modbus_ctx, 29745, 1, tab_reg); + dstate_setinfo("output.voltage", "%d", (int) (tab_reg[0] / 1000)); + + mrir(modbus_ctx, 29749, 5, tab_reg); + dstate_setinfo("battery.charge", "%d", tab_reg[0]); + /* dstate_setinfo("battery.runtime",tab_reg[1]*60); */ /* also reported on this address, but less accurately */ + + mrir(modbus_ctx, 29792, 10, tab_reg); + dstate_setinfo("battery.voltage", "%f", (double) (tab_reg[0]) / 1000.0); + dstate_setinfo("battery.temperature", "%d", tab_reg[1] - 273); + dstate_setinfo("battery.runtime", "%d", tab_reg[3]); + dstate_setinfo("battery.capacity", "%d", tab_reg[8] * 10); + dstate_setinfo("output.current", "%f", (double) (tab_reg[6]) / 1000.0); + + /* ALARMS */ + mrir(modbus_ctx, 29840, 1, tab_reg); + alarm_init(); + if (CHECK_BIT(tab_reg[0], 4) && CHECK_BIT(tab_reg[0], 5)) + alarm_set("End of life (Resistance)"); + if (CHECK_BIT(tab_reg[0], 6)) + alarm_set("End of life (Time)"); + if (CHECK_BIT(tab_reg[0], 7)) + alarm_set("End of life (Voltage)"); + if (CHECK_BIT(tab_reg[0], 9)) + alarm_set("No Battery"); + if (CHECK_BIT(tab_reg[0], 10)) + alarm_set("Inconsistent technology"); + if (CHECK_BIT(tab_reg[0], 11)) + alarm_set("Overload Cutoff"); + /* We don't use those low-battery indicators below. + * No info or configuration exists for those alarm low-bat + */ + if (CHECK_BIT(tab_reg[0], 12)) + alarm_set("Low Battery (Voltage)"); + if (CHECK_BIT(tab_reg[0], 13)) + alarm_set("Low Battery (Charge)"); + if (CHECK_BIT(tab_reg[0], 14)) + alarm_set("Low Battery (Time)"); + if (CHECK_BIT(tab_reg[0], 16)) + alarm_set("Low Battery (Service)"); + + if (errcount == 0) { + alarm_commit(); + status_commit(); + dstate_dataok(); + } + else + dstate_datastale(); + +} + +void upsdrv_shutdown(void) + __attribute__((noreturn)); + +void upsdrv_shutdown(void) +{ + fatalx(EXIT_FAILURE, "shutdown not supported"); +} + +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ +} + +void upsdrv_initups(void) +{ + int r; + upsdebugx(2, "upsdrv_initups"); + + modbus_ctx = modbus_new_rtu(device_path, 115200, 'E', 8, 1); + if (modbus_ctx == NULL) + fatalx(EXIT_FAILURE, "Unable to create the libmodbus context"); + + r = modbus_set_slave(modbus_ctx, MODBUS_SLAVE_ID); /* slave ID */ + if (r < 0) { + modbus_free(modbus_ctx); + fatalx(EXIT_FAILURE, "Invalid modbus slave ID %d",MODBUS_SLAVE_ID); + } + + if (modbus_connect(modbus_ctx) == -1) { + modbus_free(modbus_ctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + +} + + +void upsdrv_cleanup(void) +{ + if (modbus_ctx != NULL) { + modbus_close(modbus_ctx); + modbus_free(modbus_ctx); + } +} + +/* Modbus Read Input Registers */ +static int mrir(modbus_t * arg_ctx, int addr, int nb, uint16_t * dest) +{ + int r; + r = modbus_read_input_registers(arg_ctx, addr, nb, dest); + if (r == -1) { + upslogx(LOG_ERR, "mrir: modbus_read_input_registers(addr:%d, count:%d): %s (%s)", addr, nb, modbus_strerror(errno), device_path); + errcount++; + } + return r; +} diff --git a/drivers/pijuice.c b/drivers/pijuice.c new file mode 100644 index 0000000..a1b1db1 --- /dev/null +++ b/drivers/pijuice.c @@ -0,0 +1,853 @@ +/* pijuice.c Driver for the PiJuice HAT (www.pijuice.com), addressed via i2c. + + Copyright (C) 2019 Andrew Anderson + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "main.h" + +#include +#include + +/* + * Linux I2C userland is a bit of a mess until distros refresh to + * the i2c-tools 4.x release that profides i2c/smbus.h for userspace + * instead of (re)using linux/i2c-dev.h, which conflicts with a + * kernel header of the same name. + * + * See: + * https://i2c.wiki.kernel.org/index.php/Plans_for_I2C_Tools_4 + */ +#if HAVE_LINUX_SMBUS_H +# include +#endif +#if HAVE_LINUX_I2C_DEV_H +# include /* for I2C_SLAVE */ +# if !HAVE_LINUX_SMBUS_H +# ifndef I2C_FUNC_I2C +# include +# endif +# endif +#endif + +/* + * i2c-tools pre-4.0 has a userspace header with a name that conflicts + * with a kernel header, so it may be ignored/removed by distributions + * when packaging i2c-tools. + * + * This will cause the driver to be un-buildable on certain + * configurations, so include the necessary bits here to handle this + * situation. + */ +#if WITH_LINUX_I2C +#if !HAVE_DECL_I2C_SMBUS_ACCESS +static inline __s32 i2c_smbus_access(int file, char read_write, __u8 command, + int size, union i2c_smbus_data *data) +{ + struct i2c_smbus_ioctl_data args; + __s32 err; + + args.read_write = read_write; + args.command = command; + args.size = size; + args.data = data; + + err = ioctl(file, I2C_SMBUS, &args); + if (err == -1) + err = -errno; + return err; +} +#endif + +#if !HAVE_DECL_I2C_SMBUS_READ_BYTE_DATA +static inline __s32 i2c_smbus_read_byte_data(int file, __u8 command) +{ + union i2c_smbus_data data; + int err; + + if ((err = i2c_smbus_access(file, I2C_SMBUS_READ, command, + I2C_SMBUS_BYTE_DATA, &data)) < 0) + return err; + else + return 0x0FF & data.byte; +} +#endif + +#if !HAVE_DECL_I2C_SMBUS_WRITE_BYTE_DATA +static inline __s32 i2c_smbus_write_byte_data(int file, __u8 command, __u8 value) +{ + union i2c_smbus_data data; + int err; + + data.byte = value; + if ((err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command, + I2C_SMBUS_BYTE_DATA, &data)) < 0) + return err; + else + return 0x0FF & data.byte; +} +#endif + +#if !HAVE_DECL_I2C_SMBUS_READ_WORD_DATA +static inline __s32 i2c_smbus_read_word_data(int file, __u8 command) +{ + union i2c_smbus_data data; + int err; + + if ((err = i2c_smbus_access(file, I2C_SMBUS_READ, command, + I2C_SMBUS_WORD_DATA, &data)) < 0) + return err; + else + return 0x0FFFF & data.word; +} +#endif + +#if !HAVE_DECL_I2C_SMBUS_WRITE_WORD_DATA +static inline __s32 i2c_smbus_write_word_data(int file, __u8 command, __u16 value) +{ + union i2c_smbus_data data; + int err; + + data.word = value; + if ((err = i2c_smbus_access(file, I2C_SMBUS_WRITE, command, + I2C_SMBUS_WORD_DATA, &data)) < 0) + return err; + else + return 0x0FFFF & data.word; +} +#endif + +#if !HAVE_DECL_I2C_SMBUS_READ_BLOCK_DATA +static inline __u8* i2c_smbus_read_i2c_block_data(int file, __u8 command, __u8 length, __u8 *values) +{ + union i2c_smbus_data data; + int err; + + if ( length > I2C_SMBUS_BLOCK_MAX) + { + length = I2C_SMBUS_BLOCK_MAX; + } + + data.block[0] = length; + memcpy(data.block + 1, values, length); + + if ((err = i2c_smbus_access(file, I2C_SMBUS_READ, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data)) < 0) + return NULL; + else + memcpy(values, &data.block[1], data.block[0]); + + return values; +} +#endif +#endif // if WITH_LINUX_I2C + +#define STATUS_CMD 0x40 +#define CHARGE_LEVEL_CMD 0x41 +#define CHARGE_LEVEL_HI_RES_CMD 0x42 +#define FAULT_EVENT_CMD 0x44 +#define BUTTON_EVENT_CMD 0x45 +#define BATTERY_TEMPERATURE_CMD 0x47 +#define BATTERY_VOLTAGE_CMD 0x49 +#define BATTERY_CURRENT_CMD 0x4b +#define IO_VOLTAGE_CMD 0x4d +#define IO_CURRENT_CMD 0x4f + +#define CHARGING_CONFIG_CMD 0x51 +#define BATTERY_PROFILE_ID_CMD 0x52 +#define BATTERY_PROFILE_CMD 0x53 +#define BATTERY_EXT_PROFILE_CMD 0x54 +#define BATTERY_TEMP_SENSE_CONFIG_CMD 0x5D + +#define POWER_INPUTS_CONFIG_CMD 0x5E +#define RUN_PIN_CONFIG_CMD 0x5F +#define POWER_REGULATOR_CONFIG_CMD 0x60 +#define WATCHDOG_ACTIVATION_CMD 0x61 +#define POWER_OFF_CMD 0x62 +#define WAKEUP_ON_CHARGE_CMD 0x63 +#define SYSTEM_POWER_SWITCH_CTRL_CMD 0x64 + +#define LED_STATE_CMD 0x66 +#define LED_BLINK_CMD 0x68 +#define LED_CONFIGURATION_CMD 0x6A +#define BUTTON_CONFIGURATION_CMD 0x6E + +#define IO1_CONFIGURATION_CMD 0x72 +#define IO1_PIN_ACCESS_CMD 0x75 + +#define IO2_CONFIGURATION_CMD 0x77 +#define IO2_PIN_ACCESS_CMD 0x7A + +#define I2C_ADDRESS_CMD 0x7C + +#define ID_EEPROM_WRITE_PROTECT_CTRL_CMD 0x7E +#define ID_EEPROM_ADDRESS_CMD 0x7F + +#define RTC_TIME_CMD 0xB0 +#define RTC_ALARM_CMD 0xB9 +#define RTC_CTRL_STATUS_CMD 0xC2 + +#define RESET_TO_DEFAULT_CMD 0xF0 +#define FIRMWARE_VERSION_CMD 0xFD + +#define BATT_NORMAL 0 +#define BATT_CHARGING_FROM_IN 1 +#define BATT_CHARGING_FROM_5V 2 +#define BATT_NOT_PRESENT 3 + +#define POWER_NOT_PRESENT 0 +#define POWER_BAD 1 +#define POWER_WEAK 2 +#define POWER_PRESENT 3 + +#define LOW_BATTERY_THRESHOLD 25.0 +#define HIGH_BATTERY_THRESHOLD 75.0 +#define NOMINAL_BATTERY_VOLTAGE 4.18 + +#define DRIVER_NAME "PiJuice UPS driver" +#define DRIVER_VERSION "0.10" + +static uint8_t i2c_address = 0x14; +static uint8_t shutdown_delay = 30; + +/* + * Flags used to indicate a change in power status + */ +static uint8_t usb_power = 0; +static uint8_t gpio_power = 0; +static uint8_t battery_power = 0; + +/* + * Smooth out i2c read errors by holding the most recent + * battery charge level reading + */ +static float battery_charge_level = 0; + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Andrew Anderson ", + DRV_EXPERIMENTAL, + { NULL } +}; + +/* The macros below all write into a "data" variable defined by the routine + * scope which calls them, with respective type of uint8_t for "byte" and + * uint16_t for "word" macros. Native i2c functions operate with __s32 type + * (currently, signed 32-bit ints?) with negative values for error returns. + * Note: some manpages refer to "s32" while headers on my and CI systems use + * a "__s32" type. Maybe this is something to determine in configure script? + * Code below was fixed to convert the valid values and avoid compiler + * warnings about comparing whether unsigned ints happened to be negative. + */ +#define I2C_READ_BYTE(fd, cmd, label) \ + { \ + __s32 sData; \ + if ((sData = i2c_smbus_read_byte_data(upsfd, cmd)) < 0 ) { \ + upsdebugx(2, "Failure reading the i2c bus [%s]", label); \ + return; \ + } ; \ + data = (uint8_t) sData; \ + } + +#define I2C_WRITE_BYTE(fd, cmd, value, label) \ + { \ + if ( i2c_smbus_write_byte_data(upsfd, cmd, value) < 0 ) { \ + upsdebugx(2, "Failure writing to the i2c bus [%s]", label); \ + return; \ + } ; \ + } + +#define I2C_READ_WORD(fd, cmd, label) \ + { \ + __s32 sData; \ + if ((sData = i2c_smbus_read_word_data(upsfd, cmd)) < 0 ) { \ + upsdebugx(2, "Failure reading the i2c bus [%s]", label); \ + return; \ + } ; \ + data = (uint16_t) sData; \ + } + +#define I2C_READ_BLOCK(fd, cmd, size, block, label) \ + if ((i2c_smbus_read_i2c_block_data(upsfd, cmd, size, block)) < 0 ) { \ + upsdebugx(2, "Failure reading the i2c bus [%s]", label); \ + return; \ + } + +static inline int open_i2c_bus(char *path, uint8_t addr) +{ + int file; + + if ((file = open(path, O_RDWR)) < 0) + { + fatal_with_errno(EXIT_FAILURE, "Failed to open the i2c bus on %s", path); + } + + if (ioctl(file, I2C_SLAVE, addr) < 0) + { + fatal_with_errno(EXIT_FAILURE, "Failed to acquire the i2c bus and/or talk to the UPS"); + } + + return file; +} + +static void get_charge_level_hi_res() +{ + uint8_t cmd = CHARGE_LEVEL_HI_RES_CMD; + uint16_t data; + + upsdebugx( 3, __func__ ); + + I2C_READ_WORD( upsfd, cmd, __func__ ) + + /* + * Use an external variable to allow for missed i2c bus + * reads; the charge level data may be slightly stale, + * but no other options seem reasonable: + * + * 1) store 0 + * Leads to a false report of a depleted battery, possibly + * triggering an immediate shutdown if on battery power only + * 2) store -1 + * Adds a lot of logic to "skip" over negative charge levels, + * which effectively accomplishes the same thing + * 3) retry the read immediately + * Could tie up the i2c bus and make matters exponentially worse + */ + battery_charge_level = data / 10.0; + + upsdebugx( 1, "Battery Charge Level: %02.1f%%", battery_charge_level ); + dstate_setinfo( "battery.charge", "%02.1f", battery_charge_level ); +} + +static void get_status() +{ + uint8_t cmd = STATUS_CMD; + uint8_t data; + char status_buf[ST_MAX_VALUE_LEN]; + + upsdebugx( 3, __func__ ); + + memset( status_buf, 0, ST_MAX_VALUE_LEN ); + + I2C_READ_BYTE( upsfd, cmd, __func__ ) + + uint8_t batteryStatus = data >> 2 & 0x03; + switch( batteryStatus ) + { + case BATT_NORMAL: + upsdebugx( 1, "Battery Status: Normal" ); + dstate_setinfo( "battery.packs", "%d", 1 ); + dstate_setinfo( "battery.packs.bad", "%d", 0 ); + break; + case BATT_CHARGING_FROM_IN: + upsdebugx( 1, "Battery Status: Charging from IN" ); + dstate_setinfo( "battery.packs", "%d", 1 ); + dstate_setinfo( "battery.packs.bad", "%d", 0 ); + break; + case BATT_CHARGING_FROM_5V: + upsdebugx( 1, "Battery Status: Charging from 5V" ); + dstate_setinfo( "battery.packs", "%d", 1 ); + dstate_setinfo( "battery.packs.bad", "%d", 0 ); + break; + case BATT_NOT_PRESENT: + upsdebugx( 1, "Battery Status: Not Present" ); + dstate_setinfo( "battery.packs", "%d", 0 ); + dstate_setinfo( "battery.packs.bad", "%d", 1 ); + break; + default: + upsdebugx( 1, "battery.status: UNKNOWN" ); + } + + uint8_t powerInput = data >> 4 & 0x03; + switch( powerInput ) + { + case POWER_NOT_PRESENT: + upsdebugx( 1, "Power Input: Not Present" ); + break; + case POWER_BAD: + upsdebugx( 1, "Power Input: Bad" ); + break; + case POWER_WEAK: + upsdebugx( 1, "Power Input: Weak" ); + break; + case POWER_PRESENT: + upsdebugx( 1, "Power Input: Present" ); + break; + default: + upsdebugx( 1, "Power Input: UNKNOWN" ); + } + + uint8_t powerInput5vIo = data >> 6 & 0x03; + switch( powerInput5vIo ) + { + case POWER_NOT_PRESENT : + upsdebugx(1, "Power Input 5v: Not Present"); + break; + case POWER_BAD: + upsdebugx(1, "Power Input 5v: Bad"); + break; + case POWER_WEAK: + upsdebugx(1, "Power Input 5v: Weak"); + break; + case POWER_PRESENT: + upsdebugx(1, "Power Input 5v: Present"); + break; + default: + upsdebugx(1, "Power Input 5v: UNKNOWN"); + } + + if ( batteryStatus == BATT_NORMAL || + batteryStatus == BATT_CHARGING_FROM_IN || + batteryStatus == BATT_CHARGING_FROM_5V ) + { + get_charge_level_hi_res(); + + if ( battery_charge_level <= LOW_BATTERY_THRESHOLD ) + { + upsdebugx( 1, "Battery Charge Status: LOW" ); + snprintfcat( status_buf, ST_MAX_VALUE_LEN, "LB " ); + } + else if ( battery_charge_level > HIGH_BATTERY_THRESHOLD ) + { + upsdebugx( 1, "Battery Charge Status: HIGH" ); + snprintfcat( status_buf, ST_MAX_VALUE_LEN, "HB " ); + } + } + else if ( batteryStatus == BATT_NOT_PRESENT ) + { + snprintfcat( status_buf, ST_MAX_VALUE_LEN, "RB " ); + } + + if ( batteryStatus <= BATT_NOT_PRESENT && + powerInput <= POWER_PRESENT && + powerInput5vIo <= POWER_PRESENT ) + { + if ( powerInput == POWER_NOT_PRESENT && + ( powerInput5vIo != POWER_NOT_PRESENT )) + { + if ( usb_power != 1 || gpio_power != 0 ) + { + upslogx( LOG_NOTICE, "On USB power" ); + } + usb_power = 1; + gpio_power = 0; + battery_power = 0; + upsdebugx( 1, "On USB power [%d:%d:%d]", usb_power, gpio_power, battery_power ); + + snprintfcat( status_buf, sizeof(status_buf), "OL" ); + if ( batteryStatus == BATT_CHARGING_FROM_5V ) + { + snprintfcat( status_buf, sizeof( status_buf ), " CHRG" ); + upsdebugx( 1, "Battery Charger Status: charging" ); + dstate_setinfo( "battery.charger.status", "%s", "charging" ); + } + else if ( batteryStatus == BATT_NORMAL ) + { + upsdebugx( 1, "Battery Charger Status: resting" ); + dstate_setinfo( "battery.charger.status", "%s", "resting" ); + } + status_set( status_buf ); + } + else if ( powerInput5vIo == POWER_NOT_PRESENT && + ( powerInput != POWER_NOT_PRESENT && + powerInput <= POWER_PRESENT )) + { + if ( gpio_power != 1 || usb_power != 0 ) + { + upslogx( LOG_NOTICE, "On 5V_GPIO power" ); + } + usb_power = 0; + gpio_power = 1; + battery_power = 0; + upsdebugx( 1, "On 5V_GPIO power [%d:%d:%d]", usb_power, gpio_power, battery_power ); + + snprintfcat( status_buf, sizeof(status_buf), "OL" ); + if ( batteryStatus == BATT_CHARGING_FROM_IN ) + { + snprintfcat( status_buf, sizeof(status_buf), " CHRG" ); + status_set( status_buf ); + upsdebugx( 1, "Battery Charger Status: charging" ); + dstate_setinfo( "battery.charger.status", "%s", "charging" ); + } + else if ( batteryStatus == BATT_NORMAL ) + { + status_set( status_buf ); + upsdebugx( 1, "Battery Charger Status: resting" ); + dstate_setinfo( "battery.charger.status", "%s", "resting" ); + } + } + else if ( ( powerInput != POWER_NOT_PRESENT && powerInput <= POWER_PRESENT ) && + ( powerInput5vIo != POWER_NOT_PRESENT && powerInput5vIo <= POWER_PRESENT )) + { + if ( usb_power != 1 || gpio_power != 1 ) + { + upslogx( LOG_NOTICE, "On USB and 5V_GPIO power" ); + } + usb_power = 1; + gpio_power = 1; + battery_power = 0; + upsdebugx( 1, "On USB and 5V_GPIO power [%d:%d:%d]", usb_power, gpio_power, battery_power ); + + snprintfcat( status_buf, sizeof( status_buf ), "OL" ); + if ( batteryStatus == BATT_CHARGING_FROM_IN ) + { + snprintfcat( status_buf, sizeof(status_buf), " CHRG"); + status_set( status_buf ); + upsdebugx( 1, "Battery Charger Status: charging" ); + dstate_setinfo("battery.charger.status", "%s", "charging"); + } + else if ( batteryStatus == BATT_NORMAL ) + { + status_set( status_buf ); + upsdebugx( 1, "Battery Charger Status: resting" ); + dstate_setinfo( "battery.charger.status", "%s", "resting" ); + } + } + else if ( powerInput == POWER_NOT_PRESENT && powerInput5vIo == POWER_NOT_PRESENT ) + { + if ( usb_power != 0 || gpio_power != 0 ) + { + upslogx( LOG_NOTICE, "On Battery power" ); + } + usb_power = 0; + gpio_power = 0; + battery_power = 1; + upsdebugx( 1, "On Battery power [%d:%d:%d]", usb_power, gpio_power, battery_power ); + + snprintfcat( status_buf, sizeof(status_buf), "OB DISCHRG" ); + status_set( status_buf ); + } + } +} + +static void get_battery_temperature() +{ + uint8_t cmd = BATTERY_TEMPERATURE_CMD; + int16_t data; + + upsdebugx( 3, __func__ ); + + I2C_READ_WORD( upsfd, cmd, __func__ ) + + upsdebugx( 1, "Battery Temperature: %d°C", data ); + dstate_setinfo( "battery.temperature", "%d", data ); +} + +static void get_battery_voltage() +{ + uint8_t cmd = BATTERY_VOLTAGE_CMD; + int16_t data; + + upsdebugx( 3, __func__ ); + + I2C_READ_WORD( upsfd, cmd, __func__ ) + + upsdebugx( 1, "Battery Voltage: %0.3fV", data / 1000.0 ); + dstate_setinfo( "battery.voltage", "%0.3f", data / 1000.0 ); +} + +static void get_battery_current() +{ + uint8_t cmd = BATTERY_CURRENT_CMD; + int16_t data; + + upsdebugx( 3, __func__ ); + + /* + * The reported current can actually be negative, so we cannot + * check for I2C failure by looking for negative values + */ + data = i2c_smbus_read_word_data(upsfd, cmd); + + if ( data & ( 1 << 15 ) ) + { + data = data - ( 1 << 16 ); + } + + upsdebugx( 1, "Battery Current: %0.3fA", data / 1000.0 ); + dstate_setinfo( "battery.current", "%0.3f", data / 1000.0 ); +} + +static void get_io_voltage() +{ + uint8_t cmd = IO_VOLTAGE_CMD; + int16_t data; + + upsdebugx( 3, __func__ ); + + I2C_READ_WORD( upsfd, cmd, __func__ ) + + upsdebugx( 1, "Input Voltage: %.3fV", data / 1000.0 ); + dstate_setinfo( "input.voltage", "%.3f", data / 1000.0 ); +} + +static void get_io_current() +{ + uint8_t cmd = IO_CURRENT_CMD; + int16_t data; + + upsdebugx( 3, __func__ ); + + /* + * The reported current can actually be negative, so we cannot + * check for I2C failure by looking for negative values + */ + data = i2c_smbus_read_word_data(upsfd, cmd); + + if ( data & ( 1 << 15 ) ) + { + data = data - ( 1 << 16 ); + } + + upsdebugx( 1, "Input Current: %.3fA", data / 1000.0 ); + dstate_setinfo( "input.current", "%.3f", data / 1000.0 ); +} + +static void get_firmware_version() +{ + uint8_t cmd = FIRMWARE_VERSION_CMD; + uint16_t data; + uint8_t major, minor; + + upsdebugx( 3, __func__ ); + + I2C_READ_WORD( upsfd, cmd, __func__ ) + + major = data >> 4; + minor = ( data << 4 & 0xf0 ) >> 4; + + if (( major != 1 ) || ( minor > 3 )) + { + upslogx( LOG_WARNING, "Unknown Firmware release: %d.%d", major, minor ); + } + + upsdebugx( 1, "UPS Firmware Version: %d.%d", major, minor ); + dstate_setinfo( "ups.firmware", "%d.%d", major, minor ); +} + +static void get_battery_profile() +{ + uint8_t cmd = BATTERY_PROFILE_CMD; + __u8 block[I2C_SMBUS_BLOCK_MAX]; + + upsdebugx( 3, __func__ ); + + I2C_READ_BLOCK( upsfd, cmd, 14, block, __func__ ) + + upsdebugx( 1, "Battery Capacity: %0.3fAh", ( block[1] << 8 | block[0] ) / 1000.0 ); + dstate_setinfo( "battery.capacity", "%0.3f", ( block[1] << 8 | block[0] ) / 1000.0 ); +} + +static void get_battery_profile_ext() +{ + uint8_t cmd = BATTERY_EXT_PROFILE_CMD; + __u8 block[I2C_SMBUS_BLOCK_MAX]; + + upsdebugx( 3, __func__ ); + + I2C_READ_BLOCK( upsfd, cmd, 17, block, __func__ ) + + switch( block[0] & 0xFF00 ) + { + case 0: + upsdebugx( 1, "Battery Chemistry: LiPO" ); + dstate_setinfo( "battery.type", "%s", "LiPO" ); + break; + case 1: + upsdebugx( 1, "Battery Chemistry: LiFePO4" ); + dstate_setinfo( "battery.type", "%s", "LiFePO4" ); + break; + default: + upsdebugx( 1, "Battery Chemistry: UNKNOWN" ); + dstate_setinfo( "battery.type", "%s", "UNKNOWN" ); + } +} + +static void get_power_off() +{ + uint8_t cmd = POWER_OFF_CMD; + uint8_t data; + + upsdebugx( 3, __func__ ); + + I2C_READ_BYTE( upsfd, cmd, __func__ ) + + if ( data == 255 ) + { + upsdebugx( 1, "Power Off: DISABLED" ); + } + else if ( data <= 250 ) + { + upsdebugx( 1, "Power Off: %d Seconds", data ); + } +} + +static void set_power_off() +{ + uint8_t cmd = POWER_OFF_CMD; + + upsdebugx( 3, __func__ ); + + /* + * Acceptable values for shutdown_delay are 1-250, + * use 0/255 to clear a scheduled power off command + */ + + if ( shutdown_delay > 250 ) + { + upslogx( + LOG_WARNING, + "shutdown delay of >250 seconds requested, shortening to 250 seconds" + ); + shutdown_delay = 250; + } + + if ( shutdown_delay == 0 ) + { + upslogx( + LOG_WARNING, + "shutdown delay of 0 seconds requested, using 1 second instead" + ); + shutdown_delay = 1; + } + + I2C_WRITE_BYTE( upsfd, cmd, shutdown_delay, __func__ ) +} + +static void get_time() +{ + uint8_t cmd = RTC_TIME_CMD; + __u8 block[I2C_SMBUS_BLOCK_MAX]; + uint8_t second, minute, hour, day, month, subsecond; + uint16_t year; + + upsdebugx( 3, __func__ ); + + I2C_READ_BLOCK( upsfd, cmd, 9, block, __func__ ) + + second = (( (block[0] >> 4 ) & 0x07) * 10 ) + ( block[0] & 0x0F ); + minute = (( (block[1] >> 4 ) & 0x07) * 10 ) + ( block[1] & 0x0F ); + hour = (( (block[2] >> 4 ) & 0x03) * 10 ) + ( block[2] & 0x0F ); + day = (( (block[4] >> 4 ) & 0x03) * 10 ) + ( block[4] & 0x0F ); + month = (( (block[5] >> 4 ) & 0x01) * 10 ) + ( block[5] & 0x0F ); + year = (( (block[6] >> 4 ) & 0x0F) * 10 ) + ( block[6] & 0x0F ) + 2000; + subsecond = block[7] * 100 / 256; + + upsdebugx( 1, "UPS Time: %02d:%02d:%02d.%02d", hour, minute, second, subsecond ); + dstate_setinfo( "ups.time", "%02d:%02d:%02d.%02d", hour, minute, second, subsecond ); + + upsdebugx( 1, "UPS Date: %04d-%02d-%02d", year, month, day ); + dstate_setinfo( "ups.date", "%04d-%02d-%02d", year, month, day ); +} + +static void get_i2c_address() +{ + uint8_t cmd = I2C_ADDRESS_CMD; + uint8_t data; + + upsdebugx( 3, __func__ ); + + I2C_READ_BYTE( upsfd, cmd, __func__ ) + + upsdebugx( 1, "I2C Address: 0x%0x", data ); + + if ( data == i2c_address ) + { + upsdebugx( 1, "Found device '0x%0x' on port '%s'", + (unsigned int) i2c_address, device_path ); + } + else + { + fatalx( EXIT_FAILURE, + "Could not find PiJuice HAT at I2C address 0x%0x", + i2c_address ); + } +} + +void upsdrv_initinfo(void) +{ + + dstate_setinfo( "ups.mfr", "%s", "PiJuice" ); + dstate_setinfo( "ups.type", "%s", "HAT" ); + + /* note: for a transition period, these data are redundant */ + + dstate_setinfo( "device.mfr", "%s", "PiJuice" ); + dstate_setinfo( "device.type", "%s", "HAT" ); + + upsdebugx( 1, "Low Battery Threshold: %0.0f%%", LOW_BATTERY_THRESHOLD ); + dstate_setinfo( "battery.charge.low", "%0.0f", LOW_BATTERY_THRESHOLD ); + + upsdebugx( 1, "Nominal Battery Voltage: %0.3fV", NOMINAL_BATTERY_VOLTAGE ); + dstate_setinfo( "battery.voltage.nominal", "%0.3f", NOMINAL_BATTERY_VOLTAGE ); + + get_i2c_address(); + get_battery_profile(); + get_battery_profile_ext(); +} + +void upsdrv_updateinfo(void) +{ + status_init(); + + get_status(); + get_battery_temperature(); + get_battery_voltage(); + get_battery_current(); + get_io_voltage(); + get_io_current(); + get_time(); + get_power_off(); + + status_commit(); + dstate_dataok(); +} + +void upsdrv_shutdown(void) +{ + set_power_off(); +} + +void upsdrv_help(void) +{ + printf("\nThe default I2C address is 20 [0x14]\n"); + printf("\n"); +} + +void upsdrv_makevartable(void) +{ + addvar(VAR_VALUE, "i2c_address", "Override i2c address setting"); +} + +void upsdrv_initups(void) +{ + upsfd = open_i2c_bus( device_path, i2c_address ); + + /* probe ups type */ + get_firmware_version(); + + /* get variables and flags from the command line */ + + if (getval("i2c_address")) + i2c_address = atoi(getval("i2c_address")); +} + +void upsdrv_cleanup(void) +{ + close(upsfd); +} diff --git a/drivers/powercom-hid.c b/drivers/powercom-hid.c index 7e1b7fd..a3973ff 100644 --- a/drivers/powercom-hid.c +++ b/drivers/powercom-hid.c @@ -25,7 +25,7 @@ #include "powercom-hid.h" #include "usb-common.h" -#define POWERCOM_HID_VERSION "PowerCOM HID 0.3" +#define POWERCOM_HID_VERSION "PowerCOM HID 0.6" /* FIXME: experimental flag to be put in upsdrv_info */ /* PowerCOM */ @@ -43,11 +43,12 @@ static usb_device_id_t powercom_usb_device_table[] = { { USB_DEVICE(POWERCOM_VENDORID, 0x00a5), NULL }, /* PowerCOM BNT - Black Knight Pro */ { USB_DEVICE(POWERCOM_VENDORID, 0x00a6), NULL }, - /* PowerCOM BNT-xxxAP */ + /* PowerCOM Vanguard and BNT-xxxAP */ { USB_DEVICE(POWERCOM_VENDORID, 0x0004), NULL }, + { USB_DEVICE(POWERCOM_VENDORID, 0x0001), NULL }, /* Terminating entry */ - { -1, -1, NULL } + { 0, 0, NULL } }; static char powercom_scratch_buf[32]; @@ -66,9 +67,18 @@ static double powercom_startup_nuf(const char *value) { const char *s = dstate_getinfo("ups.delay.start"); uint16_t val, command; + int iv; - val = atoi(value ? value : s) / 60; - command = ((val << 8) + (val >> 8)); + iv = atoi(value ? value : s) / 60; + if (iv < 0 || (intmax_t)iv > (intmax_t)UINT16_MAX) { + upsdebugx(0, "%s: value = %d is not in uint16_t range", __func__, iv); + return 0; + } + + /* COMMENTME: What are we doing here, a byte-swap in the word? */ + val = (uint16_t)iv; + command = (uint16_t)(val << 8); + command += (uint16_t)(val >> 8); upsdebugx(3, "%s: value = %s, command = %04X", __func__, value, command); return command; @@ -92,9 +102,17 @@ static double powercom_shutdown_nuf(const char *value) { const char *s = dstate_getinfo("ups.delay.shutdown"); uint16_t val, command; + int iv; - val = atoi(value ? value : s); - command = ((val % 60) << 8) + (val / 60); + iv = atoi(value ? value : s); + if (iv < 0 || (intmax_t)iv > (intmax_t)UINT16_MAX) { + upsdebugx(0, "%s: value = %d is not in uint16_t range", __func__, iv); + return 0; + } + + val = (uint16_t)iv; + val = val ? val : 1; /* 0 sets the maximum delay */ + command = ((uint16_t)((val % 60) << 8)) + (uint16_t)(val / 60); command |= 0x4000; /* AC RESTART NORMAL ENABLE */ upsdebugx(3, "%s: value = %s, command = %04X", __func__, value, command); @@ -109,9 +127,17 @@ static double powercom_stayoff_nuf(const char *value) { const char *s = dstate_getinfo("ups.delay.shutdown"); uint16_t val, command; + int iv; - val = atoi(value ? value : s); - command = ((val % 60) << 8) + (val / 60); + iv = atoi(value ? value : s); + if (iv < 0 || (intmax_t)iv > (intmax_t)UINT16_MAX) { + upsdebugx(0, "%s: value = %d is not in uint16_t range", __func__, iv); + return 0; + } + + val = (uint16_t)iv; + val = val ? val : 1; /* 0 sets the maximum delay */ + command = ((uint16_t)((val % 60) << 8)) + (uint16_t)(val / 60); command |= 0x8000; /* AC RESTART NORMAL DISABLE */ upsdebugx(3, "%s: value = %s, command = %04X", __func__, value, command); @@ -123,9 +149,137 @@ static info_lkp_t powercom_stayoff_info[] = { }; static info_lkp_t powercom_beeper_info[] = { - { 1, "enabled", NULL }, - { 2, "disabled", NULL }, /* muted? */ - { 0, NULL, NULL } + { 1, "enabled", NULL, NULL }, + { 2, "disabled", NULL, NULL }, /* muted? */ + { 0, NULL, NULL, NULL } +}; + +static const char *powercom_voltage_conversion_fun(double value) +{ + static char buf[20]; + snprintf(buf, sizeof(buf), "%0.0f", value * 4); + return buf; +} + +static info_lkp_t powercom_voltage_conversion[] = { + { 0, NULL, powercom_voltage_conversion_fun, NULL } +}; + +static const char *powercom_upsfail_conversion_fun(double value) +{ + if ((long)value & 0x0001) { + return "fanfail"; + } else { + return "!fanfail"; + } +} + +static info_lkp_t powercom_upsfail_conversion[] = { + { 0, NULL, powercom_upsfail_conversion_fun, NULL } +}; + +static const char *powercom_replacebatt_conversion_fun(double value) +{ + if ((long)value & 0x0002) { + return "replacebatt"; + } else { + return "!replacebatt"; + } +} + +static info_lkp_t powercom_replacebatt_conversion[] = { + { 0, NULL, powercom_replacebatt_conversion_fun, NULL } +}; + +static const char *powercom_test_conversion_fun(double value) +{ + if ((long)value & 0x0004) { + return "cal"; + } else { + return "!cal"; + } +} + +static info_lkp_t powercom_test_conversion[] = { + { 0, NULL, powercom_test_conversion_fun, NULL } +}; + +static const char *powercom_shutdownimm_conversion_fun(double value) +{ + if ((long)value & 0x0010) { + return "shutdownimm"; + } else { + return "!shutdownimm"; + } +} + +static info_lkp_t powercom_shutdownimm_conversion[] = { + { 0, NULL, powercom_shutdownimm_conversion_fun, NULL } +}; + +static const char *powercom_online_conversion_fun(double value) +{ + if ((long)value & 0x0001) { + return "!online"; + } else { + return "online"; + } +} + +static info_lkp_t powercom_online_conversion[] = { + { 0, NULL, powercom_online_conversion_fun, NULL } +}; + +static const char *powercom_lowbatt_conversion_fun(double value) +{ + if ((long)value & 0x0002) { + return "lowbatt"; + } else { + return "!lowbatt"; + } +} + +static info_lkp_t powercom_lowbatt_conversion[] = { + { 0, NULL, powercom_lowbatt_conversion_fun, NULL } +}; + +static const char *powercom_trim_conversion_fun(double value) +{ + if (((long)value & 0x0018) == 0x0008) { + return "trim"; + } else { + return "!trim"; + } +} + +static info_lkp_t powercom_trim_conversion[] = { + { 0, NULL, powercom_trim_conversion_fun, NULL } +}; + +static const char *powercom_boost_conversion_fun(double value) +{ + if (((long)value & 0x0018) == 0x0018) { + return "boost"; + } else { + return "!boost"; + } +} + +static info_lkp_t powercom_boost_conversion[] = { + { 0, NULL, powercom_boost_conversion_fun, NULL } +}; + +static const char *powercom_overload_conversion_fun(double value) +{ + if ((long)value & 0x0020) { + return "overload"; + } else { + return "!overload"; + } +} + +static info_lkp_t powercom_overload_conversion[] = { + { 0, NULL, powercom_overload_conversion_fun, NULL } }; /* --------------------------------------------------------------- */ @@ -134,9 +288,31 @@ static info_lkp_t powercom_beeper_info[] = { /* POWERCOM usage table */ static usage_lkp_t powercom_usage_lkp[] = { + { "PowercomUPS", 0x00020004 }, + { "PowercomBatterySystem", 0x00020010 }, + { "PowercomPowerConverter", 0x00020016 }, + { "PowercomInput", 0x0002001a }, + { "PowercomOutput", 0x0002001c }, + { "PowercomVoltage", 0x00020030 }, + { "PowercomFrequency", 0x00020032 }, + { "PowercomPercentLoad", 0x00020035 }, + { "PowercomTemperature", 0x00020036 }, + { "PowercomDelayBeforeStartup", 0x00020056 }, + { "PowercomDelayBeforeShutdown", 0x00020057 }, + { "PowercomTest", 0x00020058 }, + { "PowercomShutdownRequested", 0x00020068 }, + { "PowercomInternalChargeController", 0x00020081 }, + { "PowercomPrimaryBatterySupport", 0x00020082 }, + { "PowercomDesignCapacity", 0x00020083 }, + { "PowercomSpecificationInfo", 0x00020084 }, + { "PowercomManufacturerDate", 0x00020085 }, + { "PowercomSerialNumber", 0x00020086 }, + { "PowercomManufacturerName", 0x00020087 }, { "POWERCOM1", 0x0084002f }, { "POWERCOM2", 0xff860060 }, { "POWERCOM3", 0xff860080 }, + { "PCMDelayBeforeStartup", 0x00ff0056 }, + { "PCMDelayBeforeShutdown", 0x00ff0057 }, { NULL, 0 } }; @@ -235,6 +411,8 @@ static hid_info_t powercom_hid2nut[] = { */ { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.PowerSummary.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL }, { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, powercom_shutdown_info }, + { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 8, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL }, + { "ups.timer.shutdown", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, powercom_shutdown_info }, { "input.voltage", 0, 0, "UPS.Input.Voltage", NULL, "%.1f", 0, NULL }, { "input.voltage.nominal", 0, 0, "UPS.Input.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, @@ -251,10 +429,67 @@ static hid_info_t powercom_hid2nut[] = { /* instcmds */ { "beeper.toggle", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, + { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, + { "beeper.disable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "0", HU_TYPE_CMD, NULL }, { "test.battery.start.quick", 0, 0, "UPS.Battery.Test", NULL, "1", HU_TYPE_CMD, NULL }, { "load.on.delay", 0, 0, "UPS.PowerSummary.DelayBeforeStartup", NULL, NULL, HU_TYPE_CMD, powercom_startup_info }, { "shutdown.return", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_shutdown_info }, { "shutdown.stayoff", 0, 0, "UPS.PowerSummary.DelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_stayoff_info }, + { "load.on", 0, 0, "UPS.PowerSummary.PCMDelayBeforeStartup", NULL, "0", HU_TYPE_CMD, powercom_startup_info }, + { "load.off", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, "0", HU_TYPE_CMD, powercom_stayoff_info }, + { "shutdown.return", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_shutdown_info }, + { "shutdown.stayoff", 0, 0, "UPS.PowerSummary.PCMDelayBeforeShutdown", NULL, NULL, HU_TYPE_CMD, powercom_stayoff_info }, + + { "ups.serial", 0, 0, "PowercomUPS.PowercomSerialNumber", NULL, "%s", 0, stringid_conversion }, + { "ups.mfr", 0, 0, "PowercomUPS.PowercomManufacturerName", NULL, "%s", 0, stringid_conversion }, +/* { "UPS.DesignCapacity", 0, 0, "PowercomUPS.PowercomDesignCapacity", NULL, "%.0f", 0, NULL }, is always 255 */ + { "ups.mfr.date", 0, 0, "PowercomUPS.PowercomManufacturerDate", NULL, "%s", 0, date_conversion }, + { "battery.temperature", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomTemperature", NULL, "%.0f", 0, NULL }, + { "battery.temperature", 0, 0, "UPS.Battery.Temperature", NULL, "%.1f", 0, NULL }, + { "battery.charge", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomVoltage", NULL, "%.0f", 0, NULL }, +/* { "UPS.BatterySystem.SpecificationInfo", 0, 0, "PowercomUPS.PowercomBatterySystem.PowercomSpecificationInfo", NULL, "%.0f", 0, NULL }, */ + { "input.frequency", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomInput.PowercomFrequency", NULL, "%.0f", 0, NULL }, + { "input.voltage", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomInput.PowercomVoltage", NULL, "%.0f", 0, powercom_voltage_conversion }, + { "output.voltage", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomVoltage", NULL, "%.0f", 0, powercom_voltage_conversion }, + { "ups.load", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPercentLoad", NULL, "%.0f", 0, NULL }, + /* flags: 4 - Testing, 8 - Probably mute (it's set on battery with muted beeper and sometimes during ups test) + * bit 0 UPS fault (1 = FAILT) + * bit 1 Battery status (1 = BAD, 0 = NORMAL) + * bit 2 Test mode (1 = TEST, 0 = NORMAL) + * bit 3 X + * bit 4 Pre-SD count mode (1 = ACTIVE) + * bit 5 Schedule count mode (1 = ACTIVE) + * bit 6 Disable NO LOAD SHUTDOWN (1 = ACTIVE) + * bit 7 0 + */ +/* { "UPS.PowerConverter.Output.InternalChargeController", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, "%.0f", 0, NULL }, */ + { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_upsfail_conversion }, + { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_replacebatt_conversion }, + { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_test_conversion }, + { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomInternalChargeController", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_shutdownimm_conversion }, + /* flags: 1 - On battery, 2 - Low Battery, 8 - Trim, 8+16 - Boost + * bit 0 is line fail (1 = INV, 0 = LINE) + * bit 1 is low battery (1 = BAT_ LOW, 0 = NORMAL) + * bit 2 X + * bit 3 AVR status (1 = AVR, 0 = NO_AVR) + * bit 4 AVR mode (1 = BOOST, 0 = BUCK) + * bit 5 Load status (1 = OVER LOAD, 0 = NORMAL) + * bit 6 X + * bit 7 SD mode display + */ +/* { "UPS.PowerConverter.Output.PrimaryBatterySupport", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, "%.0f", 0, NULL }, */ + { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_online_conversion }, + /* Low battery status may not work */ + { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_lowbatt_conversion }, + { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_trim_conversion }, + { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_boost_conversion }, + { "BOOL", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomPrimaryBatterySupport", NULL, NULL, HU_FLAG_QUICK_POLL, powercom_overload_conversion }, + { "output.frequency", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomOutput.PowercomFrequency", NULL, "%.0f", 0, NULL }, + { "ups.test.result", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomTest", NULL, "%s", 0, test_read_info }, +/* { "UPS.PowerConverter.ShutdownRequested", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomShutdownRequested", NULL, "%.0f", 0, NULL }, */ + { "ups.delay.shutdown", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeShutdown", NULL, "%.0f", 0, NULL }, + { "ups.delay.start", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeStartup", NULL, "%.0f", 0, NULL }, + { "load.off", 0, 0, "PowercomUPS.PowercomPowerConverter.PowercomDelayBeforeShutdown", NULL, "0", HU_TYPE_CMD, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } @@ -276,7 +511,7 @@ static const char *powercom_format_serial(HIDDevice_t *hd) { * the device is supported by this subdriver, else 0. */ static int powercom_claim(HIDDevice_t *hd) { - int status = is_usb_device_supported(powercom_usb_device_table, hd->VendorID, hd->ProductID); + int status = is_usb_device_supported(powercom_usb_device_table, hd); switch (status) { @@ -295,6 +530,10 @@ static int powercom_claim(HIDDevice_t *hd) return 0; case SUPPORTED: + if (hd->ProductID == 0x0001) { + interrupt_only = 1; + interrupt_size = 8; + } return 1; case NOT_SUPPORTED: @@ -311,4 +550,5 @@ subdriver_t powercom_subdriver = { powercom_format_model, powercom_format_mfr, powercom_format_serial, + fix_report_desc, }; diff --git a/drivers/powercom.c b/drivers/powercom.c index 28ce962..6e58a3b 100644 --- a/drivers/powercom.c +++ b/drivers/powercom.c @@ -6,27 +6,28 @@ * See http://www.advice.co.il/product/inter/ups.html for its specifications. * This model is based on PowerCom (www.powercom.com) models. * -Socomec Sicon Egys 420 - * - * $Id: powercom.c 2990 2011-05-19 18:34:09Z aquette $ + * -OptiUPS VS 575C * * Copyrights: + * (C) 2015 Arnaud Quette + * (C) 2013 Florian Bruhin * (C) 2002 Simon Rozman * (C) 1999 Peter Bieringer - * + * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. - * + * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * + * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * + * * rev 0.7: Alexey Sidorov * - add Powercom's Black Knight Pro model support ( BNT-400/500/600/800/801/1000/1200/1500/2000AP 220-240V ) * @@ -60,7 +61,24 @@ * * Tested on: BNT-1500A * - */ + * rev 0.14: Florian Bruhin (The Compiler) + * - Added support for OptiUPS VS 575C + * This probably also works with others, but I don't have their model numbers. + * + * rev 0.15: VSE NN + * - Fixed UPS type assignment for Powercom Imperial USB series manufactured since 2009. + * + * Tested on: IMP-625AP + * + * rev 0.16: Arnaud Quette + * - Fixed the processing of input/output voltages for KIN models + * (https://github.com/networkupstools/nut/issues/187) + * + * rev 0.18: Rouben Tchakhmakhtchian + * - Added nobt flag to config that skips UPS battery check on startup/init + * (https://github.com/networkupstools/nut/issues/546) + * + */ #include "main.h" #include "serial.h" @@ -68,7 +86,7 @@ #include "math.h" #define DRIVER_NAME "PowerCom protocol UPS driver" -#define DRIVER_VERSION "0.13" +#define DRIVER_VERSION "0.19" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -76,7 +94,10 @@ upsdrv_info_t upsdrv_info = { DRIVER_VERSION, "Simon Rozman \n" \ "Peter Bieringer \n" \ - "Alexey Sidorov ", + "Alexey Sidorov \n" \ + "Florian Bruhin \n" \ + "Arnaud Quette \n" \ + "Rouben Tchakhmakhtchian ", DRV_STABLE, { NULL } }; @@ -124,83 +145,94 @@ static void no_flow_control (void); * 120VoltageFactor, 120VoltageConstant }, */ static struct type types[] = { - { - "Trust", - 11, - { "dtr0rts1", dtr0rts1 }, - { { 5U, 0U }, { 7U, 0U }, { 8U, 0U } }, - { { 0U, 10U }, 'n' }, - { 0.00020997, 0.00020928 }, - { 6.1343, -0.3808, 4.3110, 0.1811 }, - { 5.0000, 0.3268, -825.00, 4.5639, -835.82 }, - { 1.9216, -0.0977, 0.9545, 0.0000 }, - }, - { - "Egys", - 16, - { "no_flow_control", no_flow_control }, - { { 5U, 0x80U }, { 7U, 0U }, { 8U, 0U } }, - { { 0U, 10U }, 'n' }, - { 0.00020997, 0.00020928 }, - { 6.1343, -0.3808, 1.3333, 0.6667 }, - { 5.0000, 0.3268, -825.00, 2.2105, -355.37 }, - { 1.9216, -0.0977, 0.9545, 0.0000 }, - }, - { - "KP625AP", - 16, - { "dtr0rts1", dtr0rts1 }, - { { 5U, 0x80U }, { 7U, 0U }, { 8U, 0U } }, - { { 0U, 10U }, 'n' }, - { 0.00020997, 0.00020928 }, - { 6.1343, -0.3808, 4.3110, 0.1811 }, - { 5.0000, 0.3268, -825.00, 4.5639, -835.82 }, - { 1.9216, -0.0977, 0.9545, 0.0000 }, - }, - { - "IMP", - 16, - { "no_flow_control", no_flow_control }, - { { 5U, 0xFFU }, { 7U, 0U }, { 8U, 0U } }, - { { 1U, 30U }, 'y' }, - { 0.00020997, 0.00020928 }, - { 6.1343, -0.3808, 4.3110, 0.1811 }, - { 5.0000, 0.3268, -825.00, 4.5639, -835.82 }, - { 1.9216, -0.0977, 0.9545, 0.0000 }, - }, - { - "KIN", - 16, - { "no_flow_control", no_flow_control }, - { { 11U, 0x4bU }, { 8U, 0U }, { 8U, 0U } }, - { { 1U, 30U }, 'y' }, - { 0.00020997, 0.0 }, - { 6.1343, -0.3808, 1.075, 0.1811 }, - { 5.0000, 0.3268, -825.00, 0.46511, 0 }, - { 1.9216, -0.0977, 0.82857, 0.0000 }, - }, - { - "BNT", - 16, - { "no_flow_control", no_flow_control }, - { { 11U, 0x42U }, { 8U, 0U }, { 8U, 0U } }, - { { 1U, 30U }, 'y' }, - { 0.00020803, 0.0 }, - { 1.4474, 0.0, 0.8594, 0.0 }, - { 5.0000, 0.3268, -825.00, 0.46511, 0 }, - { 1.9216, -0.0977, 0.82857, 0.0000 }, - }, - { - "BNT-other", - 16, - { "no_flow_control", no_flow_control }, - { { 8U, 0U }, { 8U, 0U }, { 8U, 0U } }, - { { 1U, 30U }, 'y' }, - { 0.00027778, 0.0000 }, - { 1.0000, 0.0000, 1.0000, 0.0000 }, - { 1.0000, 0.0000, 0.0000, 1.0000, 0.0000 }, - { 2.0000, 0.0000, 2.0000, 0.0000 }, - }, + { + "Trust", + 11, + { "dtr0rts1", dtr0rts1 }, + { { 5U, 0U }, { 7U, 0U }, { 8U, 0U } }, + { { 0U, 10U }, 'n' }, + { 0.00020997, 0.00020928 }, + { 6.1343, -0.3808, 4.3110, 0.1811 }, + { 5.0000, 0.3268, -825.00, 4.5639, -835.82 }, + { 1.9216, -0.0977, 0.9545, 0.0000 }, + }, + { + "Egys", + 16, + { "no_flow_control", no_flow_control }, + { { 5U, 0x80U }, { 7U, 0U }, { 8U, 0U } }, + { { 0U, 10U }, 'n' }, + { 0.00020997, 0.00020928 }, + { 6.1343, -0.3808, 1.3333, 0.6667 }, + { 5.0000, 0.3268, -825.00, 2.2105, -355.37 }, + { 1.9216, -0.0977, 0.9545, 0.0000 }, + }, + { + "KP625AP", + 16, + { "dtr0rts1", dtr0rts1 }, + { { 5U, 0x80U }, { 7U, 0U }, { 8U, 0U } }, + { { 0U, 10U }, 'n' }, + { 0.00020997, 0.00020928 }, + { 6.1343, -0.3808, 4.3110, 0.1811 }, + { 5.0000, 0.3268, -825.00, 4.5639, -835.82 }, + { 1.9216, -0.0977, 0.9545, 0.0000 }, + }, + { + "IMP", + 16, + { "no_flow_control", no_flow_control }, + { { 5U, 0xFFU }, { 7U, 0U }, { 8U, 0U } }, + { { 1U, 30U }, 'y' }, + { 0.00020997, 0.00020928 }, + { 6.1343, -0.3808, 4.3110, 0.1811 }, + { 5.0000, 0.3268, -825.00, 4.5639, -835.82 }, + { 1.9216, -0.0977, 0.9545, 0.0000 }, + }, + { + "KIN", + 16, + { "no_flow_control", no_flow_control }, + { { 11U, 0x4bU }, { 8U, 0U }, { 8U, 0U } }, + { { 1U, 30U }, 'y' }, + { 0.00020997, 0.0 }, + { 6.1343, -0.3808, 1.075, 0.1811 }, + { 5.0000, 0.3268, -825.00, 0.46511, 0 }, + { 1.9216, -0.0977, 0.82857, 0.0000 }, + }, + { + "BNT", + 16, + { "no_flow_control", no_flow_control }, + { { 11U, 0x42U }, { 8U, 0U }, { 8U, 0U } }, + { { 1U, 30U }, 'y' }, + { 0.00020803, 0.0 }, + { 1.4474, 0.0, 0.8594, 0.0 }, + { 5.0000, 0.3268, -825.00, 0.46511, 0 }, + { 1.9216, -0.0977, 0.82857, 0.0000 }, + }, + { + "BNT-other", + 16, + { "no_flow_control", no_flow_control }, + { { 8U, 0U }, { 8U, 0U }, { 8U, 0U } }, + { { 1U, 30U }, 'y' }, + { 0.00027778, 0.0000 }, + { 1.0000, 0.0000, 1.0000, 0.0000 }, + { 1.0000, 0.0000, 0.0000, 1.0000, 0.0000 }, + { 2.0000, 0.0000, 2.0000, 0.0000 }, + }, + { + "OPTI", + 16, + { "no_flow_control", no_flow_control }, + { { 5U, 0xFFU }, { 7U, 0U }, { 8U, 0U } }, + { { 1U, 30U }, 'y' }, + { 0.0000, 0.0000 }, + { 1.0000, 0.0000, 1.0000, 0.0000 }, + { 1.0000, 0.0000, 0.0000, 1.0000, 0.0000 }, + { 2.0000, 0.0000, 2.0000, 0.0000 }, + }, }; /* values for sending to UPS */ @@ -208,7 +240,7 @@ enum commands { SEND_DATA = '\x01', BATTERY_TEST = '\x03', WAKEUP_TIME = '\x04', - RESTART = '\xb9', + RESTART = '\xb9', SHUTDOWN = '\xba', COUNTER = '\xbc' }; @@ -247,55 +279,69 @@ enum status { OFF = 128U }; -unsigned int voltages[]={100,110,115,120,0,0,0,200,220,230,240,0,0,0,0,0}; -unsigned int BNTmodels[]={0,400,500,600,800,801,1000,1200,1500,2000,0,0,0,0,0,0}; -unsigned int KINmodels[]={0,425,500,525,625,800,1000,1200,1500,1600,2200,2200,2500,3000,5000,0}; -unsigned int IMPmodels[]={0,425,525,625,825,1025,1200,1500,2000,0,0,0,0,0,0,0}; +static unsigned int voltages[] = {100,110,115,120,0,0,0,200,220,230,240,0,0,0,0,0}; +static unsigned int BNTmodels[] = {0,400,500,600,800,801,1000,1200,1500,2000,0,0,0,0,0,0}; +static unsigned int KINmodels[] = {0,425,500,525,625,800,1000,1200,1500,1600,2200,2200,2500,3000,5000,0}; +static unsigned int IMPmodels[] = {0,425,525,625,825,1025,1200,1500,2000,0,0,0,0,0,0,0}; +static unsigned int OPTImodels[] = {0,0,0,575,0,0,0,0,0,0,0,0,0,0,0,0}; /* * local used functions */ +static void shutdown_halt(void) + __attribute__((noreturn)); + static void shutdown_halt(void) { - ser_send_char (upsfd, SHUTDOWN); - if (types[type].shutdown_arguments.minutesShouldBeUsed != 'n') + ser_send_char (upsfd, (unsigned char)SHUTDOWN); + if (types[type].shutdown_arguments.minutesShouldBeUsed != 'n') ser_send_char (upsfd, types[type].shutdown_arguments.delay[0]); ser_send_char (upsfd, types[type].shutdown_arguments.delay[1]); upslogx(LOG_INFO, "Shutdown (stayoff) initiated."); exit (0); } +static void shutdown_ret(void) + __attribute__((noreturn)); + static void shutdown_ret(void) { - ser_send_char (upsfd, RESTART); - ser_send_char (upsfd, COUNTER); - if (types[type].shutdown_arguments.minutesShouldBeUsed != 'n') + ser_send_char (upsfd, (unsigned char)RESTART); + ser_send_char (upsfd, (unsigned char)COUNTER); + if (types[type].shutdown_arguments.minutesShouldBeUsed != 'n') ser_send_char (upsfd, types[type].shutdown_arguments.delay[0]); ser_send_char (upsfd, types[type].shutdown_arguments.delay[1]); upslogx(LOG_INFO, "Shutdown (return) initiated."); - + exit (0); } /* registered instant commands */ static int instcmd (const char *cmdname, const char *extra) { - if (!strcasecmp(cmdname, "test.battery.start")) { + if (!strcasecmp(cmdname, "test.battery.start")) { ser_send_char (upsfd, BATTERY_TEST); return STAT_INSTCMD_HANDLED; - } - if (!strcasecmp(cmdname, "shutdown.return")) { + } + if (!strcasecmp(cmdname, "shutdown.return")) { + /* NOTE: In this context, "return" is UPS behavior after the + * wall-power gets restored. The routine exits the driver anyway. + */ shutdown_ret(); +#ifndef HAVE___ATTRIBUTE__NORETURN return STAT_INSTCMD_HANDLED; +#endif } if (!strcasecmp(cmdname, "shutdown.stayoff")) { - shutdown_halt(); + shutdown_halt(); +#ifndef HAVE___ATTRIBUTE__NORETURN return STAT_INSTCMD_HANDLED; +#endif } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); - return STAT_INSTCMD_UNKNOWN; + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; } /* set DTR and RTS lines on a serial port to supply a passive @@ -303,8 +349,8 @@ static int instcmd (const char *cmdname, const char *extra) */ static void dtr0rts1 (void) { - ser_set_dtr(upsfd, 0); - ser_set_rts(upsfd, 1); + ser_set_dtr(upsfd, 0); + ser_set_rts(upsfd, 1); upsdebugx(2, "DTR => 0, RTS => 1"); } @@ -312,13 +358,13 @@ static void dtr0rts1 (void) static void no_flow_control (void) { struct termios tio; - + tcgetattr (upsfd, &tio); - - tio.c_iflag &= ~ (IXON | IXOFF); + + tio.c_iflag &= ~ ((tcflag_t)IXON | (tcflag_t)IXOFF); tio.c_cc[VSTART] = _POSIX_VDISABLE; tio.c_cc[VSTOP] = _POSIX_VDISABLE; - + upsdebugx(2, "Flow control disable"); /* disable any flow control */ @@ -328,15 +374,15 @@ static void no_flow_control (void) /* sane check for returned buffer */ static int validate_raw_data (void) { - int i = 0, - num_of_tests = + int i = 0, + num_of_tests = sizeof types[0].validation / sizeof types[0].validation[0]; - + for (i = 0; - i < num_of_tests && + i < num_of_tests && raw_data[ types[type].validation[i].index_of_byte] == - types[type].validation[i].required_value; + types[type].validation[i].required_value; i++) ; return (i < num_of_tests) ? 1 : 0; } @@ -344,8 +390,9 @@ static int validate_raw_data (void) /* get info from ups */ static int ups_getinfo(void) { - int i, c; - + size_t i; + ssize_t c; + /* send trigger char to UPS */ if (ser_send_char (upsfd, SEND_DATA) != 1) { upslogx(LOG_NOTICE, "writing error"); @@ -354,33 +401,34 @@ static int ups_getinfo(void) } else { upsdebugx(5, "Num of bytes requested for reading from UPS: %d", types[type].num_of_bytes_from_ups); + /* Note: num_of_bytes_from_ups is (unsigned char) so comparable + * to ssize_t without more range checks */ c = ser_get_buf_len(upsfd, raw_data, types[type].num_of_bytes_from_ups, 3, 0); - - if (c != types[type].num_of_bytes_from_ups) { - upslogx(LOG_NOTICE, "data receiving error (%d instead of %d bytes)", c, types[type].num_of_bytes_from_ups); + + if (c != (ssize_t)types[type].num_of_bytes_from_ups) { + upslogx(LOG_NOTICE, "data receiving error (%zd instead of %d bytes)", c, types[type].num_of_bytes_from_ups); dstate_datastale(); return 0; } else - upsdebugx(5, "Num of bytes received from UPS: %d", c); - - }; + upsdebugx(5, "Num of bytes received from UPS: %zd", c); + } /* optional dump of raw data */ if (nut_debug_level > 4) { /* FIXME: use upsdebug_hex() ? */ printf("Raw data from UPS:\n"); for (i = 0; i < types[type].num_of_bytes_from_ups; i++) { - printf("%2d 0x%02x (%c)\n", i, raw_data[i], raw_data[i]>=0x20 ? raw_data[i] : ' '); - }; - }; + printf("%2zu 0x%02x (%c)\n", i, raw_data[i], raw_data[i]>=0x20 ? raw_data[i] : ' '); + } + } /* validate raw data for correctness */ if (validate_raw_data() != 0) { upslogx(LOG_NOTICE, "data receiving error (validation check)"); dstate_datastale(); return 0; - }; + } return 1; } @@ -388,22 +436,34 @@ static float input_voltage(void) { unsigned int model; float tmp=0.0; - + if ( !strcmp(types[type].name, "BNT") && raw_data[MODELNUMBER]%16 > 7 ) { tmp=2.2*raw_data[INPUT_VOLTAGE]-24; } else if ( !strcmp(types[type].name, "KIN")) { model=KINmodels[raw_data[MODELNUMBER]/16]; - if (model<=625){ - tmp=1.79*raw_data[INPUT_VOLTAGE]+3.35; - } else if (model<2000){ - tmp=1.61*raw_data[INPUT_VOLTAGE]; - } else { - tmp=1.625*raw_data[INPUT_VOLTAGE]; + /* Process input voltage, according to line voltage and model rating */ + if (linevoltage < 200) { + if (model <= 625) { + tmp = 0.89 * raw_data[INPUT_VOLTAGE] + 6.18; + } else if ((model >= 800) && (model < 2000)) { + tmp = 1.61 * raw_data[INPUT_VOLTAGE] / 2.0; + } else { + tmp = 1.625 * raw_data[INPUT_VOLTAGE] / 2.0; + } } - } else if ( !strcmp(types[type].name, "IMP")) { + if (linevoltage >= 200) { + if (model <= 625) { + tmp = 1.79 * raw_data[INPUT_VOLTAGE] + 3.35; + } else if ((model >= 800) && (model < 2000)) { + tmp = 1.61 * raw_data[INPUT_VOLTAGE]; + } else { + tmp = 1.625 * raw_data[INPUT_VOLTAGE]; + } + } + } else if ( !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) { tmp=raw_data[INPUT_VOLTAGE]*2.0; } else { - tmp=linevoltage >= 220 ? + tmp=linevoltage >= 220 ? types[type].voltage[0] * raw_data[INPUT_VOLTAGE] + types[type].voltage[1] : types[type].voltage[2] * raw_data[INPUT_VOLTAGE] + types[type].voltage[3]; } @@ -415,10 +475,13 @@ static float output_voltage(void) { float tmp,rdatax,rdatay,rdataz,boostdata; unsigned int statINV = 0,statAVR = 0,statAVRMode = 0,model,t; - static float datax[]={0,1.0,1.0,1.0,1.0,1.89,1.89,1.89,0.127,0.127,1.89,1.89,1.89,0.256}; - static float datay[]={0,1.73,1.74,1.74,1.77,0.9,0.9,0.9,13.204,13.204,0.88,0.88,0.88,6.645}; - static float dataz[]={0,1.15,0.9,0.9,0.75,1.1,1.1,1.1,0.8,0.8,0.86,0.86,0.86,0.7}; - + static float datax1[]={0,1.0,1.0,1.0,1.0,0.945,0.945,0.945,0.127,0.127,0.945,0.945,0.945,0.256}; + static float datay1[]={0,0.85,0.85,0.85,0.88,0.9,0.9,0.9,6.6,6.6,0.87,0.87,0.87,3.29}; + static float dataz1[]={0,1.03,0.78,0.78,0.72,0.55,0.55,0.55,0.5,0.5,0.43,0.43,0.43,0.3}; + static float datax2[]={0,1.0,1.0,1.0,1.0,1.89,1.89,1.89,0.127,0.127,1.89,1.89,1.89,0.256}; + static float datay2[]={0,1.73,1.74,1.74,1.77,0.9,0.9,0.9,13.204,13.204,0.88,0.88,0.88,6.645}; + static float dataz2[]={0,1.15,0.9,0.9,0.75,1.1,1.1,1.1,0.8,0.8,0.86,0.86,0.86,0.7}; + if ( !strcmp(types[type].name, "BNT") || !strcmp(types[type].name, "KIN")) { statINV=raw_data[STATUS_A] & ONLINE; statAVR=raw_data[STATUS_A] & AVR_ON; @@ -444,45 +507,84 @@ static float output_voltage(void) } } else if ( !strcmp(types[type].name, "KIN")) { model=KINmodels[raw_data[MODELNUMBER]/16]; - if (statINV==0) { - if (statAVR==0) { - if (model<=625) - tmp=1.79*raw_data[OUTPUT_VOLTAGE]+3.35; - else if (model<2000) - tmp=1.61*raw_data[OUTPUT_VOLTAGE]; - else - tmp=1.625*raw_data[OUTPUT_VOLTAGE]; - } else { - if (statAVRMode > 0){ - if (model<=525) - tmp=2.07*raw_data[OUTPUT_VOLTAGE]; - else if (model==625) - tmp=2.07*raw_data[OUTPUT_VOLTAGE]+5; + if (statINV == 0) { + if (statAVR == 0) { + /* FIXME: miss test "if (iUPS == 1) {" */ + if (linevoltage >= 200) { + if (model <= 625) + tmp = 1.79*raw_data[OUTPUT_VOLTAGE] + 3.35; else if (model<2000) - tmp=1.87*raw_data[OUTPUT_VOLTAGE]; + tmp = 1.61*raw_data[OUTPUT_VOLTAGE]; else - tmp=1.87*raw_data[OUTPUT_VOLTAGE]; + tmp = 1.625*raw_data[OUTPUT_VOLTAGE]; } else { - if (model<=625) - tmp=1.571*raw_data[OUTPUT_VOLTAGE]; + if (model <= 625) + tmp = 0.89 * raw_data[OUTPUT_VOLTAGE] + 6.18; else if (model<2000) - tmp=1.37*raw_data[OUTPUT_VOLTAGE]; + tmp = 1.61 * raw_data[OUTPUT_VOLTAGE] / 2.0; else - tmp=1.4*raw_data[OUTPUT_VOLTAGE]; + tmp = 1.625 * raw_data[OUTPUT_VOLTAGE] / 2.0; + } + } + else if (statAVR == 1) { + /* FIXME: miss test "if ((iUPS == 1) || (iUPS == 13)) {" */ + if (linevoltage >= 200) { + if (model <= 525) + tmp = 2.07 * raw_data[OUTPUT_VOLTAGE]; + else if (model == 625) + tmp = 2.07 * raw_data[OUTPUT_VOLTAGE]+5; + else if (model < 2000) + tmp = 1.87 * raw_data[OUTPUT_VOLTAGE]; + else + tmp = 1.87 * raw_data[OUTPUT_VOLTAGE]; + } else { + if (model <= 625) + tmp = 2.158 * raw_data[OUTPUT_VOLTAGE] / 2.0; + else if (model < 2000) + tmp = 1.842 * raw_data[OUTPUT_VOLTAGE] / 2.0; + else + tmp = 1.875 * raw_data[OUTPUT_VOLTAGE] / 2.0; + } + } else { + /* FIXME: miss test "if ((iUPS == 1) || (iUPS == 13)) {" */ + if (linevoltage >= 200) { + if (model == 625) + tmp = 1.571 * raw_data[OUTPUT_VOLTAGE]; + else if (model < 2000) + tmp = 1.37 * raw_data[OUTPUT_VOLTAGE]; + else + tmp = 1.4 * raw_data[OUTPUT_VOLTAGE]; + } else { + if (model <= 625) + tmp = 1.635 * raw_data[OUTPUT_VOLTAGE] / 2.0; + else if (model < 2000) + tmp = 1.392 * raw_data[OUTPUT_VOLTAGE] / 2.0; + else + tmp = 1.392 * raw_data[OUTPUT_VOLTAGE] / 2.0; } } } else { - rdatax=datax[raw_data[MODELNUMBER]/16]; - rdatay=datay[raw_data[MODELNUMBER]/16]; - rdataz=dataz[raw_data[MODELNUMBER]/16]; - boostdata=1.0+statAVR*20.0/135.0; - t=raw_data[OUTPUT_FREQUENCY]/2; - tmp=0; - if (model>625){ + /* FIXME: miss test "if ((iUPS == 1) && (T != 0))" */ + if (linevoltage < 200) { + rdatax = datax1[raw_data[MODELNUMBER]/16]; + rdatay = datay1[raw_data[MODELNUMBER]/16]; + rdataz = dataz1[raw_data[MODELNUMBER]/16]; + } else { + rdatax = datax2[raw_data[MODELNUMBER]/16]; + rdatay = datay2[raw_data[MODELNUMBER]/16]; + rdataz = dataz2[raw_data[MODELNUMBER]/16+1]; + } + + boostdata = 1.0 + statAVR * 20.0 / 135.0; + t = raw_data[OUTPUT_FREQUENCY]/2; + tmp = 0; + if (model > 625){ tmp=(raw_data[BATTERY_CHARGE]*rdatax)*(raw_data[BATTERY_CHARGE]*rdatax)* (t-raw_data[OUTPUT_VOLTAGE])/t; if (tmp>0) - tmp=sqrt(tmp)*rdatay*boostdata-raw_data[UPS_LOAD]*rdataz*boostdata; + /* Casts below try to avoid potential multiplication overflow */ + tmp=(float)( (double)sqrt(tmp)*rdatay*boostdata - + (double)raw_data[UPS_LOAD]*rdataz*boostdata ); } else { tmp=(raw_data[BATTERY_CHARGE]*rdatax-raw_data[UPS_LOAD]*rdataz)* (raw_data[BATTERY_CHARGE]*rdatax-raw_data[UPS_LOAD]*rdataz)* @@ -490,8 +592,9 @@ static float output_voltage(void) if (tmp>0) tmp=sqrt(tmp)*rdatay; } + /* FIXME: may miss a last processing with ErrorVal = 5 | 10 */ } - } else if ( !strcmp(types[type].name, "IMP")) { + } else if ( !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) { tmp=raw_data[OUTPUT_VOLTAGE]*2.0; } else { tmp= linevoltage >= 220 ? @@ -508,24 +611,24 @@ static float input_freq(void) { if ( !strcmp(types[type].name, "BNT") || !strcmp(types[type].name, "KIN")) return 4807.0/raw_data[INPUT_FREQUENCY]; - else if ( !strcmp(types[type].name, "IMP")) + else if ( !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) return raw_data[INPUT_FREQUENCY]; - return raw_data[INPUT_FREQUENCY] ? + return raw_data[INPUT_FREQUENCY] ? 1.0 / (types[type].freq[0] * - raw_data[INPUT_FREQUENCY] + - types[type].freq[1]) : 0; + raw_data[INPUT_FREQUENCY] + + types[type].freq[1]) : 0; } static float output_freq(void) { if ( !strcmp(types[type].name, "BNT") || !strcmp(types[type].name, "KIN")) return 4807.0/raw_data[OUTPUT_FREQUENCY]; - else if ( !strcmp(types[type].name, "IMP")) + else if ( !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) return raw_data[OUTPUT_FREQUENCY]; - return raw_data[OUTPUT_FREQUENCY] ? + return raw_data[OUTPUT_FREQUENCY] ? 1.0 / (types[type].freq[0] * - raw_data[OUTPUT_FREQUENCY] + - types[type].freq[1]) : 0; + raw_data[OUTPUT_FREQUENCY] + + types[type].freq[1]) : 0; } static float load_level(void) @@ -548,7 +651,7 @@ static float load_level(void) int load801i[]={1,1,1,1,1,1,1,1,44,42,40}; int load1000i[]={1,1,1,1,1,1,1,1,56,54,52}; int load1200i[]={1,1,1,1,1,1,1,1,76,74,72}; - + if ( !strcmp(types[type].name, "BNT") && raw_data[MODELNUMBER]%16 > 7 ) { statINV=raw_data[STATUS_A] & ONLINE; voltage=raw_data[MODELNUMBER]%16; @@ -582,18 +685,18 @@ static float load_level(void) if (model==525) return raw_data[UPS_LOAD]*110.0/load525[voltage]; if (model==625) return raw_data[UPS_LOAD]*110.0/load625[voltage]; if (model<2000) return raw_data[UPS_LOAD]*1.13; - if (model>=2000) return raw_data[UPS_LOAD]*110.0/load2k[voltage]; + return raw_data[UPS_LOAD]*110.0/load2k[voltage]; } else { if (model==425) return raw_data[UPS_LOAD]*110.0/load425i[voltage]; if (model==525) return raw_data[UPS_LOAD]*110.0/load525i[voltage]; if (model==625) return raw_data[UPS_LOAD]*110.0/load625i[voltage]; if (model<2000) return raw_data[UPS_LOAD]*1.66; - if (model>=2000) return raw_data[UPS_LOAD]*110.0/load2ki[voltage]; + return raw_data[UPS_LOAD]*110.0/load2ki[voltage]; } - } else if ( !strcmp(types[type].name, "IMP")) { + } else if ( !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) { return raw_data[UPS_LOAD]; } - return raw_data[STATUS_A] & MAINS_FAILURE ? + return (raw_data[STATUS_A] & MAINS_FAILURE) ? types[type].loadpct[0] * raw_data[UPS_LOAD] + types[type].loadpct[1] : types[type].loadpct[2] * raw_data[UPS_LOAD] + @@ -602,9 +705,10 @@ static float load_level(void) static float batt_level(void) { - int bat0,bat29,bat100,model; + int bat0,bat29,bat100; + unsigned int model; float battval; - + if ( !strcmp(types[type].name, "BNT") ) { bat0=157; bat29=165; @@ -612,9 +716,9 @@ static float batt_level(void) battval=(raw_data[UPS_LOAD])/4+raw_data[BATTERY_CHARGE]; if (battval<=bat0) return 0.0; - if (battval>bat0 && battval<=bat29) + if (battval<=bat29) return (battval-bat0)*30.0/(bat29-bat0); - if (battval>bat29 && battval<=bat100) + if (battval<=bat100) return 30.0+(battval-bat29)*70.0/(bat100-bat29); return 100.0; } @@ -646,9 +750,9 @@ static float batt_level(void) return 30.0+(battval-bat29)*70.0/(bat100-bat29); return 100; } - if ( !strcmp(types[type].name, "IMP")) + if ( !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) return raw_data[BATTERY_CHARGE]; - return raw_data[STATUS_A] & ONLINE ? /* Are we on battery power? */ + return (raw_data[STATUS_A] & ONLINE) ? /* Are we on battery power? */ /* Yes */ types[type].battpct[0] * raw_data[BATTERY_CHARGE] + types[type].battpct[1] * load_level() + types[type].battpct[2] : @@ -665,11 +769,11 @@ static float batt_level(void) void upsdrv_updateinfo(void) { char val[32]; - + if (!ups_getinfo()){ return; } - + /* input.frequency */ upsdebugx(3, "input.frequency (raw data): [raw: %u]", raw_data[INPUT_FREQUENCY]); @@ -682,7 +786,7 @@ void upsdrv_updateinfo(void) dstate_setinfo("output.frequency", "%02.2f", output_freq()); upsdebugx(2, "output.frequency: %s", dstate_getinfo("output.frequency")); - /* ups.load */ + /* ups.load */ upsdebugx(3, "ups.load (raw data): [raw: %u]", raw_data[UPS_LOAD]); dstate_setinfo("ups.load", "%03.1f", load_level()); @@ -694,23 +798,23 @@ void upsdrv_updateinfo(void) dstate_setinfo("battery.charge", "%03.1f", batt_level()); upsdebugx(2, "battery.charge: %s", dstate_getinfo("battery.charge")); - /* input.voltage */ + /* input.voltage */ upsdebugx(3, "input.voltage (raw data): [raw: %u]", raw_data[INPUT_VOLTAGE]); dstate_setinfo("input.voltage", "%03.1f",input_voltage()); upsdebugx(2, "input.voltage: %s", dstate_getinfo("input.voltage")); - - /* output.voltage */ + + /* output.voltage */ upsdebugx(3, "output.voltage (raw data): [raw: %u]", raw_data[OUTPUT_VOLTAGE]); dstate_setinfo("output.voltage", "%03.1f",output_voltage()); upsdebugx(2, "output.voltage: %s", dstate_getinfo("output.voltage")); status_init(); - + *val = 0; if (!(raw_data[STATUS_A] & MAINS_FAILURE)) { - !(raw_data[STATUS_A] & OFF) ? + !(raw_data[STATUS_A] & OFF) ? status_set("OL") : status_set("OFF"); } else { status_set("OB"); @@ -719,7 +823,7 @@ void upsdrv_updateinfo(void) if (raw_data[STATUS_A] & LOW_BAT) status_set("LB"); if (raw_data[STATUS_A] & AVR_ON) { - input_voltage() < linevoltage ? + input_voltage() < linevoltage ? status_set("BOOST") : status_set("TRIM"); } @@ -736,6 +840,9 @@ void upsdrv_updateinfo(void) } /* shutdown UPS */ +void upsdrv_shutdown(void) + __attribute__((noreturn)); + void upsdrv_shutdown(void) { /* power down the attached load immediately */ @@ -746,71 +853,72 @@ void upsdrv_shutdown(void) /* initialize UPS */ void upsdrv_initups(void) { - int tmp,model = 0; + int tmp; + unsigned int model = 0; unsigned int i; static char buf[20]; /* check manufacturer name from arguments */ - if (getval("manufacturer") != NULL) + if (testvar("manufacturer")) manufacturer = getval("manufacturer"); - + /* check model name from arguments */ - if (getval("modelname") != NULL) + if (testvar("modelname")) modelname = getval("modelname"); - + /* check serial number from arguments */ - if (getval("serialnumber") != NULL) + if (testvar("serialnumber")) serialnumber = getval("serialnumber"); - + /* get and check type */ - if (getval("type") != NULL) { - for (i = 0; + if (testvar("type")) { + for (i = 0; i < NUM_OF_SUBTYPES && strcmp(types[i].name, getval("type")); i++) ; if (i >= NUM_OF_SUBTYPES) { printf("Given UPS type '%s' isn't valid!\n", getval("type")); exit (1); } - type = i; - }; - + type = i; + } + /* check line voltage from arguments */ - if (getval("linevoltage") != NULL) { + if (testvar("linevoltage")) { tmp = atoi(getval("linevoltage")); if (! ( (tmp >= 200 && tmp <= 240) || (tmp >= 100 && tmp <= 120) ) ) { printf("Given line voltage '%d' is out of range (100-120 or 200-240 V)\n", tmp); exit (1); - }; + } linevoltage = (unsigned int) tmp; - }; + } - if (getval("numOfBytesFromUPS") != NULL) { + if (testvar("numOfBytesFromUPS")) { tmp = atoi(getval("numOfBytesFromUPS")); if (! (tmp > 0 && tmp <= MAX_NUM_OF_BYTES_FROM_UPS) ) { printf("Given numOfBytesFromUPS '%d' is out of range (1 to %d)\n", - tmp, MAX_NUM_OF_BYTES_FROM_UPS); + tmp, MAX_NUM_OF_BYTES_FROM_UPS); exit (1); - }; + } types[type].num_of_bytes_from_ups = (unsigned char) tmp; } - if (getval("methodOfFlowControl") != NULL) { - for (i = 0; - i < NUM_OF_SUBTYPES && + if (testvar("methodOfFlowControl")) { + for (i = 0; + i < NUM_OF_SUBTYPES && strcmp(types[i].flowControl.name, getval("methodOfFlowControl")); i++) ; if (i >= NUM_OF_SUBTYPES) { - printf("Given methodOfFlowControl '%s' isn't valid!\n", + printf("Given methodOfFlowControl '%s' isn't valid!\n", getval("methodOfFlowControl")); exit (1); - }; - types[type].flowControl = types[i].flowControl; + } + types[type].flowControl = types[i].flowControl; } - if (getval("validationSequence") && - sscanf(getval("validationSequence"), - "{{%u,%x},{%u,%x},{%u,%x}}", + if (testvar("validationSequence") && + sscanf(getval("validationSequence"), + "{{%u,%x},{%u,%x},{%u,%x}}", &types[type].validation[0].index_of_byte, &types[type].validation[0].required_value, &types[type].validation[1].index_of_byte, @@ -819,58 +927,60 @@ void upsdrv_initups(void) &types[type].validation[2].required_value ) < 6 ) { - printf("Given validationSequence '%s' isn't valid!\n", + printf("Given validationSequence '%s' isn't valid!\n", getval("validationSequence")); exit (1); } - if (getval("shutdownArguments") && - sscanf(getval("shutdownArguments"), "{{%u,%u},%c}", + /* NOTE: %hhu is not supported before C99; that would need reading + * arguments into an uint as %u, checking range and casting */ + if (testvar("shutdownArguments") && + sscanf(getval("shutdownArguments"), "{{%hhu,%hhu},%c}", &types[type].shutdown_arguments.delay[0], &types[type].shutdown_arguments.delay[1], - &types[type].shutdown_arguments.minutesShouldBeUsed + &types[type].shutdown_arguments.minutesShouldBeUsed ) < 3 ) { - printf("Given shutdownArguments '%s' isn't valid!\n", + printf("Given shutdownArguments '%s' isn't valid!\n", getval("shutdownArguments")); exit (1); - } + } - if (getval("frequency") && - sscanf(getval("frequency"), "{%f,%f}", - &types[type].freq[0], &types[type].freq[1] + if (testvar("frequency") && + sscanf(getval("frequency"), "{%f,%f}", + &types[type].freq[0], &types[type].freq[1] ) < 2 ) { - printf("Given frequency '%s' isn't valid!\n", + printf("Given frequency '%s' isn't valid!\n", getval("frequency")); exit (1); } - if (getval("loadPercentage") && - sscanf(getval("loadPercentage"), "{%f,%f,%f,%f}", + if (testvar("loadPercentage") && + sscanf(getval("loadPercentage"), "{%f,%f,%f,%f}", &types[type].loadpct[0], &types[type].loadpct[1], &types[type].loadpct[2], &types[type].loadpct[3] ) < 4 ) { - printf("Given loadPercentage '%s' isn't valid!\n", + printf("Given loadPercentage '%s' isn't valid!\n", getval("loadPercentage")); exit (1); } - if (getval("batteryPercentage") && - sscanf(getval("batteryPercentage"), "{%f,%f,%f,%f,%f}", + if (testvar("batteryPercentage") && + sscanf(getval("batteryPercentage"), "{%f,%f,%f,%f,%f}", &types[type].battpct[0], &types[type].battpct[1], &types[type].battpct[2], &types[type].battpct[3], &types[type].battpct[4] ) < 5 ) { - printf("Given batteryPercentage '%s' isn't valid!\n", + printf("Given batteryPercentage '%s' isn't valid!\n", getval("batteryPercentage")); exit (1); } - if (getval("voltage") && - sscanf(getval("voltage"), "{%f,%f,%f,%f}", + if (testvar("voltage") && + sscanf(getval("voltage"), "{%f,%f,%f,%f}", &types[type].voltage[0], &types[type].voltage[1], &types[type].voltage[2], &types[type].voltage[3] ) < 4 @@ -882,23 +992,33 @@ void upsdrv_initups(void) /* open serial port */ upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B1200); - + /* setup flow control */ types[type].flowControl.setup_flow_control(); /* Setup Model and LineVoltage */ - if (!strncmp(types[type].name, "BNT",3) || !strcmp(types[type].name, "KIN") || !strcmp(types[type].name, "IMP")){ + if (!strncmp(types[type].name, "BNT",3) || !strcmp(types[type].name, "KIN") || !strcmp(types[type].name, "IMP") || !strcmp(types[type].name, "OPTI")) { if (!ups_getinfo()) return; /* Give "BNT-other" a chance! */ - if (raw_data[MODELNAME]==0x42 || raw_data[MODELNAME]==0x4B){ - model=BNTmodels[raw_data[MODELNUMBER]/16]; - if (!strcmp(types[type].name, "BNT-other")) - types[type].name="BNT-other"; - else if (raw_data[MODELNAME]==0x42) - types[type].name="BNT"; - else if (raw_data[MODELNAME]==0x4B){ - types[type].name="KIN"; - model=KINmodels[raw_data[MODELNUMBER]/16]; + if (raw_data[MODELNAME]==0x42 || raw_data[MODELNAME]==0x4B || raw_data[MODELNAME]==0x4F){ + /* Give "IMP" a chance also! */ + if (raw_data[UPSVERSION]==0xFF){ + types[type].name="IMP"; + model=IMPmodels[raw_data[MODELNUMBER]/16]; + } + else { + model=BNTmodels[raw_data[MODELNUMBER]/16]; + if (!strcmp(types[type].name, "BNT-other")) + types[type].name="BNT-other"; + else if (raw_data[MODELNAME]==0x42) + types[type].name="BNT"; + else if (raw_data[MODELNAME]==0x4B){ + types[type].name="KIN"; + model=KINmodels[raw_data[MODELNUMBER]/16]; + } else if (raw_data[MODELNAME]==0x4F){ + types[type].name="OPTI"; + model=OPTImodels[raw_data[MODELNUMBER]/16]; + } } } else if (raw_data[UPSVERSION]==0xFF){ @@ -906,52 +1026,62 @@ void upsdrv_initups(void) model=IMPmodels[raw_data[MODELNUMBER]/16]; } linevoltage=voltages[raw_data[MODELNUMBER]%16]; - snprintf(buf,sizeof(buf),"%s-%dAP",types[type].name,model); + if (!strcmp(types[type].name, "OPTI")) { + snprintf(buf,sizeof(buf),"%s-%u",types[type].name, model); + } else { + snprintf(buf,sizeof(buf),"%s-%uAP",types[type].name, model); + } if (!strcmp(modelname, "Unknown")) modelname=buf; upsdebugx(1,"Detected: %s , %dV",buf,linevoltage); - if (ser_send_char (upsfd, BATTERY_TEST) != 1) { - upslogx(LOG_NOTICE, "writing error"); - dstate_datastale(); - return; + if (testvar("nobt") || dstate_getinfo("driver.flag.nobt")) { + upslogx(LOG_NOTICE, "nobt flag set, skipping battery test as requested"); + } + else { + upslogx(LOG_NOTICE, "nobt flag not set, performing battery test as requested"); + if (ser_send_char (upsfd, BATTERY_TEST) != 1) { + upslogx(LOG_NOTICE, "Write error: failed to send battery test command to UPS!"); + dstate_datastale(); + return; + } } } - + upsdebugx(1, "Values of arguments:"); upsdebugx(1, " manufacturer : '%s'", manufacturer); upsdebugx(1, " model name : '%s'", modelname); upsdebugx(1, " serial number : '%s'", serialnumber); upsdebugx(1, " line voltage : '%u'", linevoltage); upsdebugx(1, " type : '%s'", types[type].name); - upsdebugx(1, " number of bytes from UPS: '%u'", - types[type].num_of_bytes_from_ups); - upsdebugx(1, " method of flow control : '%s'", - types[type].flowControl.name); + upsdebugx(1, " number of bytes from UPS: '%u'", + types[type].num_of_bytes_from_ups); + upsdebugx(1, " method of flow control : '%s'", + types[type].flowControl.name); upsdebugx(1, " validation sequence: '{{%u,%#x},{%u,%#x},{%u,%#x}}'", - types[type].validation[0].index_of_byte, - types[type].validation[0].required_value, - types[type].validation[1].index_of_byte, - types[type].validation[1].required_value, - types[type].validation[2].index_of_byte, - types[type].validation[2].required_value); + types[type].validation[0].index_of_byte, + types[type].validation[0].required_value, + types[type].validation[1].index_of_byte, + types[type].validation[1].required_value, + types[type].validation[2].index_of_byte, + types[type].validation[2].required_value); upsdebugx(1, " shutdown arguments: '{{%u,%u},%c}'", - types[type].shutdown_arguments.delay[0], - types[type].shutdown_arguments.delay[1], - types[type].shutdown_arguments.minutesShouldBeUsed); + types[type].shutdown_arguments.delay[0], + types[type].shutdown_arguments.delay[1], + types[type].shutdown_arguments.minutesShouldBeUsed); if ( strcmp(types[type].name, "KIN") && strcmp(types[type].name, "BNT") && strcmp(types[type].name, "IMP")) { upsdebugx(1, " frequency calculation coefficients: '{%f,%f}'", - types[type].freq[0], types[type].freq[1]); + types[type].freq[0], types[type].freq[1]); upsdebugx(1, " load percentage calculation coefficients: " - "'{%f,%f,%f,%f}'", - types[type].loadpct[0], types[type].loadpct[1], - types[type].loadpct[2], types[type].loadpct[3]); - upsdebugx(1, " battery percentage calculation coefficients: " - "'{%f,%f,%f,%f,%f}'", - types[type].battpct[0], types[type].battpct[1], - types[type].battpct[2], types[type].battpct[3], - types[type].battpct[4]); + "'{%f,%f,%f,%f}'", + types[type].loadpct[0], types[type].loadpct[1], + types[type].loadpct[2], types[type].loadpct[3]); + upsdebugx(1, " battery percentage calculation coefficients: " + "'{%f,%f,%f,%f,%f}'", + types[type].battpct[0], types[type].battpct[1], + types[type].battpct[2], types[type].battpct[3], + types[type].battpct[4]); upsdebugx(1, " voltage calculation coefficients: '{%f,%f}'", - types[type].voltage[2], types[type].voltage[3]); + types[type].voltage[2], types[type].voltage[3]); } } @@ -959,12 +1089,12 @@ void upsdrv_initups(void) /* display help */ void upsdrv_help(void) { - // 1 2 3 4 5 6 7 8 - // 12345678901234567890123456789012345678901234567890123456789012345678901234567890 MAX + /* 1 2 3 4 5 6 7 8 */ + /* 12345678901234567890123456789012345678901234567890123456789012345678901234567890 MAX */ printf("\n"); printf("Specify UPS information in the ups.conf file.\n"); printf(" type: Type of UPS: 'Trust','Egys','KP625AP','IMP','KIN','BNT',\n"); - printf(" 'BNT-other' (default: 'Trust')\n"); + printf(" 'BNT-other', 'OPTI' (default: 'Trust')\n"); printf(" 'BNT-other' is a special type intended for BNT 100-120V models,\n"); printf(" but can be used to override ALL models.\n"); printf("You can additional specify these variables:\n"); @@ -998,7 +1128,8 @@ void upsdrv_help(void) printf(" voltage: Voltage conversion values for 240 and 120 voltage:\n"); printf(" {240A,240B,120A,120B}\n"); printf(" used in function: A*x+B\n"); - printf(" If the raw value x IS HALF the Voltage, then A=2, B=0\n\n"); + printf(" If the raw value x IS HALF the Voltage, then A=2, B=0\n"); + printf(" nobt: Flag to skip battery check on init/startup.\n\n"); printf("Example for BNT1500AP in ups.conf:\n"); printf("[BNT1500AP]\n"); @@ -1018,6 +1149,7 @@ void upsdrv_help(void) printf("# loadPercentage = {1.0000,0.0,1.0000,0.0}\n"); printf("# batteryPercentage = {1.0000,0.0000,0.0000,1.0000,0.0000}\n"); printf("# voltage = {2.0000,0.0000,2.0000,0.0000}\n"); + printf(" nobt\n"); return; } @@ -1032,19 +1164,19 @@ void upsdrv_initinfo(void) dstate_setinfo ("input.voltage.nominal", "%u", linevoltage); /* now add the instant commands */ - dstate_addcmd ("test.battery.start"); - dstate_addcmd ("shutdown.return"); - dstate_addcmd ("shutdown.stayoff"); + dstate_addcmd ("test.battery.start"); + dstate_addcmd ("shutdown.return"); + dstate_addcmd ("shutdown.stayoff"); upsh.instcmd = instcmd; } /* define possible arguments */ void upsdrv_makevartable(void) { - // 1 2 3 4 5 6 7 8 - //2345678901234567890123456789012345678901234567890123456789012345678901234567890 MAX + /* 1 2 3 4 5 6 7 8 */ + /*2345678901234567890123456789012345678901234567890123456789012345678901234567890 MAX */ addvar(VAR_VALUE, "type", - "Type of UPS: 'Trust','Egys','KP625AP','IMP','KIN','BNT','BNT-other'\n" + "Type of UPS: 'Trust','Egys','KP625AP','IMP','KIN','BNT','BNT-other','OPTI'\n" " (default: 'Trust')"); addvar(VAR_VALUE, "manufacturer", "Manufacturer name (default: 'PowerCom')"); @@ -1071,6 +1203,8 @@ void upsdrv_makevartable(void) "Battery conversion values: OffFactor, LoadFactor, OffConst, OnFactor, OnConst"); addvar(VAR_VALUE, "voltage", "Voltage conversion values: 240VFactor, 240VConst, 120VFactor, 120VConst"); + addvar(VAR_FLAG, "nobt", + "Disable battery test at driver init/startup"); } } diff --git a/drivers/powercom.h b/drivers/powercom.h index 3286860..4ac2f63 100644 --- a/drivers/powercom.h +++ b/drivers/powercom.h @@ -1,8 +1,6 @@ /* * powercom.h - defines for the newpowercom.c driver * - * $Id: powercom.h 2984 2011-05-13 13:18:34Z aquette $ - * * Copyrights: * (C) 2002 Simon Rozman * (C) 1999 Peter Bieringer @@ -23,15 +21,18 @@ * */ +#ifndef NUT_POWERCOM_H_SEEN +#define NUT_POWERCOM_H_SEEN 1 + /* C-libary includes */ #include #include #include #include -#include "serial.h" -#include /* nut includes */ +#include "serial.h" +#include "nut_stdint.h" #include "timehead.h" @@ -39,51 +40,51 @@ struct type { const char *name; unsigned char num_of_bytes_from_ups; - + struct method_of_flow_control { - const char *name; - void (*setup_flow_control)(void); + const char *name; + void (*setup_flow_control)(void); } flowControl; - + struct validation_byte { - unsigned int index_of_byte, required_value; - /* An example might explain the intention better then prose. - * Suppose we want to validate the data with: - * powercom_raw_data[5] == 0x80 - * then we will set index_of_byte to 5U and required_value to - * 0x80U: { 5U, 0x80U }. - */ + unsigned int index_of_byte, required_value; + /* An example might explain the intention better then prose. + * Suppose we want to validate the data with: + * powercom_raw_data[5] == 0x80 + * then we will set index_of_byte to 5U and required_value to + * 0x80U: { 5U, 0x80U }. + */ } validation[3]; - /* The validation array is of length 3 because 3 is longest + /* The validation array is of length 3 because 3 is longest * validation sequence for any type. */ - - /* Some UPSs must have a minutes and a seconds arguments for + + /* Some UPSs must have a minutes and a seconds arguments for * the COUNTER commands while others are known to work with the * seconds argument alone. */ struct delay_for_power_kill { - unsigned int delay[2]; /* { minutes, seconds } */ - unsigned char minutesShouldBeUsed; - /* 'n' in case the minutes value, which is delay[0], should - * be skipped and not sent to the UPS. + unsigned char delay[2]; /* { minutes, seconds } */ + unsigned char minutesShouldBeUsed; + /* 'n' in case the minutes value, which is delay[0], should + * be skipped and not sent to the UPS. */ } shutdown_arguments; - + /* parameters to calculate input and output freq., one pair used for * both input and output functions: * The pair [0],[1] defines parameters for 1/(A*x+B) to calculate freq. * from raw data 'x'. */ float freq[2]; - + /* parameters to calculate load %, two pairs for each type: * First pair [0],[1] defines the parameters for A*x+B to calculate load * from raw data when offline and the second pair [2],[3] is used when * online */ float loadpct[4]; - + /* parameters to calculate battery %, five parameters for each type: * First three params [0],[1],[2] defines the parameters for A*x+B*y+C to calculate * battery % (x is raw data, y is load %) when offline. @@ -99,3 +100,5 @@ struct type { */ float voltage[4]; }; + +#endif /* NUT_POWERCOM_H_SEEN */ diff --git a/drivers/powerman-pdu.c b/drivers/powerman-pdu.c index e12340a..df4e91f 100644 --- a/drivers/powerman-pdu.c +++ b/drivers/powerman-pdu.c @@ -20,10 +20,10 @@ #include "main.h" -#include +#include /* pm_err_t and other beasts */ #define DRIVER_NAME "Powerman PDU client driver" -#define DRIVER_VERSION "0.11" +#define DRIVER_VERSION "0.12" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -35,11 +35,11 @@ upsdrv_info_t upsdrv_info = { }; /* Powerman functions and variables */ -static pm_err_t query_one(pm_handle_t pm, char *s, int mode); -static pm_err_t query_all(pm_handle_t pm, int mode); +static pm_err_t query_one(pm_handle_t arg_pm, char *s, int mode); +static pm_err_t query_all(pm_handle_t arg_pm, int mode); -pm_handle_t pm; -char ebuf[64]; +static pm_handle_t pm; +static char ebuf[64]; /* modes to snmp_ups_walk. */ #define WALKMODE_INIT 0 @@ -49,7 +49,7 @@ static int reconnect_ups(void); static int instcmd(const char *cmdname, const char *extra) { - pm_err_t rv = -1; + pm_err_t rv = PM_EBADARG; char *cmdsuffix = NULL; char *cmdindex = NULL; char outletname[SMALLBUF]; @@ -90,7 +90,7 @@ static int instcmd(const char *cmdname, const char *extra) return (rv==PM_ESUCCESS)?STAT_INSTCMD_HANDLED:STAT_SET_INVALID; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -133,6 +133,9 @@ void upsdrv_initinfo(void) /* FIXME: no need for setvar (ex for outlet.n.delay.*)!? */ } +void upsdrv_shutdown(void) + __attribute__((noreturn)); + void upsdrv_shutdown(void) { /* FIXME: shutdown all outlets? */ @@ -188,9 +191,9 @@ static int reconnect_ups(void) { pm_err_t rv; - upsdebugx(4, "=================================================="); - upsdebugx(4, "= connexion lost with Powerman, try to reconnect ="); - upsdebugx(4, "=================================================="); + upsdebugx(4, "==================================================="); + upsdebugx(4, "= connection lost with Powerman, try to reconnect ="); + upsdebugx(4, "==================================================="); /* clear the situation */ pm_disconnect(pm); @@ -199,7 +202,7 @@ static int reconnect_ups(void) if ((rv = pm_connect(device_path, NULL, &pm, 0)) != PM_ESUCCESS) return 0; else { - upsdebugx(4, "connexion restored with Powerman"); + upsdebugx(4, "connection restored with Powerman"); return 1; } } @@ -208,7 +211,7 @@ static int reconnect_ups(void) * powerman support functions ****************************/ -static pm_err_t query_one(pm_handle_t pm, char *s, int outletnum) +static pm_err_t query_one(pm_handle_t arg_pm, char *s, int outletnum) { pm_err_t rv; pm_node_state_t ns; @@ -216,7 +219,7 @@ static pm_err_t query_one(pm_handle_t pm, char *s, int outletnum) upsdebugx(1, "entering query_one (%s)", s); - rv = pm_node_status(pm, s, &ns); + rv = pm_node_status(arg_pm, s, &ns); if (rv == PM_ESUCCESS) { upsdebugx(3, "updating status"); @@ -229,7 +232,7 @@ static pm_err_t query_one(pm_handle_t pm, char *s, int outletnum) return rv; } -static pm_err_t query_all(pm_handle_t pm, int mode) +static pm_err_t query_all(pm_handle_t arg_pm, int mode) { pm_err_t rv; pm_node_iterator_t itr; @@ -239,7 +242,7 @@ static pm_err_t query_all(pm_handle_t pm, int mode) upsdebugx(1, "entering query_all ()"); - rv = pm_node_iterator_create(pm, &itr); + rv = pm_node_iterator_create(arg_pm, &itr); if (rv != PM_ESUCCESS) return rv; @@ -247,7 +250,7 @@ static pm_err_t query_all(pm_handle_t pm, int mode) /* in WALKMODE_UPDATE, we always call this one for the * status update... */ - if ((rv = query_one(pm, s, outletnum)) != PM_ESUCCESS) + if ((rv = query_one(arg_pm, s, outletnum)) != PM_ESUCCESS) break; else { /* set the initial generic properties (ie except status) diff --git a/drivers/powerp-bin.c b/drivers/powerp-bin.c index b128369..89b0687 100644 --- a/drivers/powerp-bin.c +++ b/drivers/powerp-bin.c @@ -1,6 +1,6 @@ /* * powerp-bin.c - Model specific routines for CyberPower binary - * protocol UPSes + * protocol UPSes * * Copyright (C) * 2007 Doug Reynolds @@ -31,9 +31,12 @@ #include "serial.h" #include "powerp-bin.h" +#include "nut_stdint.h" #include +#define POWERPANEL_BIN_VERSION "Powerpanel-Binary 0.5" + typedef struct { unsigned char start; unsigned char i_volt; @@ -64,34 +67,34 @@ static unsigned char powpan_answer[SMALLBUF]; static const valtab_t tran_high_pr[] = { { "138", -9 }, { "139", -8 }, { "140", -7 }, { "141", -6 }, { "142", -5 }, { "143", -4 }, { "144", -3 }, { "145", -2 }, { "146", -1 }, { "147", 0 }, - { NULL } + { NULL, 0 } }; /* OP series */ static const valtab_t tran_high_op[] = { { "140", -5 }, { "141", -4 }, { "142", -3 }, { "143", -2 }, { "144", -1 }, { "145", 0 }, { "146", +1 }, { "147", +2 }, { "148", +3 }, { "149", +4 }, - { "150", +5 }, { NULL } + { "150", +5 }, { NULL, 0 } }; /* PR series */ static const valtab_t tran_low_pr[] = { { "88", 0 }, { "89", +1 }, { "90", +2 }, { "91", +3 }, { "92", +4 }, { "93", +5 }, { "94", +6 }, { "95", +7 }, { "96", +8 }, { "97", +9 }, - { NULL } + { NULL, 0 } }; /* OP series */ static const valtab_t tran_low_op[] = { { "85", -5 }, { "86", -4 }, { "87", -3 }, { "88", -2 }, { "89", -1 }, { "90", 0 }, { "91", +1 }, { "92", +2 }, { "93", +3 }, { "94", +4 }, - { "95", +5 }, { NULL } + { "95", +5 }, { NULL, 0 } }; /* PR series */ static const valtab_t batt_low_pr[] = { { "25", -6 }, { "30", -5 }, { "35", -3 }, { "40", -1 }, { "45", 0 }, - { "50", +2 }, { "55", +4 }, { "60", +6 }, { NULL } + { "50", +2 }, { "55", +4 }, { "60", +6 }, { NULL, 0 } }; /* OP series */ @@ -99,23 +102,23 @@ static const valtab_t batt_low_op[] = { { "15", -8 }, { "18", -7 }, { "19", -6 }, { "20", -5 }, { "22", -4 }, { "24", -3 }, { "25", -2 }, { "26", -1 }, { "28", 0 }, { "30", +1 }, { "32", +2 }, { "34", +3 }, { "35", +4 }, { "36", +5 }, { "38", +6 }, - { "40", +7 }, { NULL } + { "40", +7 }, { NULL, 0 } }; /* PR series */ static const valtab_t out_volt_pr[] = { - { "110", -10 }, { "120", 0 }, { "130", +10 }, { NULL } + { "110", -10 }, { "120", 0 }, { "130", +10 }, { NULL, 0 } }; /* OP series */ static const valtab_t out_volt_op[] = { { "110", -2 }, { "115", -1 }, { "120", 0 }, { "124", +1 }, { "128", +2 }, - { "130", +3 }, { NULL } + { "130", +3 }, { NULL, 0 } }; static const valtab_t yes_no_info[] = { { "yes", 2 }, { "no", 0 }, - { NULL } + { NULL, 0 } }; /* Older models report the model in a numeric format 'rOnn' */ @@ -133,7 +136,7 @@ static const struct { { "rO41", "OP700AVR" }, { "rO43", "OP1250AVR" }, { "rO45", "OP1500AVR" }, - { NULL } + { NULL, NULL } }; static const struct { @@ -147,13 +150,13 @@ static const struct { { "battery.charge.low", "R\x08\r", "Q\x08%c\r", { batt_low_pr, batt_low_op } }, { "ups.start.battery", "R\x0F\r", "Q\x0F%c\r", { yes_no_info, yes_no_info } }, { "output.voltage.nominal", "R\x18\r", "Q\x18%c\r", { out_volt_pr, out_volt_op } }, - { NULL } + { NULL, NULL, NULL, { NULL, 0 } } }; static const struct { const char *cmd; const char *command; - const int len; + const size_t len; } cmdtab[] = { { "test.battery.start.quick", "T\230\r", 3 }, /* 20 seconds test */ { "test.battery.stop", "CT\r", 3 }, @@ -163,7 +166,7 @@ static const struct { * as shutdown.return when on battery */ { "shutdown.stayoff", "S\0\0W\r", 5 }, { "shutdown.stop", "C\r", 2 }, - { NULL } + { NULL, NULL, 0 } }; /* map UPS data to (approximated) input/output voltage */ @@ -212,9 +215,9 @@ static float op_freq(unsigned char in) return (12600.0 / (in + 32)); } -static int powpan_command(const char *buf, size_t bufsize) +static ssize_t powpan_command(const char *buf, size_t bufsize) { - int ret; + ssize_t ret; ser_flush_io(upsfd); @@ -248,7 +251,7 @@ static int powpan_command(const char *buf, size_t bufsize) return -1; } - upsdebug_hex(3, "read", powpan_answer, ret); + upsdebug_hex(3, "read", powpan_answer, (size_t)ret); return ret; } @@ -257,18 +260,22 @@ static int powpan_instcmd(const char *cmdname, const char *extra) int i; for (i = 0; cmdtab[i].cmd != NULL; i++) { + ssize_t ret; if (strcasecmp(cmdname, cmdtab[i].cmd)) { continue; } - if ((powpan_command(cmdtab[i].command, cmdtab[i].len) == - cmdtab[i].len - 1) && - (!memcmp(powpan_answer, cmdtab[i].command, cmdtab[i].len - 1))) { + ret = powpan_command(cmdtab[i].command, cmdtab[i].len); + assert(cmdtab[i].len < SSIZE_MAX); + if (ret >= 0 && + (ret == (ssize_t)(cmdtab[i].len - 1)) && + (!memcmp(powpan_answer, cmdtab[i].command, cmdtab[i].len - 1)) + ) { return STAT_INSTCMD_HANDLED; } - upslogx(LOG_ERR, "%s: command [%s] failed", __func__, cmdname); + upslogx(LOG_ERR, "%s: command [%s] [%s] failed", __func__, cmdname, extra); return STAT_INSTCMD_FAILED; } @@ -298,8 +305,20 @@ static int powpan_setvar(const char *varname, const char *val) continue; } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif snprintf(command, sizeof(command), vartab[i].set, vartab[i].map[type][j].command); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif if ((powpan_command(command, 4) == 3) && (!memcmp(powpan_answer, command, 3))) { dstate_setinfo(varname, "%s", val); @@ -341,7 +360,7 @@ static void powpan_initinfo(void) dstate_setinfo("ups.model", "%s", modeltab[i].model); } else { /* report model value as is */ - dstate_setinfo("ups.model", "%s", rtrim(s, ' ')); + dstate_setinfo("ups.model", "%s", str_rtrim(s, ' ')); } } if ((s = strtok(NULL, ".")) != NULL) { @@ -359,7 +378,7 @@ static void powpan_initinfo(void) } for (i = 0; vartab[i].var != NULL; i++) { - + if (powpan_command(vartab[i].get, 3) < 2) { continue; } @@ -373,7 +392,7 @@ static void powpan_initinfo(void) dstate_setinfo(vartab[i].var, "%s", vartab[i].map[type][j].val); break; } - + if (dstate_getinfo(vartab[i].var) == NULL) { upslogx(LOG_WARNING, "warning: [%d] unknown value for [%s]!", powpan_answer[1], vartab[i].var); @@ -397,14 +416,14 @@ static void powpan_initinfo(void) powpan_command("R\x2B\r", 3); powpan_command("R\x3D\r", 3); } - + dstate_addcmd("shutdown.stayoff"); dstate_addcmd("shutdown.reboot"); } -static int powpan_status(status_t *status) +static ssize_t powpan_status(status_t *status) { - int ret; + ssize_t ret; ser_flush_io(upsfd); @@ -443,7 +462,7 @@ static int powpan_status(status_t *status) return -1; } - upsdebug_hex(3, "read", status, ret); + upsdebug_hex(3, "read", status, (size_t)ret); if ((status->flags[0] + status->flags[1]) != 255) { upsdebugx(4, " \\_ : checksum flags[0..1] failed"); @@ -481,7 +500,19 @@ static int powpan_updateinfo(void) dstate_setinfo("input.frequency", "%.1f", op_freq(status.i_freq)); break; + case PR: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif dstate_setinfo("input.voltage", "%d", status.i_volt); if (status.flags[0] & 0x84) { dstate_setinfo("output.voltage", "%s", dstate_getinfo("output.voltage.nominal")); @@ -523,7 +554,7 @@ static int powpan_updateinfo(void) } else if (status.o_volt < 1.05 * status.i_volt) { upsdebugx(2, "%s: appears to be in BYPASS state", __func__); } else if (status.o_volt < 1.5 * status.i_volt) { - status_set("BOOST"); + status_set("BOOST"); } else { upsdebugx(2, "%s: output voltage too high", __func__); } @@ -542,9 +573,10 @@ static int powpan_updateinfo(void) return (status.flags[0] & 0x80) ? 1 : 0; } -static int powpan_initups(void) +static ssize_t powpan_initups(void) { - int ret, i; + ssize_t ret; + int i; upsdebugx(1, "Trying %s protocol...", powpan_binary.version); @@ -593,10 +625,10 @@ static int powpan_initups(void) continue; } - upsdebug_hex(3, "read", powpan_answer, ret); + upsdebug_hex(3, "read", powpan_answer, (size_t)ret); if (ret < 20) { - upsdebugx(2, "Expected 20 bytes but only got %d", ret); + upsdebugx(2, "Expected 20 bytes but only got %zd", ret); continue; } @@ -631,6 +663,7 @@ static int powpan_initups(void) subdriver_t powpan_binary = { "binary", + POWERPANEL_BIN_VERSION, powpan_instcmd, powpan_setvar, powpan_initups, diff --git a/drivers/powerp-bin.h b/drivers/powerp-bin.h index 4346597..0bb1c63 100644 --- a/drivers/powerp-bin.h +++ b/drivers/powerp-bin.h @@ -1,6 +1,6 @@ /* * powerp-bin.h - Model specific data/definitions for CyberPower text/binary - * protocol UPSes + * protocol UPSes * * Copyright (C) * 2007 Doug Reynolds diff --git a/drivers/powerp-txt.c b/drivers/powerp-txt.c index 48897d0..6f22a60 100644 --- a/drivers/powerp-txt.c +++ b/drivers/powerp-txt.c @@ -1,10 +1,11 @@ /* * powerp-txt.c - Model specific routines for CyberPower text - * protocol UPSes + * protocol UPSes * * Copyright (C) * 2007 Doug Reynolds * 2007-2008 Arjen de Korte + * 2012-2016 Timothy Pearson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -32,18 +33,28 @@ #include "powerp-txt.h" +#define POWERPANEL_TEXT_VERSION "Powerpanel-Text 0.5" + typedef struct { - float i_volt; - float o_volt; - int o_load; - int b_chrg; - int u_temp; - float i_freq; - unsigned char flags[2]; + float i_volt; + float o_volt; + int o_load; + int b_chrg; + int u_temp; + float i_freq; + unsigned char flags[2]; + unsigned char has_b_volt; + float b_volt; + unsigned char has_o_freq; + float o_freq; + unsigned char has_runtime; + int runtime; + int c_unknwn; + float q_unknwn; } status_t; -static int ondelay = 1; /* minutes */ -static int offdelay = 60; /* seconds */ +static long ondelay = 1; /* minutes */ +static long offdelay = 60; /* seconds */ static char powpan_answer[SMALLBUF]; @@ -55,7 +66,7 @@ static struct { { "input.transfer.high", "P6\r", "C2:%03d\r" }, { "input.transfer.low", "P7\r", "C3:%03d\r" }, { "battery.charge.low", "P8\r", "C4:%02d\r" }, - { NULL } + { NULL, NULL, NULL } }; static struct { @@ -63,18 +74,19 @@ static struct { const char *command; } cmdtab[] = { { "test.battery.start.quick", "T\r" }, + { "test.battery.start.deep", "TL\r" }, { "test.battery.stop", "CT\r" }, { "beeper.enable", "C7:1\r" }, { "beeper.disable", "C7:0\r" }, { "beeper.on", NULL }, { "beeper.off", NULL }, { "shutdown.stop", "C\r" }, - { NULL } + { NULL, NULL } }; -static int powpan_command(const char *command) +static ssize_t powpan_command(const char *command) { - int ret; + ssize_t ret; ser_flush_io(upsfd); @@ -109,7 +121,7 @@ static int powpan_command(const char *command) return -1; } - upsdebug_hex(3, "read", powpan_answer, ret); + upsdebug_hex(3, "read", powpan_answer, (size_t)ret); return ret; } @@ -138,34 +150,34 @@ static int powpan_instcmd(const char *cmdname, const char *extra) continue; } - if ((powpan_command(cmdtab[i].command) == 2) && (!strcasecmp(powpan_answer, "#0"))) { + if ((powpan_command(cmdtab[i].command) == 2) && (!strcasecmp(powpan_answer, "#0"))) { return STAT_INSTCMD_HANDLED; } - upslogx(LOG_ERR, "%s: command [%s] failed", __func__, cmdname); + upslogx(LOG_ERR, "%s: command [%s] [%s] failed", __func__, cmdname, extra); return STAT_INSTCMD_FAILED; } if (!strcasecmp(cmdname, "shutdown.return")) { if (offdelay < 60) { - snprintf(command, sizeof(command), "Z.%d\r", offdelay / 6); + snprintf(command, sizeof(command), "Z.%ld\r", offdelay / 6); } else { - snprintf(command, sizeof(command), "Z%02d\r", offdelay / 60); + snprintf(command, sizeof(command), "Z%02ld\r", offdelay / 60); } } else if (!strcasecmp(cmdname, "shutdown.stayoff")) { if (offdelay < 60) { - snprintf(command, sizeof(command), "S.%d\r", offdelay / 6); + snprintf(command, sizeof(command), "S.%ld\r", offdelay / 6); } else { - snprintf(command, sizeof(command), "S%02d\r", offdelay / 60); + snprintf(command, sizeof(command), "S%02ld\r", offdelay / 60); } } else if (!strcasecmp(cmdname, "shutdown.reboot")) { if (offdelay < 60) { - snprintf(command, sizeof(command), "S.%dR%04d\r", offdelay / 6, ondelay); + snprintf(command, sizeof(command), "S.%ldR%04ld\r", offdelay / 6, ondelay); } else { - snprintf(command, sizeof(command), "S%02dR%04d\r", offdelay / 60, ondelay); + snprintf(command, sizeof(command), "S%02ldR%04ld\r", offdelay / 60, ondelay); } } else { - upslogx(LOG_NOTICE, "%s: command [%s] unknown", __func__, cmdname); + upslogx(LOG_NOTICE, "%s: command [%s] [%s] unknown", __func__, cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -173,7 +185,7 @@ static int powpan_instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } - upslogx(LOG_ERR, "%s: command [%s] failed", __func__, cmdname); + upslogx(LOG_ERR, "%s: command [%s] [%s] failed", __func__, cmdname, extra); return STAT_INSTCMD_FAILED; } @@ -193,7 +205,19 @@ static int powpan_setvar(const char *varname, const char *val) return STAT_SET_HANDLED; } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif snprintf(command, sizeof(command), vartab[i].set, atoi(val)); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif if ((powpan_command(command) == 2) && (!strcasecmp(powpan_answer, "#0"))) { dstate_setinfo(varname, "%s", val); @@ -213,15 +237,15 @@ static void powpan_initinfo(void) int i; char *s; - dstate_setinfo("ups.delay.start", "%d", 60 * ondelay); - dstate_setinfo("ups.delay.shutdown", "%d", offdelay); + dstate_setinfo("ups.delay.start", "%ld", 60 * ondelay); + dstate_setinfo("ups.delay.shutdown", "%ld", offdelay); /* * NOTE: The reply is already in the buffer, since the P4\r command * was used for autodetection of the UPS. No need to do it again. */ if ((s = strtok(&powpan_answer[1], ",")) != NULL) { - dstate_setinfo("ups.model", "%s", rtrim(s, ' ')); + dstate_setinfo("ups.model", "%s", str_rtrim(s, ' ')); } if ((s = strtok(NULL, ",")) != NULL) { dstate_setinfo("ups.firmware", "%s", s); @@ -230,7 +254,7 @@ static void powpan_initinfo(void) dstate_setinfo("ups.serial", "%s", s); } if ((s = strtok(NULL, ",")) != NULL) { - dstate_setinfo("ups.mfr", "%s", rtrim(s, ' ')); + dstate_setinfo("ups.mfr", "%s", str_rtrim(s, ' ')); } /* @@ -353,15 +377,16 @@ static void powpan_initinfo(void) dstate_addcmd("shutdown.reboot"); } -static int powpan_status(status_t *status) +static ssize_t powpan_status(status_t *status) { - int ret; + ssize_t ret; ser_flush_io(upsfd); /* * WRITE D\r * READ #I119.0O119.0L000B100T027F060.0S..\r + * #I118.0O118.0L029B100F060.0R0218S..\r * 01234567890123456789012345678901234 * 0 1 2 3 */ @@ -395,13 +420,60 @@ static int powpan_status(status_t *status) return -1; } - upsdebug_hex(3, "read", powpan_answer, ret); + upsdebug_hex(3, "read", powpan_answer, (size_t)ret); ret = sscanf(powpan_answer, "#I%fO%fL%dB%dT%dF%fS%2c\r", &status->i_volt, &status->o_volt, &status->o_load, &status->b_chrg, &status->u_temp, &status->i_freq, status->flags); + if (ret >= 7) { + status->has_b_volt = 0; + status->has_o_freq = 0; + status->has_runtime = 0; + } else { + + ret = sscanf(powpan_answer, "#I%fO%fL%dB%dF%fR%dS%2c\r", + &status->i_volt, &status->o_volt, &status->o_load, + &status->b_chrg, &status->i_freq, &status->runtime, + status->flags); + + if (ret >= 7) { + status->has_b_volt = 0; + status->has_o_freq = 0; + status->has_runtime = 1; + } + + } + if (ret < 7) { + ret = ser_get_buf_len(upsfd, powpan_answer+35, 23, SER_WAIT_SEC, SER_WAIT_USEC); + + if (ret < 0) { + upsdebug_with_errno(3, "read"); + upsdebug_hex(4, " \\_", powpan_answer+35, 23); + return -1; + } + + if (ret == 0) { + upsdebugx(3, "read: timeout"); + upsdebug_hex(4, " \\_", powpan_answer+35, 23); + return -1; + } + + upsdebug_hex(3, "read", powpan_answer, (size_t)ret); + + ret = sscanf(powpan_answer, "#I%fO%fL%dB%dV%fT%dF%fH%fR%dC%dQ%fS%2c\r", + &status->i_volt, &status->o_volt, &status->o_load, + &status->b_chrg, &status->b_volt, &status->u_temp, + &status->i_freq, &status->o_freq, &status->runtime, + &status->c_unknwn, &status->q_unknwn, status->flags); + status->has_b_volt = 1; + status->has_o_freq = 1; + status->has_runtime = 1; + dstate_setinfo("battery.voltage.nominal", "%g", 72.0); + dstate_setinfo("output.voltage.nominal", "%g", 120.0); + } + if (ret < 7) { upsdebugx(4, "Parsing status string failed"); return -1; @@ -424,6 +496,15 @@ static int powpan_updateinfo(void) dstate_setinfo("input.frequency", "%.1f", status.i_freq); dstate_setinfo("ups.temperature", "%d", status.u_temp); dstate_setinfo("battery.charge", "%d", status.b_chrg); + if (status.has_b_volt) { + dstate_setinfo("battery.voltage", "%.1f", status.b_volt); + } + if (status.has_o_freq) { + dstate_setinfo("output.frequency", "%.1f", status.o_freq); + } + if (status.has_runtime) { + dstate_setinfo("battery.runtime", "%d", status.runtime*60); + } status_init(); @@ -447,7 +528,7 @@ static int powpan_updateinfo(void) } else if (status.o_volt < 1.05 * status.i_volt) { /* ignore */ } else if (status.o_volt < 1.5 * status.i_volt) { - status_set("BOOST"); + status_set("BOOST"); } else { upsdebugx(2, "%s: output voltage too high", __func__); } @@ -466,9 +547,10 @@ static int powpan_updateinfo(void) return (status.flags[0] & 0x40) ? 1 : 0; } -static int powpan_initups(void) +static ssize_t powpan_initups(void) { - int ret, i; + ssize_t ret; + int i; upsdebugx(1, "Trying text protocol..."); @@ -483,7 +565,7 @@ static int powpan_initups(void) /* * WRITE P4\r - * READ #BC1200 ,1.600,000000000000,CYBER POWER + * READ #BC1200 ,1.600,000000000000,CYBER POWER * 01234567890123456789012345678901234567890123456 * 0 1 2 3 4 */ @@ -492,9 +574,9 @@ static int powpan_initups(void) if (ret < 1) { continue; } - + if (ret < 46) { - upsdebugx(2, "Expected 46 bytes, but only got %d", ret); + upsdebugx(2, "Expected 46 bytes, but only got %zd", ret); continue; } @@ -509,7 +591,7 @@ static int powpan_initups(void) } if ((ondelay < 0) || (ondelay > 9999)) { - fatalx(EXIT_FAILURE, "Start delay '%d' out of range [0..9999]", ondelay); + fatalx(EXIT_FAILURE, "Start delay '%ld' out of range [0..9999]", ondelay); } val = getval("offdelay"); @@ -518,7 +600,7 @@ static int powpan_initups(void) } if ((offdelay < 6) || (offdelay > 600)) { - fatalx(EXIT_FAILURE, "Shutdown delay '%d' out of range [6..600]", offdelay); + fatalx(EXIT_FAILURE, "Shutdown delay '%ld' out of range [6..600]", offdelay); } /* Truncate to nearest setable value */ @@ -536,6 +618,7 @@ static int powpan_initups(void) subdriver_t powpan_text = { "text", + POWERPANEL_TEXT_VERSION, powpan_instcmd, powpan_setvar, powpan_initups, diff --git a/drivers/powerp-txt.h b/drivers/powerp-txt.h index 790fd4c..a830d03 100644 --- a/drivers/powerp-txt.h +++ b/drivers/powerp-txt.h @@ -1,6 +1,6 @@ /* * powerp-txt.h - Model specific data/definitions for CyberPower text - * protocol UPSes + * protocol UPSes * * Copyright (C) * 2007 Doug Reynolds diff --git a/drivers/powerpanel.c b/drivers/powerpanel.c index 2faf171..6c677b8 100644 --- a/drivers/powerpanel.c +++ b/drivers/powerpanel.c @@ -1,6 +1,6 @@ /* * powerpanel.c - Model specific routines for CyberPower text/binary - * protocol UPSes + * protocol UPSes * * Copyright (C) * 2007 Doug Reynolds @@ -36,14 +36,15 @@ static subdriver_t *subdriver[] = { }; #define DRIVER_NAME "CyberPower text/binary protocol UPS driver" -#define DRIVER_VERSION "0.25" +#define DRIVER_VERSION "0.28" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Doug Reynolds \n" \ - "Arjen de Korte ", + "Arjen de Korte \n" \ + "Timothy Pearson ", DRV_EXPERIMENTAL, { NULL } }; @@ -101,7 +102,7 @@ void upsdrv_updateinfo(void) void upsdrv_shutdown(void) { - int i, ret; + int i, ret = -1; /* * Try to shutdown with delay and automatic reboot if the power @@ -124,7 +125,7 @@ void upsdrv_shutdown(void) } } - if (ret) { + if (ret > 0) { /* * When on battery, the 'shutdown.stayoff' command will make * the UPS switch back on when the power returns. @@ -133,7 +134,7 @@ void upsdrv_shutdown(void) upslogx(LOG_INFO, "Waiting for power to return..."); return; } - } else { + } else if (ret == 0) { /* * Apparently, the power came back already, so we just need to reboot. */ diff --git a/drivers/powerpanel.h b/drivers/powerpanel.h index 8e441ba..b2b34be 100644 --- a/drivers/powerpanel.h +++ b/drivers/powerpanel.h @@ -1,6 +1,6 @@ /* * powerpanel.h - Model specific data/definitions for CyberPower text/binary - * protocol UPSes + * protocol UPSes * * Copyright (C) * 2007 Doug Reynolds @@ -33,10 +33,11 @@ #define SER_WAIT_USEC 250000 typedef struct { - const char *version; + const char *version; /* TODO: Rename: this is a subdriver type: "text" or "binary" */ + const char *versionString; /* TODO: Rename: this is the actual subdriver version */ int (*instcmd)(const char *cmdname, const char *extra); int (*setvar)(const char *varname, const char *val); - int (*initups)(void); + ssize_t (*initups)(void); void (*initinfo)(void); int (*updateinfo)(void); } subdriver_t; diff --git a/drivers/powervar-hid.c b/drivers/powervar-hid.c new file mode 100755 index 0000000..a37978a --- /dev/null +++ b/drivers/powervar-hid.c @@ -0,0 +1,147 @@ +/* powervar-hid.c - subdriver to monitor Powervar USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2012 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2013 Charles Lepple + * 2019 AMETEK Powervar - Andrew McCartney + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-usbhid-subdriver script. It must be customized. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" /* must be first */ + +#include "usbhid-ups.h" +#include "powervar-hid.h" +#include "main.h" /* for getval() */ +#include "usb-common.h" + +#define POWERVAR_HID_VERSION "Powervar HID 0.20" +/* FIXME: experimental flag to be put in upsdrv_info */ + +/* Powervar */ +#define POWERVAR_VENDORID 0x4234 + +/* USB IDs device table */ +static usb_device_id_t powervar_usb_device_table[] = { + /* Powervar */ + { USB_DEVICE(POWERVAR_VENDORID, 0x0002), NULL }, + + /* Terminating entry */ + { 0, 0, NULL } +}; + +static usb_communication_subdriver_t *usb = &usb_subdriver; + +/* --------------------------------------------------------------- */ +/* Vendor-specific usage table */ +/* --------------------------------------------------------------- */ + +/* POWERVAR usage table */ +static usage_lkp_t powervar_usage_lkp[] = { + { "POWERVAR1", 0xff000001 }, + { NULL, 0 } +}; + +static usage_tables_t powervar_utab[] = { + powervar_usage_lkp, + hid_usage_lkp, + NULL, +}; + +/* --------------------------------------------------------------- */ +/* HID2NUT lookup table */ +/* --------------------------------------------------------------- */ + +static hid_info_t powervar_hid2nut[] = { + + /* Battery page */ + { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_conversion }, + { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, + { "battery.capacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, + { "battery.runtime", 0, 0, "UPS.PowerSummary.RuntimeToEmpty", NULL, "%.0f", 0, NULL }, + +/* Special case: ups.status & ups.alarm */ + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ShutdownImminent", NULL, NULL, 0, shutdownimm_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, 0, lowbatt_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, 0, overload_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, 0, timelimitexpired_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.InternalFailure", NULL, NULL, 0, yes_no_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Good", NULL, NULL, 0, yes_no_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.OverTemperature", NULL, NULL, 0, overheat_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FullyCharged", NULL, NULL, 0, fullycharged_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FullyDischarged", NULL, NULL, 0, depleted_info }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } +}; + +static const char *powervar_format_model(HIDDevice_t *hd) { + return hd->Product; +} + +static const char *powervar_format_mfr(HIDDevice_t *hd) { + return hd->Vendor ? hd->Vendor : "Powervar"; +} + +static const char *powervar_format_serial(HIDDevice_t *hd) { + return hd->Serial; +} + +/* this function allows the subdriver to "claim" a device: return 1 if + * the device is supported by this subdriver, else 0. */ +static int powervar_claim(HIDDevice_t *hd) +{ + int status = is_usb_device_supported(powervar_usb_device_table, hd); + + switch (status) + { + case POSSIBLY_SUPPORTED: + /* by default, reject, unless the productid option is given */ + if (getval("productid")) { + usb->hid_rep_index = 1; + return 1; + } + possibly_supported("Powervar", hd); + return 0; + + case SUPPORTED: + usb->hid_rep_index = 1; + return 1; + + case NOT_SUPPORTED: + default: + return 0; + } +} + +subdriver_t powervar_subdriver = { + POWERVAR_HID_VERSION, + powervar_claim, + powervar_utab, + powervar_hid2nut, + powervar_format_model, + powervar_format_mfr, + powervar_format_serial, + fix_report_desc, +}; diff --git a/drivers/powervar-hid.h b/drivers/powervar-hid.h new file mode 100755 index 0000000..f86d5b8 --- /dev/null +++ b/drivers/powervar-hid.h @@ -0,0 +1,30 @@ +/* powervar-hid.h - subdriver to monitor Powervar USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2009 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef POWERVAR_HID_H +#define POWERVAR_HID_H + +#include "usbhid-ups.h" + +extern subdriver_t powervar_subdriver; + +#endif /* POWERVAR_HID_H */ diff --git a/drivers/powerware-mib.c b/drivers/powerware-mib.c index 7c4d45b..e4b0296 100644 --- a/drivers/powerware-mib.c +++ b/drivers/powerware-mib.c @@ -1,9 +1,10 @@ /* powerware-mib.c - data to monitor Powerware UPS with NUT * (using MIBs described in stdupsv1.mib and Xups.mib) * - * Copyright (C) 2005-2006 - * Olli Savia - * Niels Baggesen + * Copyright (C) + * 2005-2006 Olli Savia + * 2005-2006 Niels Baggesen + * 2015-2019 Eaton (author: Arnaud Quette ) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,19 +24,26 @@ */ #include "powerware-mib.h" +#if WITH_SNMP_LKP_FUN +/* FIXME: shared helper code, need to be put in common */ +#include "eaton-pdu-marlin-helpers.h" +#endif -#define PW_MIB_VERSION "0.6.1" +#define PW_MIB_VERSION "0.104" /* TODO: more sysOID and MIBs support: - * + * * Powerware UPS (Ingrasys X-SLOT and BD-SLOT): ".1.3.6.1.4.1.534.1" * Powerware PXGX cards: ".1.3.6.1.4.1.534.2.12" * PXGX 2000 cards (UPS): Get xupsIdentModel (".1.3.6.1.4.1.534.1.1.2.0") * PXGX 1000 cards (PDU/RPP/RPM): Get pduNumPanels ".1.3.6.1.4.1.534.6.6.4.1.1.1.4.0" */ -/* Powerware UPS (Ingrasys X-SLOT and BD-SLOT) */ +/* Powerware UPS (Ingrasys X-SLOT and BD-SLOT) + * Eaton Gigabit Network Card (Genepi) */ #define POWERWARE_SYSOID ".1.3.6.1.4.1.534.1" +/* Powerware UPS newer PXGX UPS cards (BladeUPS, ...) */ +#define EATON_PXGX_SYSOID ".1.3.6.1.4.1.534.2.12" /* SNMP OIDs set */ #define PW_OID_MFR_NAME "1.3.6.1.4.1.534.1.1.1.0" /* XUPS-MIB::xupsIdentManufacturer.0 */ @@ -67,24 +75,20 @@ #define PW_OID_BY_LINES "1.3.6.1.4.1.534.1.5.2.0" /* XUPS-MIB::xupsBypassNumPhases.0 */ #define PW_OID_BY_VOLTAGE "1.3.6.1.4.1.534.1.5.3.1.2" /* XUPS-MIB::xupsBypassVoltage */ -#define PW_OID_AMBIENT_TEMP "1.3.6.1.4.1.534.1.6.1.0" /* XUPS-MIB::xupsEnvAmbientTemp.0 */ -#define PW_OID_AMBIENT_LOW "1.3.6.1.4.1.534.1.6.2.0" /* XUPS-MIB::xupsEnvAmbientLowerLimit.0 */ -#define PW_OID_AMBIENT_HIGH "1.3.6.1.4.1.534.1.6.3.0" /* XUPS-MIB::xupsEnvAmbientUpperLimit.0 */ - #define PW_OID_BATTEST_START "1.3.6.1.4.1.534.1.8.1" /* XUPS-MIB::xupsTestBattery set to startTest(1) to initiate test*/ -#define PW_OID_BATTEST_RES "1.3.6.1.4.1.534.1.8.2" /* XUPS-MIB::xupsTestBatteryStatus */ -#define PW_OID_CONT_OFFDELAY "1.3.6.1.4.1.534.1.9.1" /* XUPS-MIB::xupsControlOutputOffDelay */ -#define PW_OID_CONT_ONDELAY "1.3.6.1.4.1.534.1.9.2" /* XUPS-MIB::xupsControlOutputOnDelay */ +#define PW_OID_CONT_OFFDELAY "1.3.6.1.4.1.534.1.9.1.0" /* XUPS-MIB::xupsControlOutputOffDelay */ +#define PW_OID_CONT_ONDELAY "1.3.6.1.4.1.534.1.9.2.0" /* XUPS-MIB::xupsControlOutputOnDelay */ #define PW_OID_CONT_OFFT_DEL "1.3.6.1.4.1.534.1.9.3" /* XUPS-MIB::xupsControlOutputOffTrapDelay */ #define PW_OID_CONT_ONT_DEL "1.3.6.1.4.1.534.1.9.4" /* XUPS-MIB::xupsControlOutputOnTrapDelay */ +#define PW_OID_CONT_LOAD_SHED_AND_RESTART "1.3.6.1.4.1.534.1.9.6" /* XUPS-MIB::xupsLoadShedSecsWithRestart */ #define PW_OID_CONF_OVOLTAGE "1.3.6.1.4.1.534.1.10.1.0" /* XUPS-MIB::xupsConfigOutputVoltage.0 */ #define PW_OID_CONF_IVOLTAGE "1.3.6.1.4.1.534.1.10.2.0" /* XUPS-MIB::xupsConfigInputVoltage.0 */ #define PW_OID_CONF_POWER "1.3.6.1.4.1.534.1.10.3.0" /* XUPS-MIB::xupsConfigOutputWatts.0 */ #define PW_OID_CONF_FREQ "1.3.6.1.4.1.534.1.10.4.0" /* XUPS-MIB::xupsConfigOutputFreq.0 */ -#define PW_OID_ALARMS "1.3.6.1.4.1.534.1.7.1" /* XUPS-MIB::xupsAlarms */ +#define PW_OID_ALARMS "1.3.6.1.4.1.534.1.7.1.0" /* XUPS-MIB::xupsAlarms */ #define PW_OID_ALARM_OB "1.3.6.1.4.1.534.1.7.3" /* XUPS-MIB::xupsOnBattery */ #define PW_OID_ALARM_LB "1.3.6.1.4.1.534.1.7.4" /* XUPS-MIB::xupsLowBattery */ @@ -93,97 +97,298 @@ #define IETF_OID_CONF_OUT_VA "1.3.6.1.2.1.33.1.9.5.0" /* UPS-MIB::upsConfigOutputVA.0 */ #define IETF_OID_CONF_RUNTIME_LOW "1.3.6.1.2.1.33.1.9.7.0" /* UPS-MIB::upsConfigLowBattTime.0 */ #define IETF_OID_LOAD_LEVEL "1.3.6.1.2.1.33.1.4.4.1.5" /* UPS-MIB::upsOutputPercentLoad */ +#define IETF_OID_AUTO_RESTART "1.3.6.1.2.1.33.1.8.5.0" /* UPS-MIB::upsAutoRestart */ + +/* Delay before powering off in seconds */ +#define DEFAULT_OFFDELAY 30 +/* Delay before powering on in seconds */ +#define DEFAULT_ONDELAY 20 +/* Default shutdown.return delay in seconds */ +#define DEFAULT_SHUTDOWNDELAY 0 static info_lkp_t pw_alarm_ob[] = { - { 1, "OB" }, - { 2, "" }, - { 0, "NULL" } -} ; + { 1, "OB", NULL, NULL }, + { 2, "", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; static info_lkp_t pw_alarm_lb[] = { - { 1, "LB" }, - { 2, "" }, - { 0, "NULL" } -} ; + { 1, "LB", NULL, NULL }, + { 2, "", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; static info_lkp_t pw_pwr_info[] = { - { 1, "" /* other */ }, - { 2, "OFF" /* none */ }, - { 3, "OL" /* normal */ }, - { 4, "BYPASS" /* bypass */ }, - { 5, "OB" /* battery */ }, - { 6, "OL BOOST" /* booster */ }, - { 7, "OL TRIM" /* reducer */ }, - { 8, "OL" /* parallel capacity */ }, - { 9, "OL" /* parallel redundancy */ }, - {10, "OL" /* high efficiancy */ }, - { 0, "NULL" } + { 1, "" /* other */, NULL, NULL }, + { 2, "OFF" /* none */, NULL, NULL }, + { 3, "OL" /* normal */, NULL, NULL }, + { 4, "BYPASS" /* bypass */, NULL, NULL }, + { 5, "OB" /* battery */, NULL, NULL }, + { 6, "OL BOOST" /* booster */, NULL, NULL }, + { 7, "OL TRIM" /* reducer */, NULL, NULL }, + { 8, "OL" /* parallel capacity */, NULL, NULL }, + { 9, "OL" /* parallel redundancy */, NULL, NULL }, + { 10, "OL" /* high efficiency */, NULL, NULL }, + /* Extended status values */ + { 240, "OB" /* battery (0xF0) */, NULL, NULL }, + { 100, "BYPASS" /* maintenanceBypass (0x64) */, NULL, NULL }, + { 96, "BYPASS" /* Bypass (0x60) */, NULL, NULL }, + { 81, "OL" /* high efficiency (0x51) */, NULL, NULL }, + { 80, "OL" /* normal (0x50) */, NULL, NULL }, + { 64, "OL" /* UPS supporting load, normal degraded mode (0x40) */, NULL, NULL }, + { 16, "OFF" /* none (0x10) */, NULL, NULL }, + { 0, NULL, NULL, NULL } }; +/* FIXME: mapped to (experimental.)ups.type, but + * should be output.source or ups.mode (need RFC) + * to complement the above ups.status + * along with having ups.type as described hereafter*/ +/* FIXME: should be used by ups.mode or output.source (need RFC); + * Note: this define is not set via project options; code was hidden with + * original commit to "snmp-ups: support newer Genepi management cards"; + * un-hidden to make it "experimental.*" namespace during backporting + */ +#ifndef USE_PW_MODE_INFO +# define USE_PW_MODE_INFO 1 +#endif + +#if USE_PW_MODE_INFO static info_lkp_t pw_mode_info[] = { - { 1, "" }, - { 2, "" }, - { 3, "normal" }, - { 4, "" }, - { 5, "" }, - { 6, "" }, - { 7, "" }, - { 8, "parallel capacity" }, - { 9, "parallel redundancy" }, - {10, "high efficiency" }, - { 0, "NULL" } + { 1, "", NULL, NULL }, + { 2, "", NULL, NULL }, + { 3, "normal", NULL, NULL }, + { 4, "", NULL, NULL }, + { 5, "", NULL, NULL }, + { 6, "", NULL, NULL }, + { 7, "", NULL, NULL }, + { 8, "parallel capacity", NULL, NULL }, + { 9, "parallel redundancy", NULL, NULL }, + { 10, "high efficiency", NULL, NULL }, + /* Extended status values, + * FIXME: check for source and completion */ + { 240, "" /* battery (0xF0) */, NULL, NULL }, + { 100, "" /* maintenanceBypass (0x64) */, NULL, NULL }, + { 96, "" /* Bypass (0x60) */, NULL, NULL }, + { 81, "high efficiency" /* high efficiency (0x51) */, NULL, NULL }, + { 80, "normal" /* normal (0x50) */, NULL, NULL }, + { 64, "" /* UPS supporting load, normal degraded mode (0x40) */, NULL, NULL }, + { 16, "" /* none (0x10) */, NULL, NULL }, + { 0, NULL, NULL, NULL } +}; +#endif /* USE_PW_MODE_INFO */ + +/* FIXME: may be standardized + * extracted from bcmxcp.c->BCMXCP_TOPOLOGY_*, Make some common definitions */ +static info_lkp_t pw_topology_info[] = { + { 0x0000, "", NULL, NULL }, /* None; use the Table of Elements */ + { 0x0010, "Off-line switcher, Single Phase", NULL, NULL }, + { 0x0020, "Line-Interactive UPS, Single Phase", NULL, NULL }, + { 0x0021, "Line-Interactive UPS, Two Phase", NULL, NULL }, + { 0x0022, "Line-Interactive UPS, Three Phase", NULL, NULL }, + { 0x0030, "Dual AC Input, On-Line UPS, Single Phase", NULL, NULL }, + { 0x0031, "Dual AC Input, On-Line UPS, Two Phase", NULL, NULL }, + { 0x0032, "Dual AC Input, On-Line UPS, Three Phase", NULL, NULL }, + { 0x0040, "On-Line UPS, Single Phase", NULL, NULL }, + { 0x0041, "On-Line UPS, Two Phase", NULL, NULL }, + { 0x0042, "On-Line UPS, Three Phase", NULL, NULL }, + { 0x0050, "Parallel Redundant On-Line UPS, Single Phase", NULL, NULL }, + { 0x0051, "Parallel Redundant On-Line UPS, Two Phase", NULL, NULL }, + { 0x0052, "Parallel Redundant On-Line UPS, Three Phase", NULL, NULL }, + { 0x0060, "Parallel for Capacity On-Line UPS, Single Phase", NULL, NULL }, + { 0x0061, "Parallel for Capacity On-Line UPS, Two Phase", NULL, NULL }, + { 0x0062, "Parallel for Capacity On-Line UPS, Three Phase", NULL, NULL }, + { 0x0102, "System Bypass Module, Three Phase", NULL, NULL }, + { 0x0122, "Hot-Tie Cabinet, Three Phase", NULL, NULL }, + { 0x0200, "Outlet Controller, Single Phase", NULL, NULL }, + { 0x0222, "Dual AC Input Static Switch Module, 3 Phase", NULL, NULL }, + { 0, NULL, NULL, NULL } }; +/* Legacy implementation */ static info_lkp_t pw_battery_abm_status[] = { - { 1, "CHRG" }, - { 2, "DISCHRG" }, -/* { 3, "Floating" }, */ -/* { 4, "Resting" }, */ -/* { 5, "Unknown" }, */ - { 0, "NULL" } -} ; + { 1, "CHRG", NULL, NULL }, + { 2, "DISCHRG", NULL, NULL }, +/* { 3, "Floating", NULL, NULL }, */ +/* { 4, "Resting", NULL, NULL }, */ +/* { 5, "Unknown", NULL, NULL }, */ + { 0, NULL, NULL, NULL } +}; -static info_lkp_t pw_batt_info[] = { - { 1, "" }, - { 2, "" }, - { 3, "Battery Floating" }, /* battery floating - can we put that stuff somewhere so one actually access that information? */ - { 4, "Battery Resting" }, /* battery resting - could come handy if support asks what - state the batteries are in... pw_batt_info doesn't get used */ - { 5, "unknown" }, /* unknown */ - { 0, "NULL" } +static info_lkp_t pw_abm_status_info[] = { + { 1, "charging", NULL, NULL }, + { 2, "discharging", NULL, NULL }, + { 3, "floating", NULL, NULL }, + { 4, "resting", NULL, NULL }, + { 5, "unknown", NULL, NULL }, /* Undefined - ABM is not activated */ + { 6, "disabled", NULL, NULL }, /* ABM Charger Disabled */ + { 0, NULL, NULL, NULL } }; static info_lkp_t pw_batt_test_info[] = { - { 1, "" }, /* unknown */ - { 2, "Done and passed" }, - { 3, "Done and error" }, - { 4, "In progress" }, - { 5, "Not supported" }, - { 6, "Inhibited" }, - { 7, "Scheduled" }, - { 0, "NULL" } + { 1, "Unknown", NULL, NULL }, + { 2, "Done and passed", NULL, NULL }, + { 3, "Done and error", NULL, NULL }, + { 4, "In progress", NULL, NULL }, + { 5, "Not supported", NULL, NULL }, + { 6, "Inhibited", NULL, NULL }, + { 7, "Scheduled", NULL, NULL }, + { 0, NULL, NULL, NULL } }; +static info_lkp_t pw_yes_no_info[] = { + { 1, "yes", NULL, NULL }, + { 2, "no", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t pw_outlet_status_info[] = { + { 1, "on", NULL, NULL }, + { 2, "off", NULL, NULL }, + { 3, "on", NULL, NULL }, /* pendingOff, transitional status */ + { 4, "off", NULL, NULL }, /* pendingOn, transitional status */ + /* { 5, "", NULL, NULL }, unknown */ + /* { 6, "", NULL, NULL }, reserved */ + { 7, "off", NULL, NULL }, /* Failed in Closed position */ + { 8, "on", NULL, NULL }, /* Failed in Open position */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t pw_ambient_drycontacts_info[] = { + { -1, "unknown", NULL, NULL }, + { 1, "opened", NULL, NULL }, + { 2, "closed", NULL, NULL }, + { 3, "opened", NULL, NULL }, /* openWithNotice */ + { 4, "closed", NULL, NULL }, /* closedWithNotice */ + { 0, NULL, NULL, NULL } +}; + +#if WITH_SNMP_LKP_FUN +/* Note: eaton_sensor_temperature_unit_fun() is defined in eaton-pdu-marlin-helpers.c + * and su_temperature_read_fun() is in snmp-ups.c + * Future work for DMF might provide same-named routines via LUA-C gateway. + */ + +# if WITH_SNMP_LKP_FUN_DUMMY +/* Temperature unit consideration */ +const char *eaton_sensor_temperature_unit_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "unknown"; +} +/* FIXME: please DMF, though this should be in snmp-ups.c or equiv. */ +const char *su_temperature_read_fun(void *raw_snmp_value) { + /* snmp_value here would be a (long*) */ + NUT_UNUSED_VARIABLE(raw_snmp_value); + return "dummy"; +}; +# endif /* WITH_SNMP_LKP_FUN_DUMMY */ + +static info_lkp_t pw_sensor_temperature_unit_info[] = { + { 0, "dummy", eaton_sensor_temperature_unit_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t pw_sensor_temperature_read_info[] = { + { 0, "dummy", su_temperature_read_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +#else /* if not WITH_SNMP_LKP_FUN: */ + +/* FIXME: For now, DMF codebase falls back to old implementation with static + * lookup/mapping tables for this, which can easily go into the DMF XML file. + */ +static info_lkp_t pw_sensor_temperature_unit_info[] = { + { 0, "kelvin", NULL, NULL }, + { 1, "celsius", NULL, NULL }, + { 2, "fahrenheit", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +#endif /* WITH_SNMP_LKP_FUN */ + +static info_lkp_t pw_ambient_drycontacts_polarity_info[] = { + { 0, "normal-opened", NULL, NULL }, + { 1, "normal-closed", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t pw_ambient_drycontacts_state_info[] = { + { 0, "inactive", NULL, NULL }, + { 1, "active", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t pw_emp002_ambient_presence_info[] = { + { 0, "unknown", NULL, NULL }, + { 2, "yes", NULL, NULL }, /* communicationOK */ + { 3, "no", NULL, NULL }, /* communicationLost */ + { 0, NULL, NULL, NULL } +}; + +/* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_status_info */ +static info_lkp_t pw_threshold_status_info[] = { + { 0, "good", NULL, NULL }, /* No threshold triggered */ + { 1, "warning-low", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "critical-low", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "warning-high", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "critical-high", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +/* extracted from drivers/eaton-pdu-marlin-mib.c -> marlin_threshold_xxx_alarms_info */ +static info_lkp_t pw_threshold_temperature_alarms_info[] = { + { 0, "", NULL, NULL }, /* No threshold triggered */ + { 1, "low temperature warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "low temperature critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "high temperature warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "high temperature critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; + +static info_lkp_t pw_threshold_humidity_alarms_info[] = { + { 0, "", NULL, NULL }, /* No threshold triggered */ + { 1, "low humidity warning!", NULL, NULL }, /* Warning low threshold triggered */ + { 2, "low humidity critical!", NULL, NULL }, /* Critical low threshold triggered */ + { 3, "high humidity warning!", NULL, NULL }, /* Warning high threshold triggered */ + { 4, "high humidity critical!", NULL, NULL }, /* Critical high threshold triggered */ + { 0, NULL, NULL, NULL } +}; /* Snmp2NUT lookup table */ static snmp_info_t pw_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* FIXME: miss device page! */ /* UPS page */ /* info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_MFR_NAME, "", SU_FLAG_STATIC, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_MODEL_NAME, "", SU_FLAG_STATIC, NULL }, + /* FIXME: the 2 "firmware" entries below should be SU_FLAG_SEMI_STATIC */ { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_FIRMREV, "", - SU_FLAG_STATIC, NULL }, + 0, NULL }, { "ups.firmware.aux", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_AGENTREV, "", - SU_FLAG_STATIC, NULL }, + 0, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, IETF_OID_IDENT, "", SU_FLAG_STATIC, NULL }, { "ups.load", 0, 1.0, PW_OID_OUT_LOAD, "", - SU_OUTPUT_1, NULL }, + 0, NULL }, + /* FIXME: should be removed in favor of output.power */ { "ups.power", 0, 1.0, PW_OID_OUT_POWER ".1", "", 0, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputWatts.1.0; Value (Integer): 300 */ + { "ups.power", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.4.1.0", "", + 0, NULL }, + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "OFF", SU_STATUS_PWR, &pw_pwr_info[0] }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_ALARM_OB, "", @@ -192,16 +397,35 @@ static snmp_info_t pw_mib[] = { SU_STATUS_BATT, &pw_alarm_lb[0] }, { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATT_STATUS, "", SU_STATUS_BATT, &pw_battery_abm_status[0] }, - { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "", +#if USE_PW_MODE_INFO + /* FIXME: should be ups.mode or output.source (need RFC) */ + /* Note: this define is not set via project options; code hidden with + * commit to "snmp-ups: support newer Genepi management cards" */ + { "experimental.ups.type", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_POWER_STATUS, "", SU_FLAG_STATIC | SU_FLAG_OK, &pw_mode_info[0] }, +#endif /* USE_PW_MODE_INFO */ + /* xupsTopologyType.0; Value (Integer): 32 */ + { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.13.1.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, &pw_topology_info[0] }, + /* FIXME: should be removed in favor of their output. equivalent! */ { "ups.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", 0, NULL }, + /* FIXME: should be removed in favor of output.power.nominal */ { "ups.power.nominal", 0, 1.0, IETF_OID_CONF_OUT_VA, "", 0, NULL }, - { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATTEST_RES, "", - 0, &pw_batt_test_info[0] }, - { "vendor.specific.abmstatus", ST_FLAG_STRING, SU_INFOSIZE, PW_OID_BATT_STATUS, "", - SU_STATUS_BATT, &pw_batt_info[0] }, + /* XUPS-MIB::xupsEnvAmbientTemp.0 */ + { "ups.temperature", 0, 1.0, "1.3.6.1.4.1.534.1.6.1.0", "", 0, NULL }, + /* FIXME: These 2 data needs RFC! */ + /* XUPS-MIB::xupsEnvAmbientLowerLimit.0 */ + { "ups.temperature.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.2.0", "", 0, NULL }, + /* XUPS-MIB::xupsEnvAmbientUpperLimit.0 */ + { "ups.temperature.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.3.0", "", 0, NULL }, + /* XUPS-MIB::xupsTestBatteryStatus */ + { "ups.test.result", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.8.2.0", "", 0, &pw_batt_test_info[0] }, + /* UPS-MIB::upsAutoRestart */ + { "ups.start.auto", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.2.1.33.1.8.5.0", "", SU_FLAG_OK, &pw_yes_no_info[0] }, + /* XUPS-MIB::xupsBatteryAbmStatus.0 */ + { "battery.charger.status", ST_FLAG_STRING, SU_INFOSIZE, "1.3.6.1.4.1.534.1.2.5.0", "", SU_STATUS_BATT, &pw_abm_status_info[0] }, /* Battery page */ { "battery.charge", 0, 1.0, PW_OID_BATT_CHARGE, "", @@ -214,18 +438,41 @@ static snmp_info_t pw_mib[] = { 0, NULL }, { "battery.runtime.low", 0, 60.0, IETF_OID_CONF_RUNTIME_LOW, "", 0, NULL }, + { "battery.date", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.1.2.6.0", NULL, SU_FLAG_OK, &su_convert_to_iso_date_info[FUNMAP_USDATE_TO_ISODATE] }, /* Output page */ - { "output.phases", 0, 1.0, PW_OID_OUT_LINES, "", - SU_FLAG_SETINT, NULL, &output_phases }, - { "output.frequency", 0, 0.1, PW_OID_OUT_FREQUENCY, "", - 0, NULL }, - { "output.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".1", "", - SU_OUTPUT_1, NULL }, + { "output.phases", 0, 1.0, PW_OID_OUT_LINES, "", 0, NULL }, + /* XUPS-MIB::xupsOutputFrequency.0 */ + { "output.frequency", 0, 0.1, "1.3.6.1.4.1.534.1.4.2.0", "", 0, NULL }, + /* XUPS-MIB::xupsConfigOutputFreq.0 */ + { "output.frequency.nominal", 0, 0.1, "1.3.6.1.4.1.534.1.10.4.0", "", 0, NULL }, + /* XUPS-MIB::xupsOutputVoltage.1 */ + { "output.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.2.1", "", SU_OUTPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputVoltage.1.0; Value (Integer): 230 */ + { "output.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.2.1.0", "", SU_OUTPUT_1, NULL }, + /* XUPS-MIB::xupsConfigOutputVoltage.0 */ + { "output.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.1.0", "", 0, NULL }, + /* XUPS-MIB::xupsConfigLowOutputVoltageLimit.0 */ + { "output.voltage.low", 0, 1.0, ".1.3.6.1.4.1.534.1.10.6.0", "", 0, NULL }, + /* XUPS-MIB::xupsConfigHighOutputVoltageLimit.0 */ + { "output.voltage.high", 0, 1.0, ".1.3.6.1.4.1.534.1.10.7.0", "", 0, NULL }, { "output.current", 0, 1.0, PW_OID_OUT_CURRENT ".1", "", SU_OUTPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsOutputCurrent.1.0; Value (Integer): 0 */ + { "output.current", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.3.1.0", "", + SU_OUTPUT_1, NULL }, { "output.realpower", 0, 1.0, PW_OID_OUT_POWER ".1", "", SU_OUTPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* Name/OID: xupsOutputWatts.1.0; Value (Integer): 1200 */ + { "output.realpower", 0, 1.0, "1.3.6.1.4.1.534.1.4.4.1.4.1.0", "", + 0, NULL }, + /* Duplicate of "ups.realpower.nominal" + * FIXME: map either ups or output, but not both (or have an auto-remap) */ + { "output.realpower.nominal", 0, 1.0, PW_OID_CONF_POWER, "", + 0, NULL }, { "output.L1-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".1", "", SU_OUTPUT_3, NULL }, { "output.L2-N.voltage", 0, 1.0, PW_OID_OUT_VOLTAGE ".2", "", @@ -244,6 +491,7 @@ static snmp_info_t pw_mib[] = { SU_OUTPUT_3, NULL }, { "output.L3.realpower", 0, 1.0, PW_OID_OUT_POWER ".3", "", SU_OUTPUT_3, NULL }, + /* FIXME: should better be output.Lx.load */ { "output.L1.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".1", "", SU_OUTPUT_3, NULL }, { "output.L2.power.percent", 0, 1.0, IETF_OID_LOAD_LEVEL ".2", "", @@ -252,16 +500,21 @@ static snmp_info_t pw_mib[] = { SU_OUTPUT_3, NULL }, { "output.voltage.nominal", 0, 1.0, PW_OID_CONF_OVOLTAGE, "", 0, NULL }, - { "output.frequency.nominal", 0, 0.1, PW_OID_CONF_FREQ, "", - 0, NULL }, /* Input page */ { "input.phases", 0, 1.0, PW_OID_IN_LINES, "", - SU_FLAG_SETINT, NULL, &input_phases }, + 0, NULL }, { "input.frequency", 0, 0.1, PW_OID_IN_FREQUENCY, "", 0, NULL }, { "input.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".0", "", SU_INPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsInputVoltage.1[.0]; Value (Integer): 245 */ + { "input.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.3.4.1.2.1", "", + SU_INPUT_1, NULL }, + + /* XUPS-MIB::xupsConfigInputVoltage.0 */ + { "input.voltage.nominal", 0, 1.0, "1.3.6.1.4.1.534.1.10.2.0", "", 0, NULL }, { "input.current", 0, 0.1, PW_OID_IN_CURRENT ".0", "", SU_INPUT_1, NULL }, { "input.L1-N.voltage", 0, 1.0, PW_OID_IN_VOLTAGE ".1", "", @@ -284,14 +537,16 @@ static snmp_info_t pw_mib[] = { SU_INPUT_3, NULL }, { "input.quality", 0, 1.0, PW_OID_IN_LINE_BADS, "", 0, NULL }, - { "input.voltage.nominal", 0, 1.0, PW_OID_CONF_IVOLTAGE, "", - 0, NULL }, - /* this segfaults? do we assume the same number of bypass phases as input phases? - { "input.bypass.phases", 0, 1.0, PW_OID_BY_LINES, "", - SU_FLAG_SETINT, NULL }, */ + /* FIXME: this segfaults! do we assume the same number of bypass phases as input phases? + { "input.bypass.phases", 0, 1.0, PW_OID_BY_LINES, "", 0, NULL }, */ + { "input.bypass.frequency", 0, 0.1, PW_OID_BY_FREQUENCY, "", 0, NULL }, { "input.bypass.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".0", "", SU_INPUT_1, NULL }, + /* Duplicate of the above entry, but pointing at the first index */ + /* xupsBypassVoltage.1.0; Value (Integer): 244 */ + { "input.bypass.voltage", 0, 1.0, "1.3.6.1.4.1.534.1.5.3.1.2.1.0", "", + SU_INPUT_1, NULL }, { "input.bypass.L1-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".1", "", SU_INPUT_3, NULL }, { "input.bypass.L2-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".2", "", @@ -299,29 +554,265 @@ static snmp_info_t pw_mib[] = { { "input.bypass.L3-N.voltage", 0, 1.0, PW_OID_BY_VOLTAGE ".3", "", SU_INPUT_3, NULL }, - /* Ambient page */ - { "ambient.temperature", 0, 1.0, PW_OID_AMBIENT_TEMP, "", - 0, NULL }, - { "ambient.temperature.low", 0, 1.0, PW_OID_AMBIENT_LOW, "", - 0, NULL }, - { "ambient.temperature.high", 0, 1.0, PW_OID_AMBIENT_HIGH, "", - 0, NULL }, + /* Outlet page */ + /* Master outlet id always equal to 0 */ + { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC , NULL }, + /* XUPS-MIB:: xupsSwitchable.0 */ + { "outlet.switchable", 0, 1, ".1.3.6.1.4.1.534.1.9.7.0", NULL, SU_FLAG_STATIC , &pw_yes_no_info[0] }, + /* XUPS-MIB::xupsNumReceptacles; Value (Integer): 2 */ + { "outlet.count", 0, 1, ".1.3.6.1.4.1.534.1.12.1.0", NULL, SU_FLAG_STATIC, NULL }, + /* XUPS-MIB::xupsRecepIndex.X; Value (Integer): X */ + { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.1.%i", NULL, SU_FLAG_STATIC | SU_OUTLET, NULL }, + /* This MIB does not provide outlets switchability info. So map to a nearby + OID, for data activation, and map all values to "yes" */ + { "outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.1.%i", NULL, SU_FLAG_STATIC | SU_OUTLET, NULL }, + /* XUPS-MIB::xupsRecepStatus.X; Value (Integer): 1 */ + { "outlet.%i.status", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.2.%i", NULL, SU_OUTLET, &pw_outlet_status_info[0] }, + + /* Ambient collection */ + /* EMP001 (legacy) mapping */ + /* XUPS-MIB::xupsEnvRemoteTemp.0 */ + { "ambient.temperature", 0, 1.0, "1.3.6.1.4.1.534.1.6.5.0", "", 0, NULL }, + /* XUPS-MIB::xupsEnvRemoteTempLowerLimit.0 */ + { "ambient.temperature.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.9.0", "", 0, NULL }, + /* XUPS-MIB::xupsEnvRemoteTempUpperLimit.0 */ + { "ambient.temperature.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.10.0", "", 0, NULL }, + /* XUPS-MIB::xupsEnvRemoteHumidity.0 */ + { "ambient.humidity", 0, 1.0, "1.3.6.1.4.1.534.1.6.6.0", "", 0, NULL }, + /* XUPS-MIB::xupsEnvRemoteHumidityLowerLimit.0 */ + { "ambient.humidity.low", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.11.0", "", 0, NULL }, + /* XUPS-MIB::xupsEnvRemoteHumidityUpperLimit.0 */ + { "ambient.humidity.high", ST_FLAG_RW, 1.0, "1.3.6.1.4.1.534.1.6.12.0", "", 0, NULL }, + /* XUPS-MIB::xupsContactDescr.n */ + { "ambient.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.4.1", "", 0, NULL }, + { "ambient.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.4.2", "", 0, NULL }, + /* XUPS-MIB::xupsContactState.n */ + { "ambient.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.3.1", "", 0, &pw_ambient_drycontacts_info[0] }, + { "ambient.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.1.6.8.1.3.2", "", 0, &pw_ambient_drycontacts_info[0] }, + + /* EMP002 (EATON EMP MIB) mapping, including daisychain support */ + /* Warning: indexes start at '1' not '0'! */ + /* sensorCount.0 */ + { "ambient.count", ST_FLAG_RW, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.1.0", "", 0, NULL }, + /* CommunicationStatus.n */ + { "ambient.%i.present", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.1.4.1.1.%i", + NULL, SU_AMBIENT_TEMPLATE, &pw_emp002_ambient_presence_info[0] }, + /* sensorName.n: OctetString EMPDT1H1C2 @1 */ + { "ambient.%i.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.3.1.1.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorManufacturer.n */ + { "ambient.%i.mfr", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.6.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorModel.n */ + { "ambient.%i.model", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.7.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorSerialNumber.n */ + { "ambient.%i.serial", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.9.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorUuid.n */ + { "ambient.%i.id", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.2.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorAddress.n */ + { "ambient.%i.address", 0, 1, ".1.3.6.1.4.1.534.6.8.1.1.2.1.4.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* sensorFirmwareVersion.n */ + { "ambient.%i.firmware", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.1.2.1.10.%i", "", SU_AMBIENT_TEMPLATE, NULL }, + /* temperatureUnit.1 + * MUST be before the temperature data reading! */ + { "ambient.%i.temperature.unit", 0, 1.0, ".1.3.6.1.4.1.534.6.8.1.2.5.0", "", SU_AMBIENT_TEMPLATE, &pw_sensor_temperature_unit_info[0] }, + /* temperatureValue.n.1 */ + { "ambient.%i.temperature", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, +#if WITH_SNMP_LKP_FUN + &pw_sensor_temperature_read_info[0] +#else + NULL +#endif + }, + { "ambient.%i.temperature.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.2.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_temperature_alarms_info[0] }, + /* FIXME: ambient.n.temperature.{minimum,maximum} */ + /* temperatureThresholdLowCritical.n.1 */ + { "ambient.%i.temperature.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* temperatureThresholdLowWarning.n.1 */ + { "ambient.%i.temperature.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* temperatureThresholdHighWarning.n.1 */ + { "ambient.%i.temperature.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* temperatureThresholdHighCritical.n.1 */ + { "ambient.%i.temperature.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.2.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* humidityValue.n.1 */ + { "ambient.%i.humidity", 0, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + { "ambient.%i.humidity.status", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_status_info[0] }, + { "ups.alarm", ST_FLAG_STRING, SU_INFOSIZE, + ".1.3.6.1.4.1.534.6.8.1.3.3.1.1.%i.1", + NULL, SU_AMBIENT_TEMPLATE, &pw_threshold_humidity_alarms_info[0] }, + /* FIXME: consider ambient.n.humidity.{minimum,maximum} */ + /* humidityThresholdLowCritical.n.1 */ + { "ambient.%i.humidity.low.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.6.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* humidityThresholdLowWarning.n.1 */ + { "ambient.%i.humidity.low.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.5.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* humidityThresholdHighWarning.n.1 */ + { "ambient.%i.humidity.high.warning", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.7.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* humidityThresholdHighCritical.n.1 */ + { "ambient.%i.humidity.high.critical", ST_FLAG_RW, 0.1, ".1.3.6.1.4.1.534.6.8.1.3.2.1.8.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + /* digitalInputName.n.{1,2} */ + { "ambient.%i.contacts.1.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.1", "", SU_AMBIENT_TEMPLATE, NULL }, + { "ambient.%i.contacts.2.name", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.2.1.1.%i.2", "", SU_AMBIENT_TEMPLATE, NULL }, + /* digitalInputPolarity.n */ + { "ambient.%i.contacts.1.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_polarity_info[0] }, + { "ambient.%i.contacts.2.config", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.534.6.8.1.4.2.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_polarity_info[0] }, + /* XUPS-MIB::xupsContactState.n */ + { "ambient.%i.contacts.1.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.1", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_state_info[0] }, + { "ambient.%i.contacts.2.status", ST_FLAG_STRING, 1.0, ".1.3.6.1.4.1.534.6.8.1.4.3.1.3.%i.2", "", SU_AMBIENT_TEMPLATE, &pw_ambient_drycontacts_state_info[0] }, /* instant commands */ { "test.battery.start.quick", 0, 1, PW_OID_BATTEST_START, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - /* Cancel output off, by writing 0 to xupsControlOutputOffDelay */ + /* Shed load and restart when line power back on; cannot be canceled */ + { "shutdown.return", 0, DEFAULT_SHUTDOWNDELAY, PW_OID_CONT_LOAD_SHED_AND_RESTART, "", + SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* Cancel output off, by writing 0 to xupsControlOutputOffDelay */ { "shutdown.stop", 0, 0, PW_OID_CONT_OFFDELAY, "", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - /* load off after 1 sec, shortest possible delay */ - { "load.off", 0, 1, PW_OID_CONT_OFFDELAY, "", + /* XUPS-MIB::xupsControlOutputOffDelay */ + /* load off after 1 sec, shortest possible delay; 0 cancels */ + { "load.off", 0, 1, PW_OID_CONT_OFFDELAY, "1", SU_TYPE_CMD | SU_FLAG_OK, NULL }, - /* load on after 1 sec, shortest possible delay */ - { "load.on", 0, 1, PW_OID_CONT_ONDELAY, "", + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + { "load.off.delay", 0, 1, PW_OID_CONT_OFFDELAY, NULL, SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* XUPS-MIB::xupsControlOutputOnDelay */ + /* load on after 1 sec, shortest possible delay; 0 cancels */ + { "load.on", 0, 1, PW_OID_CONT_ONDELAY, "1", + SU_TYPE_CMD | SU_FLAG_OK, NULL }, + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + { "load.on.delay", 0, 1, PW_OID_CONT_ONDELAY, NULL, + SU_TYPE_CMD | SU_FLAG_OK, NULL }, + + /* Delays handling: + * 0-n :Time in seconds until the command is issued + * -1:Cancel a pending Off/On command */ + /* XUPS-MIB::xupsRecepOffDelaySecs.n */ + { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.3.%i", + "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + /* XUPS-MIB::xupsRecepOnDelaySecs.n */ + { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.4.%i", + "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + /* Delayed version, parameter is mandatory (so dfl is NULL)! */ + { "outlet.%i.load.off.delay", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.3.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL }, + /* XUPS-MIB::xupsRecepOnDelaySecs.n */ + { "outlet.%i.load.on.delay", 0, 1, ".1.3.6.1.4.1.534.1.12.2.1.4.%i", + NULL, SU_TYPE_CMD | SU_OUTLET, NULL }, + + { "ups.alarms", 0, 1.0, PW_OID_ALARMS, "", + 0, NULL }, /* end of structure. */ { NULL, 0, 0, NULL, NULL, 0, NULL } } ; -mib2nut_info_t powerware = { "pw", PW_MIB_VERSION, "", PW_OID_MODEL_NAME, pw_mib, POWERWARE_SYSOID }; +static alarms_info_t pw_alarms[] = { + /* xupsLowBattery */ + { PW_OID_ALARM_LB, "LB", NULL }, + /* xupsOutputOverload */ + { ".1.3.6.1.4.1.534.1.7.7", "OVER", "Output overload!" }, + /* xupsInternalFailure */ + { ".1.3.6.1.4.1.534.1.7.8", NULL, "Internal failure!" }, + /* xupsBatteryDischarged */ + { ".1.3.6.1.4.1.534.1.7.9", NULL, "Battery discharged!" }, + /* xupsInverterFailure */ + { ".1.3.6.1.4.1.534.1.7.10", NULL, "Inverter failure!" }, + /* xupsOnBypass + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.11", "BYPASS", "On bypass!" }, + /* xupsBypassNotAvailable + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.12", NULL, "Bypass not available!" }, + /* xupsOutputOff + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.13", "OFF", "Output off!" }, + /* xupsInputFailure + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.14", NULL, "Input failure!" }, + /* xupsBuildingAlarm + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.15", NULL, "Building alarm!" }, + /* xupsShutdownImminent */ + { ".1.3.6.1.4.1.534.1.7.16", NULL, "Shutdown imminent!" }, + /* xupsOnInverter + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.17", NULL, "On inverter!" }, + /* xupsBreakerOpen + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.20", NULL, "Breaker open!" }, + /* xupsAlarmBatteryBad */ + { ".1.3.6.1.4.1.534.1.7.23", "RB", "Battery bad!" }, + /* xupsOutputOffAsRequested + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.24", "OFF", "Output off as requested!" }, + /* xupsDiagnosticTestFailed + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.25", NULL, "Diagnostic test failure!" }, + /* xupsCommunicationsLost */ + { ".1.3.6.1.4.1.534.1.7.26", NULL, "Communication with UPS lost!" }, + /* xupsUpsShutdownPending */ + { ".1.3.6.1.4.1.534.1.7.27", NULL, "Shutdown pending!" }, + /* xupsAmbientTempBad */ + { ".1.3.6.1.4.1.534.1.7.29", NULL, "Bad ambient temperature!" }, + /* xupsLossOfRedundancy */ + { ".1.3.6.1.4.1.534.1.7.30", NULL, "Redundancy lost!" }, + /* xupsAlarmTempBad */ + { ".1.3.6.1.4.1.534.1.7.31", NULL, "Bad temperature!" }, + /* xupsAlarmChargerFailed */ + { ".1.3.6.1.4.1.534.1.7.32", NULL, "Charger failure!" }, + /* xupsAlarmFanFailure */ + { ".1.3.6.1.4.1.534.1.7.33", NULL, "Fan failure!" }, + /* xupsAlarmFuseFailure */ + { ".1.3.6.1.4.1.534.1.7.34", NULL, "Fuse failure!" }, + /* xupsPowerSwitchBad */ + { ".1.3.6.1.4.1.534.1.7.35", NULL, "Powerswitch failure!" }, + /* xupsModuleFailure */ + { ".1.3.6.1.4.1.534.1.7.36", NULL, "Parallel or composite module failure!" }, + /* xupsOnAlternatePowerSource + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.37", NULL, "Using alternative power source!" }, + /* xupsAltPowerNotAvailable + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.38", NULL, "Alternative power source unavailable!" }, + /* xupsRemoteTempBad */ + { ".1.3.6.1.4.1.534.1.7.40", NULL, "Bad remote temperature!" }, + /* xupsRemoteHumidityBad */ + { ".1.3.6.1.4.1.534.1.7.41", NULL, "Bad remote humidity!" }, + /* xupsAlarmOutputBad */ + { ".1.3.6.1.4.1.534.1.7.42", NULL, "Bad output condition!" }, + /* xupsAlarmAwaitingPower + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? */ + { ".1.3.6.1.4.1.534.1.7.43", NULL, "Awaiting power!" }, + /* xupsOnMaintenanceBypass + * FIXME: informational (not an alarm), + * to RFC'ed for device.event? + * FIXME: NUT currently doesn't distinguish between Maintenance and + * Automatic Bypass (both published as "ups.alarm: BYPASS) + * Should we make the distinction? */ + { ".1.3.6.1.4.1.534.1.7.44", "BYPASS", "On maintenance bypass!" }, + + + /* end of structure. */ + { NULL, NULL, NULL } +} ; + + +mib2nut_info_t powerware = { "pw", PW_MIB_VERSION, NULL, PW_OID_MODEL_NAME, pw_mib, POWERWARE_SYSOID , pw_alarms }; +mib2nut_info_t pxgx_ups = { "pxgx_ups", PW_MIB_VERSION, NULL, PW_OID_MODEL_NAME, pw_mib, EATON_PXGX_SYSOID , pw_alarms }; diff --git a/drivers/powerware-mib.h b/drivers/powerware-mib.h index 2bd9d28..7189a24 100644 --- a/drivers/powerware-mib.h +++ b/drivers/powerware-mib.h @@ -5,5 +5,6 @@ #include "snmp-ups.h" extern mib2nut_info_t powerware; +extern mib2nut_info_t pxgx_ups; #endif /* POWERWARE_MIB_H */ diff --git a/drivers/raritan-pdu-mib.c b/drivers/raritan-pdu-mib.c index fd832c1..f4e52ac 100644 --- a/drivers/raritan-pdu-mib.c +++ b/drivers/raritan-pdu-mib.c @@ -1,6 +1,6 @@ /* raritan-mib.c - data to monitor Raritan PDUs (Basic and Complex) * - * Copyright (C) 2008 + * Copyright (C) 2008-2019 * Arnaud Quette * * Sponsored by Eaton @@ -25,7 +25,7 @@ #include "raritan-pdu-mib.h" -#define RARITAN_MIB_VERSION "0.4" +#define RARITAN_MIB_VERSION "0.8" /* Raritan MIB * this one uses the same MIB as Eaton Revelation, @@ -34,70 +34,76 @@ #define RARITAN_SYSOID RARITAN_BASE_OID #define RARITAN_OID_MODEL_NAME ".1.3.6.1.4.1.13742.1.1.12.0" -#define DO_OFF 0 -#define DO_ON 1 -#define DO_CYCLE 2 +#define DO_OFF "0" +#define DO_ON "1" +#define DO_CYCLE "2" -static info_lkp_t outlet_status_info[] = { - { -1, "error" }, - { 0, "off" }, - { 1, "on" }, - { 2, "cycling" }, /* transitional status */ - { 0, NULL } +static info_lkp_t raritan_pdu_outlet_status_info[] = { + { -1, "error", NULL, NULL }, + { 0, "off", NULL, NULL }, + { 1, "on", NULL, NULL }, + { 2, "cycling", NULL, NULL }, /* transitional status */ + { 0, NULL, NULL, NULL } }; /* Snmp2NUT lookup table for Raritan MIB */ static snmp_info_t raritan_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + /* Device page */ { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Raritan", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.12.0", - "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, + "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.2.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.6.0", "", + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, /* UPS page */ { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Raritan", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.12.0", - "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, + "Generic SNMP PDU", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.13.0", - "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, + "unknown", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.2.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.1.0", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, { "ups.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", - SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL, NULL }, - { "ups.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.1.6.0", "", - SU_FLAG_STATIC | SU_FLAG_OK, NULL, NULL }, - { "ups.temperature", 0, 1, ".1.3.6.1.4.1.13742.1.3.1.5.0", NULL, 0, NULL, NULL }, + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + { "ups.temperature", 0, 1, ".1.3.6.1.4.1.13742.1.3.1.5.0", NULL, 0, NULL }, /* Outlet page */ { "outlet.id", 0, 1, NULL, "0", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.desc", ST_FLAG_RW | ST_FLAG_STRING, 20, NULL, "All outlets", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, { "outlet.count", 0, 1, ".1.3.6.1.4.1.13742.1.2.1.0", "0", 0, NULL }, - { "outlet.current", 0, 0.001, ".1.3.6.1.4.1.13742.1.3.1.1" ".0", NULL, 0, NULL, NULL }, - { "outlet.voltage", 0, 0.001, ".1.3.6.1.4.1.13742.1.3.1.2" ".0", NULL, 0, NULL, NULL }, - { "outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.13742.1.3.1.3" ".0", NULL, 0, NULL, NULL }, - { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.13742.1.3.1.4" ".0", NULL, 0, NULL, NULL }, + { "outlet.current", 0, 0.001, ".1.3.6.1.4.1.13742.1.3.1.1" ".0", NULL, 0, NULL }, + { "outlet.voltage", 0, 0.001, ".1.3.6.1.4.1.13742.1.3.1.2" ".0", NULL, 0, NULL }, + { "outlet.realpower", 0, 1.0, ".1.3.6.1.4.1.13742.1.3.1.3" ".0", NULL, 0, NULL }, + { "outlet.power", 0, 1.0, ".1.3.6.1.4.1.13742.1.3.1.4" ".0", NULL, 0, NULL }, /* outlet template definition * Caution: the index of the data start at 0, while the name is +1 * ie outlet.1 => .0 */ - { "outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, NULL, NULL }, - { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL, NULL }, - { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.2.2.1.2.%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", NULL, SU_FLAG_OK | SU_OUTLET, &outlet_status_info[0], NULL }, - { "outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.13742.1.2.2.1.4.%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.current.maximum", 0, 0.001, ".1.3.6.1.4.1.13742.1.2.2.1.5.%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.7.%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.voltage", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.6.%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.powerfactor", 0, 0.01, ".1.3.6.1.4.1.13742.1.2.2.1.9.%i", NULL, SU_OUTLET, NULL, NULL }, - { "outlet.%i.power", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.8.%i", NULL, SU_OUTLET, NULL, NULL }, + { "outlet.%i.switchable", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.1.%i", "yes", SU_FLAG_STATIC | SU_OUTLET, NULL }, + { "outlet.%i.id", 0, 1, NULL, "%i", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK | SU_OUTLET, NULL }, + { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.2.2.1.2.%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", NULL, SU_FLAG_OK | SU_OUTLET, &raritan_pdu_outlet_status_info[0] }, + { "outlet.%i.current", 0, 0.001, ".1.3.6.1.4.1.13742.1.2.2.1.4.%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.current.maximum", 0, 0.001, ".1.3.6.1.4.1.13742.1.2.2.1.5.%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.realpower", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.7.%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.voltage", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.6.%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.powerfactor", 0, 0.01, ".1.3.6.1.4.1.13742.1.2.2.1.9.%i", NULL, SU_OUTLET, NULL }, + { "outlet.%i.power", 0, 1.0, ".1.3.6.1.4.1.13742.1.2.2.1.8.%i", NULL, SU_OUTLET, NULL }, /* FIXME: * - delay for startup/shutdown sequence @@ -110,15 +116,15 @@ static snmp_info_t raritan_mib[] = { /* instant commands. */ /* Note that load.cycle might be replaced by / mapped on shutdown.reboot */ /* no counterpart found! - { "outlet.load.off", 0, DO_OFF, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", NULL, SU_TYPE_CMD, NULL, NULL }, - { "outlet.load.on", 0, DO_ON, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", NULL, SU_TYPE_CMD, NULL, NULL }, - { "outlet.load.cycle", 0, DO_CYCLE, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", NULL, SU_TYPE_CMD, NULL, NULL }, */ - { "outlet.%i.load.off", 0, DO_OFF, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, - { "outlet.%i.load.on", 0, DO_ON, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, - { "outlet.%i.load.cycle", 0, DO_CYCLE, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", NULL, SU_TYPE_CMD | SU_OUTLET, NULL, NULL }, + { "outlet.load.off", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", DO_OFF, SU_TYPE_CMD, NULL }, + { "outlet.load.on", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", DO_ON, SU_TYPE_CMD, NULL }, + { "outlet.load.cycle", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.0", DO_CYCLE, SU_TYPE_CMD, NULL }, */ + { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", DO_OFF, SU_TYPE_CMD | SU_OUTLET, NULL }, + { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", DO_ON, SU_TYPE_CMD | SU_OUTLET, NULL }, + { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.13742.1.2.2.1.3.%i", DO_CYCLE, SU_TYPE_CMD | SU_OUTLET, NULL }, /* end of structure. */ - { NULL, 0, 0, NULL, NULL, 0, NULL, NULL } + { NULL, 0, 0, NULL, NULL, 0, NULL } }; -mib2nut_info_t raritan = { "raritan", RARITAN_MIB_VERSION, "", RARITAN_OID_MODEL_NAME, raritan_mib, RARITAN_SYSOID }; +mib2nut_info_t raritan = { "raritan", RARITAN_MIB_VERSION, NULL, RARITAN_OID_MODEL_NAME, raritan_mib, RARITAN_SYSOID, NULL }; diff --git a/drivers/raritan-px2-mib.c b/drivers/raritan-px2-mib.c new file mode 100644 index 0000000..5f7ca2d --- /dev/null +++ b/drivers/raritan-px2-mib.c @@ -0,0 +1,586 @@ +/* raritan-px2-mib.c - subdriver to monitor RARITAN PX2 SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2012 Arnaud Quette + * 2016 Arnaud Quette + * + * Based on initial work and data from Opengear + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "raritan-px2-mib.h" + +#define RARITAN_PX2_MIB_VERSION "0.4" + +#define RARITAN_PX2_MIB_SYSOID ".1.3.6.1.4.1.13742.6" +#define RARITAN_PX2_OID_MODEL_NAME ".1.3.6.1.4.1.13742.6.3.2.1.1.3.1" + +/* info elements */ +/* FIXME: triage between status and alarms, and make it compliant! */ +static info_lkp_t raritanpx2_outlet_status_info[] = { + { -1, "unavailable", NULL, NULL }, + { 0, "open", NULL, NULL }, + { 1, "closed", NULL, NULL }, + { 2, "belowLowerCritical", NULL, NULL }, + { 3, "belowLowerWarning", NULL, NULL }, + { 4, "normal", NULL, NULL }, + { 5, "aboveUpperWarning", NULL, NULL }, + { 6, "aboveUpperCritical", NULL, NULL }, + { 7, "on", NULL, NULL }, + { 8, "off", NULL, NULL }, + { 9, "detected", NULL, NULL }, + { 10, "notDetected", NULL, NULL }, + { 11, "alarmed", NULL, NULL }, + { 12, "ok", NULL, NULL }, + { 13, "marginal", NULL, NULL }, + { 14, "fail", NULL, NULL }, + { 15, "yes", NULL, NULL }, + { 16, "no", NULL, NULL }, + { 17, "standby", NULL, NULL }, + { 18, "one", NULL, NULL }, + { 19, "two", NULL, NULL }, + { 20, "inSync", NULL, NULL }, + { 21, "outOfSync", NULL, NULL }, + { 0, "NULL", NULL, NULL } +}; + +static info_lkp_t raritanpx2_outlet_switchability_info[] = { + { -1, "yes", NULL, NULL }, + { 1, "yes", NULL, NULL }, + { 2, "no", NULL, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* PDU2-MIB Snmp2NUT lookup table */ +static snmp_info_t raritan_px2_mib[] = { + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + /* pduManufacturer.1 = STRING: Raritan */ + { "device.mfr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.2.1", + "Raritan", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* pduModel.1 = STRING: PX2-5475 */ + { "device.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.3.1", + "Raritan PX2 SNMP PDU device", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* pduSerialNumber.1 = STRING: QFC3950619 */ + { "device.serial", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.4.1", + NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "device.type", ST_FLAG_STRING, SU_INFOSIZE, NULL, "pdu", SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + /* pxMACAddress.1 = STRING: 0:d:5d:b:49:0 */ + { "device.macaddr", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.11.1", + NULL, SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + + /* boardVersion.1.mainController.1 = STRING: 0x01 */ + /* FIXME: not compliant! to be RFC'ed */ + { "device.revision", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.4.1.1.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* FIXME: move to device collection! */ + /* Wrong OID! + * { "ups.mfr.date", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.6.1.1.1", "", SU_FLAG_OK | SU_FLAG_STATIC, NULL },*/ + /* boardFirmwareVersion.1.mainController.1 = STRING: 2.4.3.5-40298 */ + { "ups.firmware", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.6.1.1.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + /* pduName.1 = STRING: my PX */ + { "ups.id", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.13.1", "", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.3.1", + "Raritan PX2 SNMP PDU device", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + + /* Input data: + * Units are given in inletSensorUnits.1.1.%i + * Value should be scaled by inletSensorDecimalDigits.1.1.%i + * For example, if the value is 1 and inletSensorDecimalDigits is 2, then actual value is 0.01. */ + /* measurementsInletSensorValue.1.1.rmsCurrent = Gauge32: 10 (A) */ + { "input.load", 0, 0.1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* measurementsInletSensorValue.1.1.rmsVoltage = Gauge32: 119 (V) */ + { "input.voltage", 0, 1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* measurementsInletSensorValue.1.1.activePower = Gauge32: 10 (W) */ + { "input.realpower", 0, 1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* measurementsInletSensorValue.1.1.apparentPower = Gauge32: 122 (VA) */ + { "input.power", 0, 1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* measurementsInletSensorValue.1.1.powerFactor = Gauge32: 8 (none) */ + /* FIXME: need RFC! */ + { "input.powerfactor", 0, 0.01, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* measurementsInletSensorValue.1.1.activeEnergy = Gauge32: 193359 (wattHour) */ + /* { "unmapped.measurementsInletSensorValue", 0, 1, ".1.3.6.1.4.1.13742.6.5.2.3.1.4.1.1.8", NULL, SU_FLAG_OK, NULL }, */ + + /* inletPlug.1.1 = INTEGER: plugIEC320C20(6) */ + /* FIXME: need RFC (input.type | [input.]inlet.type...) and standardization + * { "unmapped.inletPlug", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.3.1.4.1.1", NULL, SU_FLAG_OK, NULL },*/ + + /* outletCount.1 = INTEGER: 24 */ + { "outlet.count", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.4.1", "0", SU_FLAG_STATIC | SU_FLAG_OK, NULL }, + + /* outlet template definition + * Indexes start from 1, ie outlet.1 => .1 */ + /* Note: the first definition is used to determine the base index (ie 0 or 1) */ + /* outletName.1.%i = STRING: */ + { "outlet.%i.desc", ST_FLAG_RW | ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.3.1.%i", NULL, SU_OUTLET, NULL }, + /* outletSwitchingState.1.%i = INTEGER: on(7) */ + { "outlet.%i.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.4.1.2.1.3.1.%i", NULL, SU_OUTLET, &raritanpx2_outlet_status_info[0] }, + /* outletLabel.1.%i = STRING: 1 */ + { "outlet.%i.id", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.2.1.%i", "%i", SU_FLAG_STATIC | SU_OUTLET | SU_FLAG_OK, NULL }, + + /* outletReceptacle.1.1 = INTEGER: receptacleNEMA520R(37) */ + /* FIXME: need RFC and standardization + * { "outlet.%i.type", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.4.1.%i", NULL, SU_OUTLET | SU_FLAG_OK, NULL }, */ + + /* RMS Current (divide by 10). e.g. 5 == 0.5A */ + /* measurementsOutletSensorValue.1.%i.rmsCurrent = Gauge32: 10 */ + { "outlet.%i.current", 0, 0.1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.1", NULL, SU_OUTLET | SU_FLAG_OK, NULL }, + /* measurementsOutletSensorValue.1.%i.rmsVoltage = Gauge32: 119 */ + { "outlet.%i.voltage", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.4", "%i", SU_OUTLET | SU_FLAG_OK, NULL }, + /* measurementsOutletSensorValue.1.%i.activePower = Gauge32: 10 */ + { "outlet.%i.power", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.5", "%i", SU_OUTLET | SU_FLAG_OK, NULL }, + /* measurementsOutletSensorValue.1.%i.apparentPower = Gauge32: 122 */ + { "outlet.%i.realpower", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.6", "%i", SU_OUTLET | SU_FLAG_OK, NULL }, + /* measurementsOutletSensorValue.1.%i.powerFactor = Gauge32: 8 */ + { "outlet.%i.powerfactor", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.%i.7", "%i", SU_OUTLET | SU_FLAG_OK, NULL }, + /* measurementsOutletSensorValue.1.1.activeEnergy = Gauge32: 89890 */ + /* FIXME: + * { "unmapped.measurementsOutletSensorValue", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.1.8", NULL, SU_FLAG_OK, NULL }, */ + /* measurementsOutletSensorValue.1.1.onOff = Gauge32: 0 */ + /* FIXME: + * { "unmapped.measurementsOutletSensorValue", 0, 1, ".1.3.6.1.4.1.13742.6.5.4.3.1.4.1.1.14", NULL, SU_FLAG_OK, NULL }, */ + + /* outletSwitchable.1.%i = INTEGER: true(1) */ + { "outlet.%i.switchable", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.28.1.%i", "no", SU_FLAG_STATIC | SU_OUTLET | SU_FLAG_OK, &raritanpx2_outlet_switchability_info[0] }, + + /* instant commands. */ + /* switchingOperation.1.1 = INTEGER: on(1) */ + { "outlet.%i.load.off", 0, 1, ".1.3.6.1.4.1.13742.6.4.1.2.1.2.1.%i", "0", SU_TYPE_CMD | SU_OUTLET, NULL }, + { "outlet.%i.load.on", 0, 1, ".1.3.6.1.4.1.13742.6.4.1.2.1.2.1.%i", "1", SU_TYPE_CMD | SU_OUTLET, NULL }, + { "outlet.%i.load.cycle", 0, 1, ".1.3.6.1.4.1.13742.6.4.1.2.1.2.1.%i", "2", SU_TYPE_CMD | SU_OUTLET, NULL }, + +#ifdef DEBUG + /* pduCount.0 = INTEGER: 1 */ + /* FIXME: part of daisychain support, RFC device.count */ + { "device.count", 0, 1, ".1.3.6.1.4.1.13742.6.3.1.0", NULL, SU_FLAG_OK, NULL }, + + /* pduRatedVoltage.1 = STRING: 100-120V */ + { "unmapped.pduRatedVoltage", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* pduRatedCurrent.1 = STRING: 16A */ + { "unmapped.pduRatedCurrent", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.6.1", NULL, SU_FLAG_OK, NULL }, + /* pduRatedFrequency.1 = STRING: 50/60Hz */ + { "unmapped.pduRatedFrequency", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.7.1", NULL, SU_FLAG_OK, NULL }, + /* pduRatedVA.1 = STRING: 1.6-1.9kVA */ + { "unmapped.pduRatedVA", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.8.1", NULL, SU_FLAG_OK, NULL }, + /* pduImage.1 = STRING: */ + { "unmapped.pduImage", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.1.1.9.1", NULL, SU_FLAG_OK, NULL }, + /* inletCount.1 = INTEGER: 1 */ + { "unmapped.inletCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* overCurrentProtectorCount.1 = INTEGER: 0 */ + { "unmapped.overCurrentProtectorCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.3.1", NULL, SU_FLAG_OK, NULL }, + + /* inletControllerCount.1 = INTEGER: 0 */ + { "unmapped.inletControllerCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* outletControllerCount.1 = INTEGER: 6 */ + { "unmapped.outletControllerCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.6.1", NULL, SU_FLAG_OK, NULL }, + /* externalSensorCount.1 = INTEGER: 16 */ + { "unmapped.externalSensorCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.7.1", NULL, SU_FLAG_OK, NULL }, + /* pxIPAddress.1 = IpAddress: 192.168.20.188 */ + { "unmapped.pxIPAddress", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.8.1", NULL, SU_FLAG_OK, NULL }, + /* netmask.1 = IpAddress: 255.255.255.0 */ + { "unmapped.netmask", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.9.1", NULL, SU_FLAG_OK, NULL }, + /* gateway.1 = IpAddress: 192.168.20.254 */ + { "unmapped.gateway", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.10.1", NULL, SU_FLAG_OK, NULL }, + /* utcOffset.1 = STRING: -5:00 */ + { "unmapped.utcOffset", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.12.1", NULL, SU_FLAG_OK, NULL }, + /* externalSensorsZCoordinateUnits.1 = INTEGER: rackUnits(0) */ + { "unmapped.externalSensorsZCoordinateUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.34.1", NULL, SU_FLAG_OK, NULL }, + /* unitDeviceCapabilities.1 = BITS: 00 00 00 00 00 00 */ + { "unmapped.unitDeviceCapabilities", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.35.1", NULL, SU_FLAG_OK, NULL }, + /* outletSequencingDelay.1 = Gauge32: 200 */ + { "unmapped.outletSequencingDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.36.1", NULL, SU_FLAG_OK, NULL }, + /* globalOutletPowerCyclingPowerOffPeriod.1 = Gauge32: 10 */ + { "unmapped.globalOutletPowerCyclingPowerOffPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.37.1", NULL, SU_FLAG_OK, NULL }, + /* globalOutletStateOnStartup.1 = INTEGER: lastKnownState(2) */ + { "unmapped.globalOutletStateOnStartup", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.38.1", NULL, SU_FLAG_OK, NULL }, + /* outletPowerupSequence.1 = STRING: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24 */ + { "unmapped.outletPowerupSequence", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.39.1", NULL, SU_FLAG_OK, NULL }, + /* pduPowerCyclingPowerOffPeriod.1 = Gauge32: 3 */ + { "unmapped.pduPowerCyclingPowerOffPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.40.1", NULL, SU_FLAG_OK, NULL }, + /* pduDaisychainMemberType.1 = INTEGER: standalone(0) */ + { "unmapped.pduDaisychainMemberType", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.41.1", NULL, SU_FLAG_OK, NULL }, + /* managedExternalSensorCount.1 = INTEGER: 0 */ + { "unmapped.managedExternalSensorCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.42.1", NULL, SU_FLAG_OK, NULL }, + /* pxInetAddressType.1 = INTEGER: ipv4(1) */ + { "unmapped.pxInetAddressType", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.50.1", NULL, SU_FLAG_OK, NULL }, + /* pxInetIPAddress.1 = Hex-STRING: C0 A8 14 BC */ + { "unmapped.pxInetIPAddress", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.51.1", NULL, SU_FLAG_OK, NULL }, + /* pxInetNetmask.1 = Hex-STRING: FF FF FF 00 */ + { "unmapped.pxInetNetmask", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.52.1", NULL, SU_FLAG_OK, NULL }, + /* pxInetGateway.1 = Hex-STRING: C0 A8 14 FE */ + { "unmapped.pxInetGateway", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.2.1.53.1", NULL, SU_FLAG_OK, NULL }, + /* loadShedding.1 = INTEGER: false(2) */ + { "unmapped.loadShedding", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.55.1", NULL, SU_FLAG_OK, NULL }, + /* serverCount.1 = INTEGER: 8 */ + { "unmapped.serverCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.56.1", NULL, SU_FLAG_OK, NULL }, + /* inrushGuardDelay.1 = Gauge32: 200 */ + { "unmapped.inrushGuardDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.57.1", NULL, SU_FLAG_OK, NULL }, + /* cascadedDeviceConnected.1 = INTEGER: false(2) */ + { "unmapped.cascadedDeviceConnected", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.58.1", NULL, SU_FLAG_OK, NULL }, + /* synchronizeWithNTPServer.1 = INTEGER: false(2) */ + { "unmapped.synchronizeWithNTPServer", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.59.1", NULL, SU_FLAG_OK, NULL }, + /* useDHCPProvidedNTPServer.1 = INTEGER: true(1) */ + { "unmapped.useDHCPProvidedNTPServer", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.60.1", NULL, SU_FLAG_OK, NULL }, + /* firstNTPServerAddressType.1 = INTEGER: unknown(0) */ + { "unmapped.firstNTPServerAddressType", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.61.1", NULL, SU_FLAG_OK, NULL }, + /* firstNTPServerAddress.1 = "" */ + { "unmapped.firstNTPServerAddress", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.62.1", NULL, SU_FLAG_OK, NULL }, + /* secondNTPServerAddressType.1 = INTEGER: unknown(0) */ + { "unmapped.secondNTPServerAddressType", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.63.1", NULL, SU_FLAG_OK, NULL }, + /* secondNTPServerAddress.1 = "" */ + { "unmapped.secondNTPServerAddress", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.64.1", NULL, SU_FLAG_OK, NULL }, + /* wireCount.1 = INTEGER: 0 */ + { "unmapped.wireCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.65.1", NULL, SU_FLAG_OK, NULL }, + /* transferSwitchCount.1 = INTEGER: 0 */ + { "unmapped.transferSwitchCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.2.1.66.1", NULL, SU_FLAG_OK, NULL }, + + /* boardVersion.1.outletController.{1-6} = STRING: 60 */ + { "unmapped.boardVersion", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.4.1.3.1", NULL, SU_FLAG_OK, NULL }, + + /* boardFirmwareVersion.1.outletController.{1-6} = STRING: 1F */ + { "unmapped.boardFirmwareVersion", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.2.3.1.6.1.3.1", NULL, SU_FLAG_OK, NULL }, + + /* boardFirmwareTimeStamp.1.mainController.1 = Gauge32: 0 */ + { "unmapped.boardFirmwareTimeStamp", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.3.1.8.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* dataLogging.1 = INTEGER: true(1) */ + { "unmapped.dataLogging", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* measurementPeriod.1 = INTEGER: 1 */ + { "unmapped.measurementPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.2.1", NULL, SU_FLAG_OK, NULL }, + /* measurementsPerLogEntry.1 = INTEGER: 60 */ + { "unmapped.measurementsPerLogEntry", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.3.1", NULL, SU_FLAG_OK, NULL }, + /* logSize.1 = INTEGER: 120 */ + { "unmapped.logSize", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.4.1", NULL, SU_FLAG_OK, NULL }, + /* dataLoggingEnableForAllSensors.1 = INTEGER: false(2) */ + { "unmapped.dataLoggingEnableForAllSensors", 0, 1, ".1.3.6.1.4.1.13742.6.3.2.4.1.5.1", NULL, SU_FLAG_OK, NULL }, + /* inletLabel.1.1 = STRING: I1 */ + { "unmapped.inletLabel", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.2.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletName.1.1 = STRING: */ + { "unmapped.inletName", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.3.1.1", NULL, SU_FLAG_OK, NULL }, + + /* inletPoleCount.1.1 = INTEGER: 2 */ + { "unmapped.inletPoleCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.3.1.5.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletRatedVoltage.1.1 = STRING: 100-120V */ + { "unmapped.inletRatedVoltage", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.6.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletRatedCurrent.1.1 = STRING: 16A */ + { "unmapped.inletRatedCurrent", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.7.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletDeviceCapabilities.1.1 = BITS: 9F 00 00 00 00 00 rmsCurrent(0) rmsVoltage(3) activePower(4) apparentPower(5) powerFactor(6) activeEnergy(7) */ + { "unmapped.inletDeviceCapabilities", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.3.1.10.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletPoleCapabilities.1.1 = BITS: 00 00 00 00 00 00 */ + { "unmapped.inletPoleCapabilities", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.3.1.11.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletPlugDescriptor.1.1 = STRING: IEC 60320 C20 */ + { "unmapped.inletPlugDescriptor", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.3.3.1.12.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLogAvailable.1.1.rmsCurrent = INTEGER: true(1) */ + { "unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLogAvailable.1.1.rmsVoltage = INTEGER: true(1) */ + { "unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLogAvailable.1.1.activePower = INTEGER: true(1) */ + { "unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLogAvailable.1.1.apparentPower = INTEGER: true(1) */ + { "unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLogAvailable.1.1.powerFactor = INTEGER: true(1) */ + { "unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLogAvailable.1.1.activeEnergy = INTEGER: true(1) */ + { "unmapped.inletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.4.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUnits.1.1.rmsCurrent = INTEGER: amp(2) */ + { "unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUnits.1.1.rmsVoltage = INTEGER: volt(1) */ + { "unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUnits.1.1.activePower = INTEGER: watt(3) */ + { "unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUnits.1.1.apparentPower = INTEGER: voltamp(4) */ + { "unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUnits.1.1.powerFactor = INTEGER: none(-1) */ + { "unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUnits.1.1.activeEnergy = INTEGER: wattHour(5) */ + { "unmapped.inletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.6.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorDecimalDigits.1.1.rmsCurrent = Gauge32: 1 */ + { "unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorDecimalDigits.1.1.rmsVoltage = Gauge32: 0 */ + { "unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorDecimalDigits.1.1.activePower = Gauge32: 0 */ + { "unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorDecimalDigits.1.1.apparentPower = Gauge32: 0 */ + { "unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorDecimalDigits.1.1.powerFactor = Gauge32: 2 */ + { "unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorDecimalDigits.1.1.activeEnergy = Gauge32: 0 */ + { "unmapped.inletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.7.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorAccuracy.1.1.rmsCurrent = Gauge32: 100 */ + { "unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorAccuracy.1.1.rmsVoltage = Gauge32: 100 */ + { "unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorAccuracy.1.1.activePower = Gauge32: 300 */ + { "unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorAccuracy.1.1.apparentPower = Gauge32: 200 */ + { "unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorAccuracy.1.1.powerFactor = Gauge32: 500 */ + { "unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorAccuracy.1.1.activeEnergy = Gauge32: 100 */ + { "unmapped.inletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.8.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorResolution.1.1.rmsCurrent = Gauge32: 1 */ + { "unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorResolution.1.1.rmsVoltage = Gauge32: 1 */ + { "unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorResolution.1.1.activePower = Gauge32: 1 */ + { "unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorResolution.1.1.apparentPower = Gauge32: 1 */ + { "unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorResolution.1.1.powerFactor = Gauge32: 1 */ + { "unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorResolution.1.1.activeEnergy = Gauge32: 1 */ + { "unmapped.inletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.9.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorTolerance.1.1.rmsCurrent = Gauge32: 120 */ + { "unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorTolerance.1.1.rmsVoltage = Gauge32: 5 */ + { "unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorTolerance.1.1.activePower = Gauge32: 120 */ + { "unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorTolerance.1.1.apparentPower = Gauge32: 120 */ + { "unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorTolerance.1.1.powerFactor = Gauge32: 50 */ + { "unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorTolerance.1.1.activeEnergy = Gauge32: 120 */ + { "unmapped.inletSensorTolerance", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.10.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorMaximum.1.1.rmsCurrent = Gauge32: 7680 */ + { "unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorMaximum.1.1.rmsVoltage = Gauge32: 264 */ + { "unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorMaximum.1.1.activePower = Gauge32: 202752 */ + { "unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorMaximum.1.1.apparentPower = Gauge32: 202752 */ + { "unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorMaximum.1.1.powerFactor = Gauge32: 100 */ + { "unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorMaximum.1.1.activeEnergy = Gauge32: 4294967295 */ + { "unmapped.inletSensorMaximum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.11.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorMinimum.1.1.rmsCurrent = Gauge32: 0 */ + { "unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorMinimum.1.1.rmsVoltage = Gauge32: 0 */ + { "unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorMinimum.1.1.activePower = Gauge32: 0 */ + { "unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorMinimum.1.1.apparentPower = Gauge32: 0 */ + { "unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorMinimum.1.1.powerFactor = Gauge32: 0 */ + { "unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorMinimum.1.1.activeEnergy = Gauge32: 0 */ + { "unmapped.inletSensorMinimum", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.12.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorHysteresis.1.1.rmsCurrent = Gauge32: 10 */ + { "unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorHysteresis.1.1.rmsVoltage = Gauge32: 2 */ + { "unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorHysteresis.1.1.activePower = Gauge32: 0 */ + { "unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorHysteresis.1.1.apparentPower = Gauge32: 0 */ + { "unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorHysteresis.1.1.powerFactor = Gauge32: 0 */ + { "unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorHysteresis.1.1.activeEnergy = Gauge32: 0 */ + { "unmapped.inletSensorHysteresis", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.13.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorStateChangeDelay.1.1.rmsCurrent = Gauge32: 0 */ + { "unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorStateChangeDelay.1.1.rmsVoltage = Gauge32: 0 */ + { "unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorStateChangeDelay.1.1.activePower = Gauge32: 0 */ + { "unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorStateChangeDelay.1.1.apparentPower = Gauge32: 0 */ + { "unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorStateChangeDelay.1.1.powerFactor = Gauge32: 0 */ + { "unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorStateChangeDelay.1.1.activeEnergy = Gauge32: 0 */ + { "unmapped.inletSensorStateChangeDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.14.1.1.8", NULL, SU_FLAG_OK, NULL }, + +/* Inlet thresholds */ + /* inletSensorLowerCriticalThreshold.1.1.rmsCurrent = Gauge32: 0 */ + { "unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLowerCriticalThreshold.1.1.rmsVoltage = Gauge32: 94 */ + { "unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLowerCriticalThreshold.1.1.activePower = Gauge32: 0 */ + { "unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLowerCriticalThreshold.1.1.apparentPower = Gauge32: 0 */ + { "unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLowerCriticalThreshold.1.1.powerFactor = Gauge32: 0 */ + { "unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLowerCriticalThreshold.1.1.activeEnergy = Gauge32: 0 */ + { "unmapped.inletSensorLowerCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.21.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLowerWarningThreshold.1.1.rmsCurrent = Gauge32: 0 */ + { "unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLowerWarningThreshold.1.1.rmsVoltage = Gauge32: 97 */ + { "unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLowerWarningThreshold.1.1.activePower = Gauge32: 0 */ + { "unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLowerWarningThreshold.1.1.apparentPower = Gauge32: 0 */ + { "unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLowerWarningThreshold.1.1.powerFactor = Gauge32: 0 */ + { "unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorLowerWarningThreshold.1.1.activeEnergy = Gauge32: 0 */ + { "unmapped.inletSensorLowerWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.22.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUpperCriticalThreshold.1.1.rmsCurrent = Gauge32: 128 */ + { "unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUpperCriticalThreshold.1.1.rmsVoltage = Gauge32: 127 */ + { "unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUpperCriticalThreshold.1.1.activePower = Gauge32: 0 */ + { "unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUpperCriticalThreshold.1.1.apparentPower = Gauge32: 0 */ + { "unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUpperCriticalThreshold.1.1.powerFactor = Gauge32: 0 */ + { "unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUpperCriticalThreshold.1.1.activeEnergy = Gauge32: 0 */ + { "unmapped.inletSensorUpperCriticalThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.23.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUpperWarningThreshold.1.1.rmsCurrent = Gauge32: 104 */ + { "unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUpperWarningThreshold.1.1.rmsVoltage = Gauge32: 124 */ + { "unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUpperWarningThreshold.1.1.activePower = Gauge32: 0 */ + { "unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUpperWarningThreshold.1.1.apparentPower = Gauge32: 0 */ + { "unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUpperWarningThreshold.1.1.powerFactor = Gauge32: 0 */ + { "unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorUpperWarningThreshold.1.1.activeEnergy = Gauge32: 0 */ + { "unmapped.inletSensorUpperWarningThreshold", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.24.1.1.8", NULL, SU_FLAG_OK, NULL }, + /* inletSensorEnabledThresholds.1.1.rmsCurrent = BITS: 30 upperWarning(2) upperCritical(3) */ + { "unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.1", NULL, SU_FLAG_OK, NULL }, + /* inletSensorEnabledThresholds.1.1.rmsVoltage = BITS: F0 lowerCritical(0) lowerWarning(1) upperWarning(2) upperCritical(3) */ + { "unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.4", NULL, SU_FLAG_OK, NULL }, + /* inletSensorEnabledThresholds.1.1.activePower = BITS: 00 */ + { "unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.5", NULL, SU_FLAG_OK, NULL }, + /* inletSensorEnabledThresholds.1.1.apparentPower = BITS: 00 */ + { "unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.6", NULL, SU_FLAG_OK, NULL }, + /* inletSensorEnabledThresholds.1.1.powerFactor = BITS: 00 */ + { "unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.7", NULL, SU_FLAG_OK, NULL }, + /* inletSensorEnabledThresholds.1.1.activeEnergy = BITS: 00 */ + { "unmapped.inletSensorEnabledThresholds", 0, 1, ".1.3.6.1.4.1.13742.6.3.3.4.1.25.1.1.8", NULL, SU_FLAG_OK, NULL }, + + /* outletPoleCount.1.{1-24} = INTEGER: 2 */ + { "unmapped.outletPoleCount", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.5.1.1", NULL, SU_FLAG_OK, NULL }, + + /* outletRatedVoltage.1.{1-24} = STRING: 100-120V */ + { "unmapped.outletRatedVoltage", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.6.1.1", NULL, SU_FLAG_OK, NULL }, + + /* outletRatedCurrent.1.{1-24} = STRING: 16A */ + { "unmapped.outletRatedCurrent", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.7.1.1", NULL, SU_FLAG_OK, NULL }, + + /* outletDeviceCapabilities.1.{1-24} = BITS: 9F 04 00 00 00 00 rmsCurrent(0) rmsVoltage(3) activePower(4) apparentPower(5) powerFactor(6) activeEnergy(7) onOff(13) */ + { "unmapped.outletDeviceCapabilities", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.10.1.1", NULL, SU_FLAG_OK, NULL }, + + /* outletPowerCyclingPowerOffPeriod.1.{1-24} = Gauge32: 10 */ + { "unmapped.outletPowerCyclingPowerOffPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.12.1.1", NULL, SU_FLAG_OK, NULL }, + + /* outletStateOnStartup.1.{1-24} = INTEGER: globalOutletStateOnStartup(3) */ + { "unmapped.outletStateOnStartup", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.13.1.1", NULL, SU_FLAG_OK, NULL }, + + /* outletUseGlobalPowerCyclingPowerOffPeriod.1.{1-24} = INTEGER: true(1) */ + { "unmapped.outletUseGlobalPowerCyclingPowerOffPeriod", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.14.1.1", NULL, SU_FLAG_OK, NULL }, + + /* outletReceptacleDescriptor.1.{1-24} = STRING: NEMA 5-20R */ + { "unmapped.outletReceptacleDescriptor", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.13742.6.3.5.3.1.29.1.1", NULL, SU_FLAG_OK, NULL }, + + /* outletNonCritical.1.{1-24} = INTEGER: false(2) */ + { "unmapped.outletNonCritical", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.30.1.1", NULL, SU_FLAG_OK, NULL }, + + /* outletSequenceDelay.1.{1-24} = Gauge32: 0 */ + { "unmapped.outletSequenceDelay", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.3.1.32.1.%i", NULL, SU_FLAG_OK, NULL }, + + /* outletSensorLogAvailable.1.{1-24}.rmsCurrent = INTEGER: true(1) */ + { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.1", NULL, SU_FLAG_OK, NULL }, + /* outletSensorLogAvailable.1.{1-24}.rmsVoltage = INTEGER: true(1) */ + { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.4", NULL, SU_FLAG_OK, NULL }, + /* outletSensorLogAvailable.1.{1-24}.activePower = INTEGER: true(1) */ + { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.5", NULL, SU_FLAG_OK, NULL }, + /* outletSensorLogAvailable.1.{1-24}.apparentPower = INTEGER: true(1) */ + { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.6", NULL, SU_FLAG_OK, NULL }, + /* outletSensorLogAvailable.1.{1-24}.powerFactor = INTEGER: true(1) */ + { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.7", NULL, SU_FLAG_OK, NULL }, + /* outletSensorLogAvailable.1.{1-24}.activeEnergy = INTEGER: true(1) */ + { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.8", NULL, SU_FLAG_OK, NULL }, + /* outletSensorLogAvailable.1.{1-24}.onOff = INTEGER: true(1) */ + { "unmapped.outletSensorLogAvailable", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.4.1.%i.14", NULL, SU_FLAG_OK, NULL }, + + /* outletSensorUnits.1.{1-24}.rmsCurrent = INTEGER: amp(2) */ + { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.1", NULL, SU_FLAG_OK, NULL }, + /* outletSensorUnits.1.{1-24}.rmsVoltage = INTEGER: volt(1) */ + { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.4", NULL, SU_FLAG_OK, NULL }, + /* outletSensorUnits.1.{1-24}.activePower = INTEGER: watt(3) */ + { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.5", NULL, SU_FLAG_OK, NULL }, + /* outletSensorUnits.1.{1-24}.apparentPower = INTEGER: voltamp(4) */ + { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.6", NULL, SU_FLAG_OK, NULL }, + /* outletSensorUnits.1.{1-24}.powerFactor = INTEGER: none(-1) */ + { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.7", NULL, SU_FLAG_OK, NULL }, + /* outletSensorUnits.1.{1-24}.activeEnergy = INTEGER: wattHour(5) */ + { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.8", NULL, SU_FLAG_OK, NULL }, + /* outletSensorUnits.1.{1-24}.onOff = INTEGER: none(-1) */ + { "unmapped.outletSensorUnits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.6.1.%i.14", NULL, SU_FLAG_OK, NULL }, + + /* outletSensorDecimalDigits.1.{1-24}.rmsCurrent = Gauge32: 1 */ + { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.1", NULL, SU_FLAG_OK, NULL }, + /* outletSensorDecimalDigits.1.{1-24}.rmsVoltage = Gauge32: 0 */ + { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.4", NULL, SU_FLAG_OK, NULL }, + /* outletSensorDecimalDigits.1.{1-24}.activePower = Gauge32: 0 */ + { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.5", NULL, SU_FLAG_OK, NULL }, + /* outletSensorDecimalDigits.1.{1-24}.apparentPower = Gauge32: 0 */ + { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.6", NULL, SU_FLAG_OK, NULL }, + /* outletSensorDecimalDigits.1.{1-24}.powerFactor = Gauge32: 2 */ + { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.7", NULL, SU_FLAG_OK, NULL }, + /* outletSensorDecimalDigits.1.{1-24}.activeEnergy = Gauge32: 0 */ + { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.8", NULL, SU_FLAG_OK, NULL }, + /* outletSensorDecimalDigits.1.{1-24}.onOff = Gauge32: 0 */ + { "unmapped.outletSensorDecimalDigits", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.7.1.%i.14", NULL, SU_FLAG_OK, NULL }, + + /* outletSensorAccuracy.1.{1-24}.rmsCurrent = Gauge32: 100 */ + { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.1", NULL, SU_FLAG_OK, NULL }, + /* outletSensorAccuracy.1.{1-24}.rmsVoltage = Gauge32: 100 */ + { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.4", NULL, SU_FLAG_OK, NULL }, + /* outletSensorAccuracy.1.{1-24}.activePower = Gauge32: 300 */ + { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.5", NULL, SU_FLAG_OK, NULL }, + /* outletSensorAccuracy.1.{1-24}.apparentPower = Gauge32: 200 */ + { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.6", NULL, SU_FLAG_OK, NULL }, + /* outletSensorAccuracy.1.{1-24}.powerFactor = Gauge32: 100 */ + { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.7", NULL, SU_FLAG_OK, NULL }, + /* outletSensorAccuracy.1.{1-24}.activeEnergy = Gauge32: 100 */ + { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.8", NULL, SU_FLAG_OK, NULL }, + /* outletSensorAccuracy.1.{1-24}.onOff = Gauge32: 0 */ + { "unmapped.outletSensorAccuracy", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.8.1.%i.14", NULL, SU_FLAG_OK, NULL }, + + /* outletSensorResolution.1.{1-24}.rmsCurrent = Gauge32: 1 */ + { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.1", NULL, SU_FLAG_OK, NULL }, + /* outletSensorResolution.1.{1-24}.rmsVoltage = Gauge32: 1 */ + { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.4", NULL, SU_FLAG_OK, NULL }, + /* outletSensorResolution.1.{1-24}.activePower = Gauge32: 1 */ + { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.5", NULL, SU_FLAG_OK, NULL }, + /* outletSensorResolution.1.{1-24}.apparentPower = Gauge32: 1 */ + { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.6", NULL, SU_FLAG_OK, NULL }, + /* outletSensorResolution.1.{1-24}.powerFactor = Gauge32: 1 */ + { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.7", NULL, SU_FLAG_OK, NULL }, + /* outletSensorResolution.1.{1-24}.activeEnergy = Gauge32: 1 */ + { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.8", NULL, SU_FLAG_OK, NULL }, + /* outletSensorResolution.1.{1-24}.onOff = Gauge32: 0 */ + { "unmapped.outletSensorResolution", 0, 1, ".1.3.6.1.4.1.13742.6.3.5.4.1.9.1.%i.14", NULL, SU_FLAG_OK, NULL }, + + /* end of interesting data + * the rest is 18MB of verbose log and satellite data */ + + /* Note: All reliabilityXXX data were removed */ +#endif /* DEBUG */ + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + +mib2nut_info_t raritan_px2 = { "raritan-px2", RARITAN_PX2_MIB_VERSION, NULL, RARITAN_PX2_OID_MODEL_NAME, raritan_px2_mib, RARITAN_PX2_MIB_SYSOID, NULL }; diff --git a/drivers/raritan-px2-mib.h b/drivers/raritan-px2-mib.h new file mode 100644 index 0000000..5402bd8 --- /dev/null +++ b/drivers/raritan-px2-mib.h @@ -0,0 +1,30 @@ +/* raritan-px2-mib.h - subdriver to monitor RARITAN PX2 SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2012 Arnaud Quette + * 2016 Arnaud Quette + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef RARITAN_PX2_MIB_H +#define RARITAN_PX2_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t raritan_px2; + +#endif /* RARITAN_PX2_MIB_H */ diff --git a/drivers/rhino.c b/drivers/rhino.c index 707432b..c99770d 100644 --- a/drivers/rhino.c +++ b/drivers/rhino.c @@ -22,20 +22,22 @@ 2005/10/26 - Version 0.40 - Operational-2 release 2005/11/29 - Version 0.50 - rhino commands release - + http://www.microsol.com.br */ +#include "config.h" /* must be the first header */ + #include -#include #include "main.h" #include "serial.h" +#include "nut_float.h" #include "timehead.h" #define DRIVER_NAME "Microsol Rhino UPS driver" -#define DRIVER_VERSION "0.51" +#define DRIVER_VERSION "0.52" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -129,10 +131,10 @@ static unsigned char EventosRede, EventosSaida, EventosBateria; /* Methods */ static void ScanReceivePack(void); static int AutonomyCalc( int ); -static void CommReceive(const unsigned char*, int ); +static void CommReceive(const unsigned char*, ssize_t); static void getbaseinfo(void); static void getupdateinfo(void); - + static unsigned char RecPack[37]; /* comment on english language */ @@ -160,327 +162,327 @@ static unsigned char RecPack[37]; static int AutonomyCalc( int ia ) /* all models */ { + int result = 0; + double auton, calc, currin; - int result = 0; - double auton, calc, currin; - - if( ia ) - { - if( ( BattVoltage == 0 ) ) - result = 0; - else + if( ia ) { - calc = ( OutVoltage * OutCurrent )* 1.0 / ( 0.08 * BattVoltage ); - auton = pow( calc, 1.18 ); - if( ( auton == 0 ) ) - result = 0; - else - { - auton = 1.0 / auton; - auton = auton * 11.07; - calc = ( BattVoltage * 1.0 / 10 ) - 168; - result = (int) ( auton * calc * 2.5 ); - } - } - } - else - { - currin = ( UtilPowerOut + ConstInt ) *1.0 / Vin; - auton = ( ( ( AmpH *1.0 / currin ) * 60 * ( ( BattVoltage - VbatMin ) * 1.0 /( VbatNom - VbatMin ) ) * FM ) + FA ); - if( ( BattVoltage > 129 ) || ( BattVoltage < 144 ) ) - result = 133; - else - result = (int) auton; - } - - return result; + if( d_equal(BattVoltage, 0) ) + result = 0; + else + { + calc = ( OutVoltage * OutCurrent )* 1.0 / ( 0.08 * BattVoltage ); + auton = pow( calc, 1.18 ); + if( d_equal(auton, 0) ) + result = 0; + else + { + auton = 1.0 / auton; + auton = auton * 11.07; + calc = ( BattVoltage * 1.0 / 10 ) - 168; + result = (int) ( auton * calc * 2.5 ); + } + } + } + else + { + currin = ( UtilPowerOut + ConstInt ) *1.0 / Vin; + auton = ( ( ( AmpH *1.0 / currin ) * 60 * ( ( BattVoltage - VbatMin ) * 1.0 /( VbatNom - VbatMin ) ) * FM ) + FA ); + if( ( BattVoltage > 129 ) && ( BattVoltage < 144 ) ) + result = 133; + else + result = (int) auton; + } + return result; } /* Treat received package */ static void ScanReceivePack( void ) { + /* model independent data */ - /* model independent data */ + Year = RecPack[31] + ( RecPack[32] * 100 ); + Month = RecPack[30]; + Day = RecPack[29]; - Year = RecPack[31] + ( RecPack[32] * 100 ); - Month = RecPack[30]; - Day = RecPack[29]; + /* UPS internal time */ + ihour = RecPack[26]; + imin = RecPack[27]; + isec = RecPack[28]; - /* UPS internal time */ - ihour = RecPack[26]; - imin = RecPack[27]; - isec = RecPack[28]; + /* Flag1 */ + /* SobreTemp = ( ( 0x01 & RecPack[33]) = 0x01 ); */ + /* OutputOn = ( ( 0x02 & RecPack[33]) = 0x02 ); OutputOn */ + /* InputOn = ( ( 0x04 & RecPack[33]) = 0x04 ); InputOn */ + /* ByPassOn = ( ( 0x08 & RecPack[33]) = 0x08 ); BypassOn */ + /* Auto_HAB = ( ( 0x10 & RecPack[33]) = 0x10 ); */ + /* Timer_HAB = ( ( 0x20 & RecPack[33]) = 0x20 ); */ + /* Boost_Ligado = ( ( 0x40 & RecPack[33]) = 0x40 ); */ + /* Bateria_Desc = ( ( 0x80 & RecPack[33]) = 0x80 ); */ - /* Flag1 */ - /* SobreTemp = ( ( 0x01 & RecPack[33]) = 0x01 ); */ - /* OutputOn = ( ( 0x02 & RecPack[33]) = 0x02 ); OutputOn */ - /* InputOn = ( ( 0x04 & RecPack[33]) = 0x04 ); InputOn */ - /* ByPassOn = ( ( 0x08 & RecPack[33]) = 0x08 ); BypassOn */ - /* Auto_HAB = ( ( 0x10 & RecPack[33]) = 0x10 ); */ - /* Timer_HAB = ( ( 0x20 & RecPack[33]) = 0x20 ); */ - /* Boost_Ligado = ( ( 0x40 & RecPack[33]) = 0x40 ); */ - /* Bateria_Desc = ( ( 0x80 & RecPack[33]) = 0x80 ); */ + /* Flag2 */ + /* Quad_Ant_Ent = ( ( 0x01 & RecPack[34]) = 0x01 ); */ + /* Quadratura = ( ( 0x02 & RecPack[34]) = 0x02 ); */ + /* Termino_XMODEM = ( ( 0x04 & RecPack[34]) = 0x04 ); */ + /* Em_Sincronismo = ( ( 0x08 & RecPack[34]) = 0x08 ); */ + /* Out110 = ( ( 0x10 & RecPack[34]) = 0x10 ); Out110 */ + /* Exec_Beep = ( ( 0x20 & RecPack[34]) = 0x20 ); */ + /* LowBatt = ( ( 0x40 & RecPack[34]) = 0x40 ); LowBatt */ + /* Boost_Sobre = ( ( 0x80 & RecPack[34]) = 0x80 ); */ - /* Flag2 */ - /* Quad_Ant_Ent = ( ( 0x01 & RecPack[34]) = 0x01 ); */ - /* Quadratura = ( ( 0x02 & RecPack[34]) = 0x02 ); */ - /* Termino_XMODEM = ( ( 0x04 & RecPack[34]) = 0x04 ); */ - /* Em_Sincronismo = ( ( 0x08 & RecPack[34]) = 0x08 ); */ - /* Out110 = ( ( 0x10 & RecPack[34]) = 0x10 ); Out110 */ - /* Exec_Beep = ( ( 0x20 & RecPack[34]) = 0x20 ); */ - /* LowBatt = ( ( 0x40 & RecPack[34]) = 0x40 ); LowBatt */ - /* Boost_Sobre = ( ( 0x80 & RecPack[34]) = 0x80 ); */ + /* Flag3 */ + /* OverCharge = ( ( 0x01 & RecPack[35]) = 0x01 ); OverCharge */ + /* SourceFail = ( ( 0x02 & RecPack[35]) = 0x02 ); SourceFail */ + /* RedeAnterior = ( ( 0x04 & RecPack[35]) = 0x04 ); */ + /* Cmd_Executado = ( ( 0x08 & RecPack[35]) = 0x08 ); */ + /* Exec_Autoteste = ( ( 0x10 & RecPack[35]) = 0x10 ); */ + /* Quad_Ant_Sai = ( ( 0x20 & RecPack[35]) = 0x20 ); */ + /* ComandoSerial = ( ( 0x40 & RecPack[35]) = 0x40 ); */ + /* SobreTensao = ( ( 0x80 & RecPack[35]) = 0x80 ); */ - /* Flag3 */ - /* OverCharge = ( ( 0x01 & RecPack[35]) = 0x01 ); OverCharge */ - /* SourceFail = ( ( 0x02 & RecPack[35]) = 0x02 ); SourceFail */ - /* RedeAnterior = ( ( 0x04 & RecPack[35]) = 0x04 ); */ - /* Cmd_Executado = ( ( 0x08 & RecPack[35]) = 0x08 ); */ - /* Exec_Autoteste = ( ( 0x10 & RecPack[35]) = 0x10 ); */ - /* Quad_Ant_Sai = ( ( 0x20 & RecPack[35]) = 0x20 ); */ - /* ComandoSerial = ( ( 0x40 & RecPack[35]) = 0x40 ); */ - /* SobreTensao = ( ( 0x80 & RecPack[35]) = 0x80 ); */ + OutputOn = ( ( 0x02 & RecPack[33] ) == 0x02 ); + InputOn = ( ( 0x04 & RecPack[33] ) == 0x04 ); + BypassOn = ( ( 0x08 & RecPack[33] ) == 0x08 ); - OutputOn = ( ( 0x02 & RecPack[33] ) == 0x02 ); - InputOn = ( ( 0x04 & RecPack[33] ) == 0x04 ); - BypassOn = ( ( 0x08 & RecPack[33] ) == 0x08 ); + Out110 = ( ( 0x10 & RecPack[34] ) == 0x10 ); + LowBatt = ( ( 0x40 & RecPack[34] ) == 0x40 ); - Out110 = ( ( 0x10 & RecPack[34] ) == 0x10 ); - LowBatt = ( ( 0x40 & RecPack[34] ) == 0x40 ); + OverCharge = ( ( 0x01 & RecPack[35] ) == 0x01 ); + SourceFail = ( ( 0x02 & RecPack[35] ) == 0x02 ); - OverCharge = ( ( 0x01 & RecPack[35] ) == 0x01 ); - SourceFail = ( ( 0x02 & RecPack[35] ) == 0x02 ); + /* model dependent data read */ - /* model dependent data read */ - - PowerFactor = 800; - - if( RecPack[0] ==0xC2 ) - { - LimInfBattSrc = 174; - LimSupBattSrc = 192;/* 180????? carregador eh 180 (SCOPOS) */ - LimInfBattInv = 174; - LimSupBattInv = 192;/* 170????? (SCOPOS) */ - } - else - { - LimInfBattSrc = 138; - LimSupBattSrc = 162;/* 180????? carregador eh 180 (SCOPOS) */ - LimInfBattInv = 126; - LimSupBattInv = 156;/* 170????? (SCOPOS) */ - } - - BattNonValue = 144; - /* VersaoInterna = "R10" + IntToStr( RecPack[1] ); */ - InVoltage = RecPack[2]; - InCurrent = RecPack[3]; - UtilPowerIn = RecPack[4] + RecPack[5] * 256; - AppPowerIn = RecPack[6] + RecPack[7] * 256; - FatorPotEntrada = RecPack[8]; - InFreq = ( RecPack[9] + RecPack[10] * 256 ) * 1.0 / 10; - OutVoltage = RecPack[11]; - OutCurrent = RecPack[12]; - UtilPowerOut = RecPack[13] + RecPack[14] * 256; - AppPowerOut = RecPack[15] + RecPack[16] * 256; - FatorPotSaida = RecPack[17]; - OutFreq = ( RecPack[18] + RecPack[19] * 256 ) * 1.0 / 10; - BattVoltage = RecPack[20]; - BoostVolt = RecPack[21] + RecPack[22] * 256; - Temperature = ( 0x7F & RecPack[23] ); - Rendimento = RecPack[24]; + PowerFactor = 800; - /* model independent data */ - - if( ( BattVoltage < LimInfBattInv ) ) - CriticBatt = true; - - if( BypassOn ) - OutVoltage = ( InVoltage * 1.0 / 2 ) + 5; - - if( SourceFail && RedeAnterior ) /* falha pela primeira vez */ - OcorrenciaDeFalha = true; - - if( !( SourceFail ) && !( RedeAnterior ) ) /* retorno da rede */ - RetornoDaRede = true; - - if( !( SourceFail ) == RedeAnterior ) - { - RetornoDaRede = false; - OcorrenciaDeFalha = false; - } - - RedeAnterior = !( SourceFail ); - - LimInfSaida = 75; - LimSupSaida = 150; - ValorNominalSaida = 110; - - LimInfEntrada = 190; - LimSupEntrada = 250; - ValorNominalEntrada = 220; - - if( SourceFail ) - { - StatusEntrada = 2; - RecPack[8] = 200; /* ?????????????????????????????????? */ - } - else - { - StatusEntrada = 1; - RecPack[8] = 99; /* ??????????????????????????????????? */ - } - - if( OutputOn ) /* Output Status */ - StatusSaida = 2; - else - StatusSaida = 1; - - if( OverCharge ) - StatusSaida = 3; - - if( CriticBatt ) /* Battery Status */ - StatusBateria = 4; - else - StatusBateria = 1; - - EventosRede = 0; - - if( OcorrenciaDeFalha ) - EventosRede = 1; - - if( RetornoDaRede ) - EventosRede = 2; - - /* verify InversorOn */ - if( Flag_inversor ) - { - oldInversorOn = InputOn; - Flag_inversor = false; - } + if( RecPack[0] == 0xC2 ) + { + LimInfBattSrc = 174; + LimSupBattSrc = 192;/* 180????? carregador eh 180 (SCOPOS) */ + LimInfBattInv = 174; + LimSupBattInv = 192;/* 170????? (SCOPOS) */ + } + else + { + LimInfBattSrc = 138; + LimSupBattSrc = 162;/* 180????? carregador eh 180 (SCOPOS) */ + LimInfBattInv = 126; + LimSupBattInv = 156;/* 170????? (SCOPOS) */ + } - EventosSaida = 0; - if( InputOn && !( oldInversorOn ) ) - EventosSaida = 26; - if( oldInversorOn && !( InputOn ) ) - EventosSaida = 27; - oldInversorOn = InputOn; - if( SuperAquecimento && !( SuperAquecimentoAnterior ) ) - EventosSaida = 12; - if( SuperAquecimentoAnterior && !( SuperAquecimento ) ) - EventosSaida = 13; - SuperAquecimentoAnterior = SuperAquecimento; - EventosBateria = 0; - OldCritBatt = CriticBatt; + BattNonValue = 144; + /* VersaoInterna = "R10" + IntToStr( RecPack[1] ); */ + InVoltage = RecPack[2]; + InCurrent = RecPack[3]; + UtilPowerIn = RecPack[4] + RecPack[5] * 256; + AppPowerIn = RecPack[6] + RecPack[7] * 256; + FatorPotEntrada = RecPack[8]; + InFreq = ( RecPack[9] + RecPack[10] * 256 ) * 1.0 / 10; + OutVoltage = RecPack[11]; + OutCurrent = RecPack[12]; + UtilPowerOut = RecPack[13] + RecPack[14] * 256; + AppPowerOut = RecPack[15] + RecPack[16] * 256; + FatorPotSaida = RecPack[17]; + OutFreq = ( RecPack[18] + RecPack[19] * 256 ) * 1.0 / 10; + BattVoltage = RecPack[20]; + BoostVolt = RecPack[21] + RecPack[22] * 256; + Temperature = ( 0x7F & RecPack[23] ); + Rendimento = RecPack[24]; - if( OverCharge && !( OldOverCharge ) ) - EventosSaida = 10; - if( OldOverCharge && !( OverCharge ) ) - EventosSaida = 11; - OldOverCharge = OverCharge; + /* model independent data */ - /* autonomy calc. */ - if( RecPack[ 0 ] ==0xC2 ) - Autonomy = AutonomyCalc( 1 ); - else - Autonomy = AutonomyCalc( 0 ); + if( ( BattVoltage < LimInfBattInv ) ) + CriticBatt = true; + if( BypassOn ) + OutVoltage = ( InVoltage * 1.0 / 2 ) + 5; + + if( SourceFail && RedeAnterior ) /* falha pela primeira vez */ + OcorrenciaDeFalha = true; + + if( !( SourceFail ) && !( RedeAnterior ) ) /* retorno da rede */ + RetornoDaRede = true; + + if( RedeAnterior == !( SourceFail ) ) + { + RetornoDaRede = false; + OcorrenciaDeFalha = false; + } + + RedeAnterior = !( SourceFail ); + + LimInfSaida = 75; + LimSupSaida = 150; + ValorNominalSaida = 110; + + LimInfEntrada = 190; + LimSupEntrada = 250; + ValorNominalEntrada = 220; + + if( SourceFail ) + { + StatusEntrada = 2; + RecPack[8] = 200; /* ?????????????????????????????????? */ + } + else + { + StatusEntrada = 1; + RecPack[8] = 99; /* ??????????????????????????????????? */ + } + + if( OutputOn ) /* Output Status */ + StatusSaida = 2; + else + StatusSaida = 1; + + if( OverCharge ) + StatusSaida = 3; + + if( CriticBatt ) /* Battery Status */ + StatusBateria = 4; + else + StatusBateria = 1; + + EventosRede = 0; + + if( OcorrenciaDeFalha ) + EventosRede = 1; + + if( RetornoDaRede ) + EventosRede = 2; + + /* verify InversorOn */ + if( Flag_inversor ) + { + oldInversorOn = InputOn; + Flag_inversor = false; + } + + EventosSaida = 0; + if( InputOn && !( oldInversorOn ) ) + EventosSaida = 26; + if( oldInversorOn && !( InputOn ) ) + EventosSaida = 27; + oldInversorOn = InputOn; + if( SuperAquecimento && !( SuperAquecimentoAnterior ) ) + EventosSaida = 12; + if( SuperAquecimentoAnterior && !( SuperAquecimento ) ) + EventosSaida = 13; + SuperAquecimentoAnterior = SuperAquecimento; + EventosBateria = 0; + OldCritBatt = CriticBatt; + + if( OverCharge && !( OldOverCharge ) ) + EventosSaida = 10; + if( OldOverCharge && !( OverCharge ) ) + EventosSaida = 11; + OldOverCharge = OverCharge; + + /* autonomy calc. */ + if( RecPack[ 0 ] == 0xC2 ) + Autonomy = AutonomyCalc( 1 ); + else + Autonomy = AutonomyCalc( 0 ); } static void -CommReceive(const unsigned char *bufptr, int size) +CommReceive(const unsigned char *bufptr, ssize_t size) { + int i, i_end, CheckSum, chk; - int i, i_end, CheckSum, chk; - - if( ( size==37 ) ) - Waiting = 0; - - printf("CommReceive size = %d waiting = %d\n", size, Waiting ); + if( size == 37 ) + Waiting = 0; - switch( Waiting ) - { - /* normal package */ - case 0: - { - if( size == 37 ) - { - i_end = 37; - for( i = 0 ; i < i_end ; ++i ) - { - RecPack[i] = *bufptr; - bufptr++; - } - - /* CheckSum verify */ - CheckSum = 0; - i_end = 36; - for( i = 0 ; i < i_end ; ++i ) - { - chk = RecPack[ i ]; - CheckSum = CheckSum + chk; - } + printf("CommReceive size = %zd waiting = %d\n", size, Waiting ); - CheckSum = CheckSum % 256; + switch( Waiting ) + { + /* normal package */ + case 0: + { + if( size == 37 ) + { + i_end = 37; + for( i = 0 ; i < i_end ; ++i ) + { + RecPack[i] = *bufptr; + bufptr++; + } - ser_flush_in(upsfd,"",0); /* clean port */ + /* CheckSum verify */ + CheckSum = 0; + i_end = 36; + for( i = 0 ; i < i_end ; ++i ) + { + chk = RecPack[ i ]; + CheckSum = CheckSum + chk; + } - /* correct package */ - if( ( RecPack[0] == 0xC0 || RecPack[0] == 0xC1 || RecPack[0] == 0xC2 || RecPack[0] == 0xC3 ) - && ( RecPack[ 36 ] == CheckSum ) ) - { + CheckSum = CheckSum % 256; - if(!(detected)) - { - RhinoModel = RecPack[0]; - detected = true; - } + ser_flush_in(upsfd,"",0); /* clean port */ - switch( RhinoModel ) - { - case 0xC0: - case 0xC1: - case 0xC2: - case 0xC3: - { - ScanReceivePack(); - break; - } - default: - { - printf( M_UNKN ); - break; - } - } - } + /* correct package */ + if( ( RecPack[0] == 0xC0 || RecPack[0] == 0xC1 || RecPack[0] == 0xC2 || RecPack[0] == 0xC3 ) + && ( RecPack[ 36 ] == CheckSum ) ) + { - } - - break; - } - - case 1: - { - /* dumping package, nothing to do yet */ - Waiting = 0; - break; - } + if(!(detected)) + { + RhinoModel = RecPack[0]; + detected = true; + } - } + switch( RhinoModel ) + { + case 0xC0: + case 0xC1: + case 0xC2: + case 0xC3: + { + ScanReceivePack(); + break; + } + default: + { + printf( M_UNKN ); + break; + } + } + } + } - Waiting =0; + break; + } + case 1: + { + /* dumping package, nothing to do yet */ + Waiting = 0; + break; + } + + } + + Waiting = 0; } -static int -send_command( int cmd ) +static ssize_t +send_command( unsigned char cmd ) { + static const size_t sizes = 19, iend = 18; + size_t i, kount; /*, j, uc; */ + unsigned char chk, checksum = 0; + ssize_t ret = -1; + unsigned char ch, *psend = NULL; - int i, chk, checksum = 0, iend = 18, sizes = 19, ret, kount; /*, j, uc; */ - unsigned char ch, psend[sizes]; - - /* mounting buffer to send */ + if ( !(psend = xmalloc(sizeof(char) * sizes)) ) { + upslogx(LOG_ERR, "send_command() failed to allocate buffer"); + return -1; + } + + /* mounting buffer to send */ for(i = 0; i < iend; i++ ) { @@ -488,7 +490,7 @@ send_command( int cmd ) chk = 0x01; else { - if( i == 1) + if( i == 1 ) chk = cmd; else chk = 0x00; /* 0x20; */ @@ -496,12 +498,12 @@ send_command( int cmd ) ch = chk; psend[i] = ch; /* psend[0 - 17] */ - if( i > 0 ) /* psend[0] not computed */ + if( i > 0 ) /* psend[0] not computed */ checksum = checksum + chk; } ch = checksum; - ch = (~( ch) ); /* not ch */ + ch = (~( ch) ); /* not ch */ psend[iend] = ch; /* send five times the command */ @@ -518,99 +520,101 @@ send_command( int cmd ) usleep( UPSDELAY ); /* delay between sent command */ kount++; } + + free (psend); return ret; } static void sendshut( void ) { - int i; for(i=0; i < 30000; i++) - usleep( UPSDELAY ); /* 15 seconds delay */ + usleep( UPSDELAY ); /* 15 seconds delay */ - send_command( CMD_SHUT ); - upslogx(LOG_NOTICE, "Ups shutdown command sent"); + if ( send_command( CMD_SHUT ) < 1 ) + upslogx(LOG_ERR, "Ups shutdown command sending failed"); + else + upslogx(LOG_NOTICE, "Ups shutdown command sent"); printf("Ups shutdown command sent\n"); - } static void getbaseinfo(void) { - unsigned char temp[256]; + unsigned char temp[256]; unsigned char Pacote[37]; - int tam, i, j=0; - time_t *tmt; + ssize_t tam, i, j=0; + time_t tmt; struct tm *now; + struct tm tmbuf; const char *Model; - tmt = ( time_t * ) malloc( sizeof( time_t ) ); - time( tmt ); - now = localtime( tmt ); + time( &tmt ); + now = localtime_r( &tmt, &tmbuf ); dian = now->tm_mday; mesn = now->tm_mon+1; anon = now->tm_year+1900; - weekn = now->tm_wday; + weekn = now->tm_wday; /* trying detect rhino model */ while ( ( !detected ) && ( j < 10 ) ) - { + { - temp[0] = 0; /* flush temp buffer */ - tam = ser_get_buf_len(upsfd, temp, pacsize, 3, 0); - if( tam == 37 ) - { - for( i = 0 ; i < tam ; i++ ) - { - Pacote[i] = temp[i]; - } - } + temp[0] = 0; /* flush temp buffer */ + tam = ser_get_buf_len(upsfd, temp, pacsize, 3, 0); + if( tam == 37 ) + { + for( i = 0 ; i < tam ; i++ ) + { + Pacote[i] = temp[i]; + } + } - j++; - if( tam == 37) - CommReceive(Pacote, tam); - else - CommReceive(temp, tam); - } + j++; + if( tam == 37) + CommReceive(Pacote, tam); + else + CommReceive(temp, tam); + } if( (!detected) ) - { - fatalx(EXIT_FAILURE, NO_RHINO ); - } + { + fatalx(EXIT_FAILURE, NO_RHINO ); + } switch( RhinoModel ) - { - case 0xC0: - { - Model = "Rhino 20.0 kVA"; - PotenciaNominal = 20000; - break; - } - case 0xC1: - { - Model = "Rhino 10.0 kVA"; - PotenciaNominal = 10000; - break; - } - case 0xC2: - { - Model = "Rhino 6.0 kVA"; - PotenciaNominal = 6000; - break; - } - case 0xC3: - { - Model = "Rhino 7.5 kVA"; - PotenciaNominal = 7500; - break; - } - default: - { - Model = "Rhino unknown model"; - PotenciaNominal = 0; - break; - } - } + { + case 0xC0: + { + Model = "Rhino 20.0 kVA"; + PotenciaNominal = 20000; + break; + } + case 0xC1: + { + Model = "Rhino 10.0 kVA"; + PotenciaNominal = 10000; + break; + } + case 0xC2: + { + Model = "Rhino 6.0 kVA"; + PotenciaNominal = 6000; + break; + } + case 0xC3: + { + Model = "Rhino 7.5 kVA"; + PotenciaNominal = 7500; + break; + } + default: + { + Model = "Rhino unknown model"; + PotenciaNominal = 0; + break; + } + } /* manufacturer and model */ dstate_setinfo("ups.mfr", "%s", "Microsol"); @@ -630,13 +634,12 @@ static void getbaseinfo(void) dstate_addcmd("bypass.stop"); /* CMD_PASSOFF = 6 */ printf("Detected %s on %s\n", dstate_getinfo("ups.model"), device_path); - } static void getupdateinfo(void) { - unsigned char temp[256]; - int tam; + unsigned char temp[256]; + ssize_t tam; temp[0] = 0; /* flush temp buffer */ tam = ser_get_buf_len(upsfd, temp, pacsize, 3, 0); @@ -647,57 +650,54 @@ static void getupdateinfo(void) static int instcmd(const char *cmdname, const char *extra) { + ssize_t ret = 0; - int ret = 0; - if (!strcasecmp(cmdname, "shutdown.stayoff")) - { - /* shutdown now (one way) */ - /* send_command( CMD_SHUT ); */ - sendshut(); - return STAT_INSTCMD_HANDLED; - } + { + /* shutdown now (one way) */ + /* send_command( CMD_SHUT ); */ + sendshut(); + return STAT_INSTCMD_HANDLED; + } if (!strcasecmp(cmdname, "load.on")) - { - /* liga Saida */ - ret = send_command( 3 ); - if ( ret < 1 ) - upslogx(LOG_ERR, "send_command 3 failed"); - return STAT_INSTCMD_HANDLED; - } + { + /* liga Saida */ + ret = send_command( 3 ); + if ( ret < 1 ) + upslogx(LOG_ERR, "send_command 3 failed"); + return STAT_INSTCMD_HANDLED; + } if (!strcasecmp(cmdname, "load.off")) - { - /* desliga Saida */ - ret = send_command( 4 ); - if ( ret < 1 ) - upslogx(LOG_ERR, "send_command 4 failed"); - return STAT_INSTCMD_HANDLED; - } + { + /* desliga Saida */ + ret = send_command( 4 ); + if ( ret < 1 ) + upslogx(LOG_ERR, "send_command 4 failed"); + return STAT_INSTCMD_HANDLED; + } if (!strcasecmp(cmdname, "bypass.start")) - { - /* liga Bypass */ - ret = send_command( 5 ); - if ( ret < 1 ) - upslogx(LOG_ERR, "send_command 5 failed"); - return STAT_INSTCMD_HANDLED; - } + { + /* liga Bypass */ + ret = send_command( 5 ); + if ( ret < 1 ) + upslogx(LOG_ERR, "send_command 5 failed"); + return STAT_INSTCMD_HANDLED; + } if (!strcasecmp(cmdname, "bypass.stop")) - { - /* desliga Bypass */ - ret = send_command( 6 ); - if ( ret < 1 ) - upslogx(LOG_ERR, "send_command 6 failed"); - return STAT_INSTCMD_HANDLED; - } + { + /* desliga Bypass */ + ret = send_command( 6 ); + if ( ret < 1 ) + upslogx(LOG_ERR, "send_command 6 failed"); + return STAT_INSTCMD_HANDLED; + } - - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; - } void upsdrv_initinfo(void) @@ -709,8 +709,7 @@ void upsdrv_initinfo(void) void upsdrv_updateinfo(void) { - - getupdateinfo(); /* new package for updates */ + getupdateinfo(); /* new package for updates */ dstate_setinfo("output.voltage", "%03.1f", OutVoltage); dstate_setinfo("input.voltage", "%03.1f", InVoltage); @@ -718,51 +717,51 @@ void upsdrv_updateinfo(void) /* output and bypass tests */ if( OutputOn ) - dstate_setinfo("outlet.switchable", "%s", "yes"); + dstate_setinfo("outlet.switchable", "%s", "yes"); else - dstate_setinfo("outlet.switchable", "%s", "no"); + dstate_setinfo("outlet.switchable", "%s", "no"); if( BypassOn ) - dstate_setinfo("outlet.1.switchable", "%s", "yes"); + dstate_setinfo("outlet.1.switchable", "%s", "yes"); else - dstate_setinfo("outlet.1.switchable", "%s", "no"); + dstate_setinfo("outlet.1.switchable", "%s", "no"); status_init(); if (!SourceFail ) - status_set("OL"); /* on line */ + status_set("OL"); /* on line */ else - status_set("OB"); /* on battery */ - + status_set("OB"); /* on battery */ + if (Autonomy < 5 ) - status_set("LB"); /* low battery */ - + status_set("LB"); /* low battery */ + status_commit(); dstate_setinfo("ups.temperature", "%2.2f", Temperature); dstate_setinfo("input.frequency", "%2.1f", InFreq); dstate_dataok(); - } /* power down the attached load immediately */ void upsdrv_shutdown(void) { - /* basic idea: find out line status and send appropriate command */ /* on line: send normal shutdown, ups will return by itself on utility */ /* on battery: send shutdown+return, ups will cycle and return soon */ if (!SourceFail) /* on line */ - { - printf("On line, forcing shutdown command...\n"); - send_command( CMD_SHUT ); - } + { + /* FIXME: Both legs of the if-clause send CMD_SHUT, where is the "forcing"? */ + printf("On line, forcing shutdown command...\n"); + /* send_command( CMD_SHUT ); */ + sendshut(); + } else - { - printf("On battery, sending normal shutdown command...\n"); - send_command( CMD_SHUT ); - } - + { + printf("On battery, sending normal shutdown command...\n"); + /* send_command( CMD_SHUT ); */ + sendshut(); + } } void upsdrv_help(void) @@ -771,9 +770,7 @@ void upsdrv_help(void) void upsdrv_makevartable(void) { - addvar(VAR_VALUE, "battext", "Battery Extension (0-80)min"); - } void upsdrv_initups(void) @@ -784,7 +781,6 @@ void upsdrv_initups(void) /* dtr and rts setting */ ser_set_dtr(upsfd, 1); ser_set_rts(upsfd, 0); - } void upsdrv_cleanup(void) diff --git a/drivers/richcomm_usb.c b/drivers/richcomm_usb.c index 489650a..f8da32e 100644 --- a/drivers/richcomm_usb.c +++ b/drivers/richcomm_usb.c @@ -6,6 +6,7 @@ * * Copyright (C) 2007 Peter van Valderen * Dirk Teurlings + * Copyright (C) 2016 Eaton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,10 +25,11 @@ #include "main.h" #include "usb-common.h" +#include "nut_stdint.h" /* driver version */ #define DRIVER_NAME "Richcomm dry-contact to USB driver" -#define DRIVER_VERSION "0.04" +#define DRIVER_VERSION "0.10" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -55,8 +57,8 @@ static usb_device_id_t richcomm_usb_id[] = { /* Sweex 1000VA */ { USB_DEVICE(0x0925, 0x1234), NULL }, - /* end of list */ - {-1, -1, NULL} + /* Terminating entry */ + { 0, 0, NULL } }; static usb_dev_handle *udev = NULL; @@ -65,7 +67,9 @@ static unsigned int comm_failures = 0; static int device_match_func(USBDevice_t *device, void *privdata) { - switch (is_usb_device_supported(richcomm_usb_id, device->VendorID, device->ProductID)) + NUT_UNUSED_VARIABLE(privdata); + + switch (is_usb_device_supported(richcomm_usb_id, device)) { case SUPPORTED: return 1; @@ -83,30 +87,79 @@ static USBDeviceMatcher_t device_matcher = { NULL }; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif static int execute_and_retrieve_query(char *query, char *reply) { int ret; ret = usb_control_msg(udev, STATUS_REQUESTTYPE, REQUEST_VALUE, - MESSAGE_VALUE, INDEX_VALUE, query, QUERY_PACKETSIZE, 1000); + MESSAGE_VALUE, INDEX_VALUE, + (usb_ctrl_charbuf)query, QUERY_PACKETSIZE, 1000); if (ret <= 0) { - upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout"); + upsdebugx(3, "send: %s", + ret ? nut_usb_strerror(ret) : "timeout"); return ret; } - upsdebug_hex(3, "send", query, ret); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + /* Cast up within the signed/unsigned same type */ + if ((unsigned int)ret >= SIZE_MAX) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + upsdebugx(3, "send: ret=%d exceeds SIZE_MAX", ret); + } + upsdebug_hex(3, "send", query, (size_t)ret); - ret = usb_interrupt_read(udev, REPLY_REQUESTTYPE, reply, REPLY_PACKETSIZE, 1000); + ret = usb_interrupt_read(udev, + REPLY_REQUESTTYPE, + (usb_ctrl_charbuf)reply, REPLY_PACKETSIZE, 1000); if (ret <= 0) { - upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout"); + upsdebugx(3, "read: %s", + ret ? nut_usb_strerror(ret) : "timeout"); return ret; } - upsdebug_hex(3, "read", reply, ret); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + /* Cast up within the signed/unsigned same type */ + if ((unsigned int)ret >= SIZE_MAX) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + upsdebugx(3, "read: ret=%d exceeds SIZE_MAX", ret); + } + upsdebug_hex(3, "read", reply, (size_t)ret); return ret; } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) ) +# pragma GCC diagnostic pop +#endif static int query_ups(char *reply) { @@ -147,7 +200,19 @@ static void usb_comm_fail(const char *fmt, ...) } va_start(ap, fmt); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif ret = vsnprintf(why, sizeof(why), fmt, ap); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif va_end(ap); if ((ret < 1) || (ret >= (int) sizeof(why))) { @@ -163,7 +228,7 @@ static void usb_comm_good(void) return; } - upslogx(LOG_NOTICE, "Communications with UPS re-established"); + upslogx(LOG_NOTICE, "Communications with UPS re-established"); comm_failures = 0; } @@ -176,6 +241,8 @@ static void usb_comm_good(void) */ static int driver_callback(usb_dev_handle *handle, USBDevice_t *device) { + NUT_UNUSED_VARIABLE(device); + if (usb_set_configuration(handle, 1) < 0) { upsdebugx(5, "Can't set USB configuration"); return -1; @@ -186,10 +253,19 @@ static int driver_callback(usb_dev_handle *handle, USBDevice_t *device) return -1; } +#if WITH_LIBUSB_0_1 if (usb_set_altinterface(handle, 0) < 0) { upsdebugx(5, "Can't set USB alternate interface"); return -1; } +#elif WITH_LIBUSB_1_0 + int ret = 0; + + if ((ret = libusb_set_interface_alt_setting(handle, 0, 0)) < 0) { + upsdebugx(5, "Can't set USB alternate interface: %s", nut_usb_strerror(ret)); + return -1; + } +#endif /* WITH_LIBUSB_1_0 */ if (usb_clear_halt(handle, 0x81) < 0) { upsdebugx(5, "Can't reset USB endpoint"); @@ -201,31 +277,73 @@ static int driver_callback(usb_dev_handle *handle, USBDevice_t *device) static int usb_device_close(usb_dev_handle *handle) { + int ret = 0; + if (!handle) { return 0; } /* usb_release_interface() sometimes blocks and goes - into uninterruptible sleep. So don't do it. */ + * into uninterruptible sleep. So don't do it. + */ /* usb_release_interface(handle, 0); */ - return usb_close(handle); + +#if WITH_LIBUSB_1_0 + libusb_close(handle); + libusb_exit(NULL); +#else + ret = usb_close(handle); +#endif + + return ret; } static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDeviceMatcher_t *matcher, int (*callback)(usb_dev_handle *handle, USBDevice_t *device)) { - struct usb_bus *bus; + int ret = 0; + uint8_t iManufacturer = 0, iProduct = 0, iSerialNumber = 0; /* libusb base init */ +#if WITH_LIBUSB_1_0 + if (libusb_init(NULL) < 0) { + libusb_exit(NULL); + fatal_with_errno(EXIT_FAILURE, "Failed to init libusb 1.0"); + } +#else /* => WITH_LIBUSB_0_1 */ usb_init(); usb_find_busses(); usb_find_devices(); +#endif /* WITH_LIBUSB_1_0 */ #ifndef __linux__ /* SUN_LIBUSB (confirmed to work on Solaris and FreeBSD) */ /* Causes a double free corruption in linux if device is detached! */ - usb_device_close(*handlep); + /* usb_device_close(*handlep); */ + if (*handlep) + usb_close(*handlep); #endif +#if WITH_LIBUSB_1_0 + libusb_device **devlist; + ssize_t devcount = 0; + libusb_device_handle *handle; + struct libusb_device_descriptor dev_desc; + uint8_t bus; + int i; + + devcount = libusb_get_device_list(NULL, &devlist); + if (devcount <= 0) + fatal_with_errno(EXIT_FAILURE, "No USB device found"); + + for (i = 0; i < devcount; i++) { + + USBDeviceMatcher_t *m; + libusb_device *dev = devlist[i]; + libusb_get_device_descriptor(dev, &dev_desc); + ret = libusb_open(dev, &handle); + *handlep = handle; +#else /* => WITH_LIBUSB_0_1 */ + struct usb_bus *bus; for (bus = usb_busses; bus; bus = bus->next) { struct usb_device *dev; @@ -233,18 +351,22 @@ static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDev for (dev = bus->devices; dev; dev = dev->next) { - int i, ret; + int i; USBDeviceMatcher_t *m; - upsdebugx(4, "Checking USB device [%04x:%04x] (%s/%s)", dev->descriptor.idVendor, - dev->descriptor.idProduct, bus->dirname, dev->filename); - + upsdebugx(3, "Checking USB device [%04x:%04x] (%s/%s)", + dev->descriptor.idVendor, + dev->descriptor.idProduct, + bus->dirname, dev->filename); + /* supported vendors are now checked by the supplied matcher */ /* open the device */ *handlep = handle = usb_open(dev); +#endif /* WITH_LIBUSB_1_0 */ + if (!handle) { - upsdebugx(4, "Failed to open USB device, skipping: %s", usb_strerror()); + upsdebugx(4, "Failed to open USB device, skipping: %s", nut_usb_strerror(ret)); continue; } @@ -261,34 +383,70 @@ static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDev memset(device, 0, sizeof(*device)); +#if WITH_LIBUSB_1_0 + device->VendorID = dev_desc.idVendor; + device->ProductID = dev_desc.idProduct; + bus = libusb_get_bus_number(dev); + device->Bus = (char *)malloc(4); + if (device->Bus == NULL) { + libusb_free_device_list(devlist, 1); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + sprintf(device->Bus, "%03d", bus); + iManufacturer = dev_desc.iManufacturer; + iProduct = dev_desc.iProduct; + iSerialNumber = dev_desc.iSerialNumber; +#else /* => WITH_LIBUSB_0_1 */ device->VendorID = dev->descriptor.idVendor; device->ProductID = dev->descriptor.idProduct; - device->Bus = strdup(bus->dirname); - - if (dev->descriptor.iManufacturer) { + device->Bus = xstrdup(bus->dirname); + iManufacturer = dev->descriptor.iManufacturer; + iProduct = dev->descriptor.iProduct; + iSerialNumber = dev->descriptor.iSerialNumber; +#endif /* WITH_LIBUSB_1_0 */ + + if (iManufacturer) { char buf[SMALLBUF]; - ret = usb_get_string_simple(handle, dev->descriptor.iManufacturer, - buf, sizeof(buf)); + ret = usb_get_string_simple(handle, iManufacturer, + (usb_ctrl_charbuf)buf, sizeof(buf)); if (ret > 0) { device->Vendor = strdup(buf); + if (device->Vendor == NULL) { +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } } } - if (dev->descriptor.iProduct) { + if (iProduct) { char buf[SMALLBUF]; - ret = usb_get_string_simple(handle, dev->descriptor.iProduct, - buf, sizeof(buf)); + ret = usb_get_string_simple(handle, iProduct, + (usb_ctrl_charbuf)buf, sizeof(buf)); if (ret > 0) { device->Product = strdup(buf); + if (device->Product == NULL) { +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } } } - if (dev->descriptor.iSerialNumber) { + if (iSerialNumber) { char buf[SMALLBUF]; - ret = usb_get_string_simple(handle, dev->descriptor.iSerialNumber, - buf, sizeof(buf)); + ret = usb_get_string_simple(handle, iSerialNumber, + (usb_ctrl_charbuf)buf, sizeof(buf)); if (ret > 0) { device->Serial = strdup(buf); + if (device->Serial == NULL) { +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } } } @@ -300,49 +458,106 @@ static int usb_device_open(usb_dev_handle **handlep, USBDevice_t *device, USBDev upsdebugx(4, "- Bus : %s", device->Bus ? device->Bus : "unknown"); for (m = matcher; m; m = m->next) { - + switch (m->match_function(device, m->privdata)) { case 0: upsdebugx(4, "Device does not match - skipping"); goto next_device; case -1: +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ fatal_with_errno(EXIT_FAILURE, "matcher"); +#ifndef HAVE___ATTRIBUTE__NORETURN +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunreachable-code" +# endif goto next_device; +# if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) +# pragma GCC diagnostic pop +# endif +#endif case -2: upsdebugx(4, "matcher: unspecified error"); goto next_device; } } +#ifdef HAVE_LIBUSB_SET_AUTO_DETACH_KERNEL_DRIVER + /* First, try the auto-detach kernel driver method + * This function is not available on FreeBSD 10.1-10.3 */ + if ((ret = libusb_set_auto_detach_kernel_driver(handle, 1)) < 0) + upsdebugx(2, + "failed to auto detach kernel driver from USB device: %s", + nut_usb_strerror((enum libusb_error)ret)); + else + upsdebugx(2, "auto detached kernel driver from USB device"); +#endif /* HAVE_LIBUSB_SET_AUTO_DETACH_KERNEL_DRIVER */ for (i = 0; i < 3; i++) { ret = callback(handle, device); if (ret >= 0) { - upsdebugx(4, "USB device [%04x:%04x] opened", device->VendorID, device->ProductID); + upsdebugx(4, "USB device [%04x:%04x] opened", + device->VendorID, device->ProductID); +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ return ret; } -#ifdef HAVE_USB_DETACH_KERNEL_DRIVER_NP +#if WITH_LIBUSB_0_1 && (defined HAVE_USB_DETACH_KERNEL_DRIVER_NP) /* this method requires at least libusb 0.1.8: * it force device claiming by unbinding * attached driver... From libhid */ - if (usb_detach_kernel_driver_np(handle, 0) < 0) { - upsdebugx(4, "failed to detach kernel driver from USB device: %s", usb_strerror()); + if ((ret = usb_detach_kernel_driver_np(handle, 0)) < 0) { + upsdebugx(4, + "failed to detach kernel driver from USB device: %s", + nut_usb_strerror(ret)); } else { upsdebugx(4, "detached kernel driver from USB device..."); } -#endif +#else +# ifdef HAVE_LIBUSB_DETACH_KERNEL_DRIVER + if ((ret = libusb_detach_kernel_driver(handle, 0)) < 0) { + upsdebugx(4, + "failed to detach kernel driver from USB device: %s", + nut_usb_strerror(ret)); + } else { + upsdebugx(4, "detached kernel driver from USB device..."); + } +# else +# ifdef HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP + if ((ret = libusb_detach_kernel_driver_np(udev, 0)) < 0) { + upsdebugx(4, + "failed to detach kernel driver from USB device: %s", + nut_usb_strerror(ret)); + } else { + upsdebugx(4, "detached kernel driver from USB device..."); + } +# endif /* HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP */ +# endif /* HAVE_LIBUSB_DETACH_KERNEL_DRIVER */ +#endif /* HAVE_USB_DETACH_KERNEL_DRIVER_NP or HAVE_LIBUSB_DETACH_KERNEL_DRIVER or HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP */ } - fatalx(EXIT_FAILURE, "USB device [%04x:%04x] matches, but driver callback failed: %s", - device->VendorID, device->ProductID, usb_strerror()); +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ + fatalx(EXIT_FAILURE, + "USB device [%04x:%04x] matches, but driver callback failed: %s", + device->VendorID, device->ProductID, nut_usb_strerror(ret)); next_device: usb_close(handle); +#if (!WITH_LIBUSB_1_0) /* => WITH_LIBUSB_0_1 */ } +#endif /* WITH_LIBUSB_1_0 */ } *handlep = NULL; +#if WITH_LIBUSB_1_0 + libusb_free_device_list(devlist, 1); +#endif /* WITH_LIBUSB_1_0 */ upsdebugx(4, "No matching USB device found"); return -1; @@ -356,6 +571,8 @@ void upsdrv_initups(void) char reply[REPLY_PACKETSIZE]; int i; + warn_if_bad_usb_port_filename(device_path); + for (i = 0; usb_device_open(&udev, &usbdevice, &device_matcher, &driver_callback) < 0; i++) { if ((i < 32) && (sleep(5) == 0)) { diff --git a/drivers/riello.c b/drivers/riello.c new file mode 100644 index 0000000..8671225 --- /dev/null +++ b/drivers/riello.c @@ -0,0 +1,1026 @@ +/* + * riello.c: driver core for Riello protocol based UPSes + * + * Documents describing the protocol implemented by this driver can be + * found online at: + * + * http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf + * http://www.networkupstools.org/ups-protocols/riello/PSSENTR-0100.pdf + * + * Copyright (C) 2012 - Elio Parisi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Reference of the derivative work: blazer driver + */ + +#include +#include + +#include "main.h" +#include "riello.h" + +static uint8_t foundheader = 0; +uint16_t buf_ptr_length; + +uint8_t wait_packet = 0; +uint8_t foundnak = 0; +uint8_t foundbadcrc = 0; + +uint8_t commbyte; +uint8_t requestSENTR; + +static unsigned char LAST_DATA[6]; + +uint16_t riello_calc_CRC(uint8_t type, uint8_t *buff, uint16_t size, uint8_t checksum) +{ + uint16_t i; + uint16_t pom, CRC_Word; + + CRC_Word = 0; + switch (type) { + case DEV_RIELLOSENTRY: + CRC_Word = 0; + if (size == 101) { + for (i=0; i<100; i++) + CRC_Word += buff[i]; + } + else { + for (i=0; i> 4); + CRC_Word = (CRC_Word >> 8) ^ pom; + buff++; + } + } + else { + CRC_Word = 0; + for (i=1; iIdentification, &buffer[7], 16); + data->Identification[16] = 0; + memcpy(data->ModelStr, &buffer[23], 16); + data->ModelStr[16] = 0; + data->ModelStr[15] = 0; + memcpy(data->Version, &buffer[39], 12); + data->Version[12] = 0; + memcpy(data->Identif_bytes, &buffer[51], 12); + data->Identif_bytes[11] = 0; + data->NumBat = data->Identif_bytes[7] - 0x30; +} + +void riello_parse_gn(uint8_t* buffer, TRielloData* data) +{ + uint16_t pom_word; + uint32_t pom_long; + uint8_t j; + + j = 7; + pom_long = (buffer[j++]-0x30)*65536; + pom_long += (buffer[j++]-0x30)*4096; + pom_long += (buffer[j++]-0x30)*256; + pom_long += (buffer[j++]-0x30)*16; + pom_long += (buffer[j++]-0x30); + + if (data->Identif_bytes[0] != '1') + pom_long/=100; + assert (pom_long < UINT16_MAX); + data->NomPowerKVA = (uint16_t)pom_long; + + pom_long = (buffer[j++]-0x30)*65536; + pom_long += (buffer[j++]-0x30)*4096; + pom_long += (buffer[j++]-0x30)*256; + pom_long += (buffer[j++]-0x30)*16; + pom_long += (buffer[j++]-0x30); + + if (data->Identif_bytes[0] != '1') + pom_long/=100; + assert (pom_long < UINT16_MAX); + data->NomPowerKW = (uint16_t)pom_long; + + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->NomUbat = pom_word; + + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->NomBatCap = pom_word; + + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->NominalUout = pom_word; + + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->NomFout = pom_word; +} + +void riello_parse_rs(uint8_t* buffer, TRielloData* data, uint8_t numread) +{ + uint16_t pom_word; + uint8_t j; + + j = 7; + memcpy(data->StatusCode, &buffer[j], 5); + data->StatusCode[5] = 0; + data->Boost = riello_test_bit(&buffer[j], 9); + data->Buck = riello_test_bit(&buffer[j], 8); + data->LockUPS = riello_test_bit(&buffer[j], 2); + j+=5; + + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Finp = pom_word; + + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Uinp1 = pom_word; + + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + if (!riello_test_bit(&data->StatusCode[0], 3)) + pom_word = 0; + data->Fout = pom_word; + + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Uout1 = pom_word; + + pom_word = (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Pout1 = pom_word; + + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Fbypass = pom_word; + + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Ubypass1 = pom_word; + + pom_word = (buffer[j++]-0x30)*4096; + pom_word += (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Ubat = pom_word; + + pom_word = (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->BatCap = pom_word; + + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->BatTime = pom_word; + if (data->BatTime == 0xfff) + data->BatTime = 0xffff; + + pom_word = (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Tsystem = pom_word; + + if (numread > 42) { + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Uinp2 = pom_word; + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Uinp3 = pom_word; + } + else { + data->Uinp2 = 0; + data->Uinp3 = 0; + } + + if (numread > 48) { + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Uout2 = pom_word; + pom_word = (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Pout2 = pom_word; + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Ubypass2 = pom_word; + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Uout3 = pom_word; + pom_word = (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Pout3 = pom_word; + pom_word = (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Ubypass3 = pom_word; + } + else { + data->Uout2 = 0; + data->Pout2 = 0; + data->Ubypass2 = 0; + data->Uout3 = 0; + data->Pout3 = 0; + data->Ubypass3 = 0; + } +} + +void riello_parse_re(uint8_t* buffer, TRielloData* data) +{ + uint16_t pom_word; + uint32_t pom_long; + uint8_t j; + + j = 23; + data->Iinp1 = 0xFFFF; + data->Iinp2 = 0xFFFF; + data->Iinp3 = 0xFFFF; + + pom_word = (buffer[j++]-0x30)*4096; + pom_word += (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Iout1 = pom_word; + + pom_word = (buffer[j++]-0x30)*4096; + pom_word += (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Iout2 = pom_word; + + pom_word = (buffer[j++]-0x30)*4096; + pom_word += (buffer[j++]-0x30)*256; + pom_word += (buffer[j++]-0x30)*16; + pom_word += (buffer[j++]-0x30); + data->Iout3 = pom_word; + + pom_long = (buffer[j++]-0x30)*65536; + pom_long += (buffer[j++]-0x30)*4096; + pom_long += (buffer[j++]-0x30)*256; + pom_long += (buffer[j++]-0x30)*16; + pom_long += (buffer[j++]-0x30); + data->Pout1W = pom_long; + + pom_long = (buffer[j++]-0x30)*65536; + pom_long += (buffer[j++]-0x30)*4096; + pom_long += (buffer[j++]-0x30)*256; + pom_long += (buffer[j++]-0x30)*16; + pom_long += (buffer[j++]-0x30); + data->Pout2W = pom_long; + + pom_long = (buffer[j++]-0x30)*65536; + pom_long += (buffer[j++]-0x30)*4096; + pom_long += (buffer[j++]-0x30)*256; + pom_long += (buffer[j++]-0x30)*16; + pom_long += (buffer[j++]-0x30); + data->Pout3W = pom_long; + + pom_long = (buffer[j++]-0x30)*65536; + pom_long += (buffer[j++]-0x30)*4096; + pom_long += (buffer[j++]-0x30)*256; + pom_long += (buffer[j++]-0x30)*16; + pom_long += (buffer[j++]-0x30); + data->Pout1VA = pom_long; + + pom_long = (buffer[j++]-0x30)*65536; + pom_long += (buffer[j++]-0x30)*4096; + pom_long += (buffer[j++]-0x30)*256; + pom_long += (buffer[j++]-0x30)*16; + pom_long += (buffer[j++]-0x30); + data->Pout2VA = pom_long; + + pom_long = (buffer[j++]-0x30)*65536; + pom_long += (buffer[j++]-0x30)*4096; + pom_long += (buffer[j++]-0x30)*256; + pom_long += (buffer[j++]-0x30)*16; + pom_long += (buffer[j++]-0x30); + data->Pout3VA = pom_long; +} + +void riello_parse_rc(uint8_t* buffer, TRielloData* data) +{ + uint8_t j, i; + + j = 7; + for (i = 0; i < 22; i++, j+=2) { + data->StatusCodeT[i] = (char)(buffer[j+1]-0x30); + data->StatusCodeT[i] |= ((buffer[j]-0x30) << 4); + } + data->StatusCodeT[23] = 0; + data->StatusCodeT[24] = 0; +} + +void riello_parse_sentr(uint8_t* buffer, TRielloData* data) +{ + uint32_t pom; + + data->Model = buffer[2]+256*buffer[3]; + if (data->Model < 3000) { + if ((data->Model % 10) >= 4) { + if (buffer[100] & 0x01) + requestSENTR = SENTR_EXT176; + else + requestSENTR = SENTR_ALSO240; + } + else + requestSENTR = SENTR_ONLY192; + data->NomPowerKVA = data->Model/10; + } + else { + if (((data->Model-3000) % 10) >= 4) { + if (buffer[100] & 0x01) + requestSENTR = SENTR_EXT176; + else + requestSENTR = SENTR_ALSO240; + } + else + requestSENTR = SENTR_ONLY192; + data->NomPowerKVA = (data->Model-3000)/10; + } + + if (buffer[76] & 0x08) + data->NomPowerKW = ((data->NomPowerKVA * 1000) * 9 / 10) / 1000; + else + data->NomPowerKW = ((data->NomPowerKVA * 1000) * 8 / 10) / 1000; + + data->NomPowerKVA *= 1000; + data->NomPowerKW *= 1000; + if (data->Model < 3000) + data->Identif_bytes[0] = '3'; + else + data->Identif_bytes[0] = '4'; + data->Identif_bytes[1] = '3'; + data->Identif_bytes[6] = '2'; + + data->SWversion = buffer[4]+256*buffer[5]; + + data->Version[0] = (char)(uint8_t)(48 + ((data->SWversion / 1000) % 10)); + data->Version[1] = (char)(uint8_t)(48 + ((data->SWversion / 100) % 10)); + data->Version[2] = '.'; + data->Version[3] = (char)(uint8_t)(48 + ((data->SWversion / 10) % 10)); + data->Version[4] = (char)(uint8_t)(48 + (data->SWversion % 10)); + + if (data->Model < 3000) + pom = data->Model*100; + else + pom = (data->Model-3000)*100; + + if (buffer[0] == SENTR_EXT176) { + data->Uinp1 = (buffer[117]+buffer[118]*256)/10; + data->Uinp2 = (buffer[119]+buffer[120]*256)/10; + data->Uinp3 = (buffer[121]+buffer[122]*256)/10; + data->Iinp1 = (buffer[123]+buffer[124]*256)/10; + data->Iinp2 = (buffer[125]+buffer[126]*256)/10; + data->Iinp3 = (buffer[127]+buffer[128]*256)/10; + data->Finp = buffer[41]+256*buffer[42]; + + data->Uout1 = (buffer[135]+buffer[136]*256)/10; + data->Uout2 = (buffer[137]+buffer[138]*256)/10; + data->Uout3 = (buffer[139]+buffer[140]*256)/10; + data->Iout1 = (buffer[141]+buffer[142]*256)/10; + data->Iout2 = (buffer[143]+buffer[144]*256)/10; + data->Iout3 = (buffer[145]+buffer[146]*256)/10; + + data->Pout1 = buffer[62]; + data->Pout2 = buffer[63]; + data->Pout3 = buffer[64]; + data->Ipout1 = buffer[65]*3; + } + else { + data->Uinp1 = buffer[35]*230/100; + data->Uinp2 = buffer[36]*230/100; + data->Uinp3 = buffer[37]*230/100; + /* TODO: Range-check the casts to uint16_t? */ + data->Iinp1 = (uint16_t)(((pom/690)*buffer[38])/100); + data->Iinp2 = (uint16_t)(((pom/690)*buffer[39])/100); + data->Iinp3 = (uint16_t)(((pom/690)*buffer[40])/100); + data->Finp = buffer[41]+256*buffer[42]; + + if (buffer[79] & 0x80) { + data->Uout1 = buffer[59]*2; + data->Uout2 = buffer[60]*2; + data->Uout3 = buffer[61]*2; + } + else { + data->Uout1 = buffer[59]; + data->Uout2 = buffer[60]; + data->Uout3 = buffer[61]; + } + + if (buffer[73]) { + /* FIXME: Wondering how the addition below works for uint8_t[] buffer... */ + /* TODO: Range-check the casts to uint16_t? */ + if (buffer[73] < 100) + buffer[73]+=256; + if (data->Model < 3000) /* singlephase */ + data->Iout1 = (uint16_t)(((pom/buffer[73])*buffer[62])/100); + else + data->Iout1 = (uint16_t)(((pom/buffer[73])*buffer[62])/100/3); + data->Iout2 = (uint16_t)(((pom/buffer[73])*buffer[63])/100/3); + data->Iout3 = (uint16_t)(((pom/buffer[73])*buffer[64])/100/3); + } + else { + data->Iout1 = 0; + data->Iout2 = 0; + data->Iout3 = 0; + } + + if ((data->Model & 0x0007) < 4) { + data->Iout1 *= 0.9; + data->Iout2 *= 0.9; + data->Iout3 *= 0.9; + } + + data->Pout1 = buffer[62]; + data->Pout2 = buffer[63]; + data->Pout3 = buffer[64]; + data->Ipout1 = buffer[65]*3; + } + + if (data->Model < 3000) { + data->Ipout2 = 0; + data->Ipout3 = 0; + } + else { + data->Ipout2 = buffer[66]*3; + data->Ipout3 = buffer[67]*3; + } + + data->Fout = buffer[68]+256*buffer[69]; + + data->BatTime = buffer[6]+256*buffer[7]; + data->BatCap = buffer[8]; + + if ((buffer[0] == SENTR_ALSO240) || (buffer[0] == SENTR_EXT176)) { + if (buffer[100] & 0x80) + data->Ubat = buffer[43]+256*buffer[44]; + else { + if (buffer[44] < buffer[43]) + data->Ubat = buffer[44]*2; + else + data->Ubat = buffer[43]*2; + } + } + else + data->Ubat = buffer[43]+256*buffer[44]; + + data->Ubat *= 10; + data->Ibat = buffer[45]+256*buffer[46]; + if (!buffer[47]) + data->Ibat = data->Ibat*10; + + data->Tsystem = buffer[48]; + data->NomBatCap = buffer[74]+256*buffer[75]; + + switch (buffer[0]) { + case SENTR_EXT176: + data->Ubypass1 = (buffer[129]+buffer[130]*256)/10; + data->Ubypass2 = (buffer[131]+buffer[132]*256)/10; + data->Ubypass3 = (buffer[133]+buffer[134]*256)/10; + data->Fbypass = buffer[57]+256*buffer[58]; + break; + case SENTR_ALSO240: + data->Ubypass1 = buffer[51]*2; + data->Ubypass2 = buffer[53]*2; + data->Ubypass3 = buffer[55]*2; + data->Fbypass = buffer[57]+256*buffer[58]; + break; + default: + data->Ubypass1 = buffer[51]+256*buffer[52]; + data->Ubypass2 = buffer[53]+256*buffer[54]; + data->Ubypass3 = buffer[55]+256*buffer[56]; + data->Fbypass = buffer[57]+256*buffer[58]; + break; + } + + data->StatusCode[0] = 0x08; + data->StatusCode[1] = 0x00; + data->StatusCode[2] = 0x00; + data->StatusCode[3] = 0x00; + data->StatusCode[4] = 0x00; + + /* Overload if (riello_test_bit(&DevData.StatusCode[4], 2)) */ + if (buffer[31] & 128) + data->StatusCode[4] |= 0x04; + + /* Bypass if (riello_test_bit(&DevData.StatusCode[1], 3)) */ + if (((buffer[31] & 2) || (riello_test_bit(&buffer[32], 0)) || (riello_test_bit(&buffer[32], 12)) || + (riello_test_bit(&buffer[32], 13)) || (riello_test_bit(&buffer[32], 14))) && (!(buffer[34] & 8))) + data->StatusCode[1] |= 0x08; + + /* AC Fail if (riello_test_bit(&DevData.StatusCode[0], 1)) */ + if (buffer[31] & 8) + data->StatusCode[0] |= 0x02; + + /* LowBatt if ((riello_test_bit(&DevData.StatusCode[0], 1)) && (riello_test_bit(&DevData.StatusCode[0], 0))) */ + if (buffer[31] & 16) { + if (buffer[31] & 8) { + data->StatusCode[0] |= 0x02; + data->StatusCode[0] |= 0x01; + } + } + + /* Standby if (!riello_test_bit(&DevData.StatusCode[0], 3)) */ + if ((buffer[22] & 2) || (buffer[34] & 4) || (buffer[34] & 8)) + data->StatusCode[0] &= 0xF7; + + /* Battery bad (Replace battery) if (riello_test_bit(&DevData.StatusCode[2], 0)) */ + if (buffer[31] & 0x40) + data->StatusCode[2] |= 0x01; +} + +void riello_init_serial() +{ + wait_packet = 1; + buf_ptr_length = 0; + foundbadcrc = 0; + foundnak = 0; +} + +uint8_t riello_header(uint8_t type, uint8_t a, uint8_t* length) +{ + LAST_DATA[0] = LAST_DATA[1]; + LAST_DATA[1] = LAST_DATA[2]; + LAST_DATA[2] = LAST_DATA[3]; + LAST_DATA[3] = LAST_DATA[4]; + LAST_DATA[4] = LAST_DATA[5]; + LAST_DATA[5] = (uint8_t) a; + + switch (type) { + case DEV_RIELLOSENTRY: + if (((LAST_DATA[4]>=192) && (LAST_DATA[5]==103)) || + ((LAST_DATA[4]==176) && (LAST_DATA[5]==164))) { + *length = LAST_DATA[5]; + return(1); + } + break; + case DEV_RIELLOGPSER: + if ((buf_ptr_length==0) && (LAST_DATA[5]>0x20) && (LAST_DATA[4]==0x2)) + return(1); + break; + } + return(0); +} + +uint8_t riello_tail(uint8_t type, uint8_t length) +{ + uint8_t number; + + switch (type) { + case DEV_RIELLOSENTRY: + number = length; + + if (buf_ptr_length >= number) + return(1); + break; + case DEV_RIELLOGPSER: + if (LAST_DATA[5] == 0x03) + return(1); + break; + } + return(0); +} + +uint8_t riello_test_nak(uint8_t type, uint8_t* buffer) +{ + switch (type) { + case DEV_RIELLOGPSER: + if (buffer[3] == 0x15) + return(1); + break; + } + return(0); +} + +void riello_parse_serialport(uint8_t typedev, uint8_t* buffer, uint8_t checksum) +{ + static uint8_t actual_char, int_i; + static uint8_t length; + + actual_char = commbyte; + + if ((riello_header(typedev, actual_char, &length)) && (!foundheader)) { + upsdebugx(5,"Header detected: LAST_DATA:%X,%X,%X,%X,%X,%X buf_ptr:%i \n\r", + LAST_DATA[0], LAST_DATA[1], LAST_DATA[2], + LAST_DATA[3], LAST_DATA[4], LAST_DATA[5], buf_ptr_length); + + foundheader = 1; + buf_ptr_length = 1; + memset(buffer, 0, BUFFER_SIZE); + buffer[0] = LAST_DATA[4]; + } + + if ((foundheader) && (buf_ptr_length < BUFFER_SIZE)) + buffer[buf_ptr_length++] = actual_char; + + if ((foundheader) && (riello_tail(typedev, length))) { + upsdebugx(5,"\n\rEnd detected: LAST_DATA:%X,%X,%X,%X,%X,%X buf_ptr:%i \n\r", + LAST_DATA[0], LAST_DATA[1], LAST_DATA[2], + LAST_DATA[3], LAST_DATA[4], LAST_DATA[5], buf_ptr_length); + + foundheader = 0; + + for (int_i=0; int_i<6; int_i++) + LAST_DATA[int_i] = 0; + + if (riello_test_nak(typedev, buffer)) { + wait_packet = 0; + foundnak = 1; + } + + if (riello_test_crc(typedev, buffer, buf_ptr_length, checksum)) { + wait_packet = 0; + foundbadcrc = 1; + } + else { + wait_packet = 0; + foundbadcrc = 0; + } + } +} diff --git a/drivers/riello.h b/drivers/riello.h new file mode 100644 index 0000000..2f3c6a7 --- /dev/null +++ b/drivers/riello.h @@ -0,0 +1,187 @@ +/* + * riello.h: defines/macros for Riello protocol based UPSes + * + * Documents describing the protocol implemented by this driver can be + * found online at: + * + * http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf + * http://www.networkupstools.org/ups-protocols/riello/PSSENTR-0100.pdf + * + * Copyright (C) 2012 - Elio Parisi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Reference of the derivative work: blazer driver + */ + +#ifndef NUT_RIELLO_H_SEEN +#define NUT_RIELLO_H_SEEN 1 + +#include + +#define CTRL_RETRIES 50 +#define CTRL_TIMEOUT 100 + +#define MAX_READ_WRITE (16 * 1024) + +#define USB_WRITE_DELAY 200 + +#define MAXTRIES 3 +#define COUNTLOST 10 + +#define DEV_RIELLOSENTRY 14 +#define DEV_RIELLOGPSER 21 + +#define LENGTH_GI 68 +#define LENGTH_GN 34 +#define LENGTH_RS_MM 42 +#define LENGTH_RS_TM 48 +#define LENGTH_RS_TT 64 +#define LENGTH_RE 70 +#define LENGTH_RC 56 +#define LENGTH_DEF 12 + +#define SENTR_EXT176 176 +#define SENTR_ALSO240 240 +#define SENTR_ONLY192 192 + +#define BUFFER_SIZE 220 + +typedef struct { + uint16_t SWversion; + uint16_t Model; + uint16_t Uinp1; + uint16_t Uinp2; + uint16_t Uinp3; + uint16_t Iinp1; + uint16_t Iinp2; + uint16_t Iinp3; + uint16_t Finp; + + uint16_t Uout1; + uint16_t Uout2; + uint16_t Uout3; + uint16_t Iout1; + uint16_t Iout2; + uint16_t Iout3; + uint16_t Pout1; + uint16_t Pout2; + uint16_t Pout3; + uint16_t Ipout1; + uint16_t Ipout2; + uint16_t Ipout3; + uint16_t Fout; + + uint16_t BatTime; + uint16_t BatCap; + uint16_t Ubat; + uint16_t Ibat; + + uint16_t Tsystem; + uint16_t NomBatCap; + + uint16_t Ubypass1; + uint16_t Ubypass2; + uint16_t Ubypass3; + uint16_t Fbypass; + uint16_t LockUPS; + + uint8_t AlarmCode[4]; + char AlarmCodeT[12]; + uint8_t StatusCode[12]; + char StatusCodeT[42]; + + char Identification[18]; + char ModelStr[18]; + char Version[14]; + + uint16_t NomPowerKVA; + uint16_t NomPowerKW; + uint16_t NomUbat; + uint16_t NumBat; + + uint16_t UbatPerc; + + uint16_t NominalUout; + + uint16_t Boost; + uint16_t Buck; + + uint8_t Identif_bytes[12]; + uint16_t NomFout; + + uint32_t Pout1VA; + uint32_t Pout2VA; + uint32_t Pout3VA; + uint32_t Pout1W; + uint32_t Pout2W; + uint32_t Pout3W; +} TRielloData; + +/* CRC and Checksum functions */ +uint16_t riello_calc_CRC(uint8_t type, uint8_t *buff, uint16_t size, uint8_t checksum); +void riello_create_crc(uint8_t type, uint8_t *buff, uint16_t size, uint8_t checksum); +uint8_t riello_test_crc(uint8_t type, uint8_t *buff, uint16_t size, uint8_t chacksum); +uint8_t riello_test_bit(uint8_t *basic_address, uint8_t bit); + +/* send GPSER command functions */ +uint8_t riello_prepare_gi(uint8_t* buffer); +uint8_t riello_prepare_gn(uint8_t* buffer, uint8_t gpser_error_control); +uint8_t riello_prepare_rs(uint8_t* buffer, uint8_t gpser_error_control); +uint8_t riello_prepare_re(uint8_t* buffer, uint8_t gpser_error_control); +uint8_t riello_prepare_rc(uint8_t* buffer, uint8_t gpser_error_control); +uint8_t riello_prepare_cs(uint8_t* buffer, uint8_t gpser_error_control, uint16_t delay); +uint8_t riello_prepare_cr(uint8_t* buffer, uint8_t gpser_error_control, uint16_t delay); +uint8_t riello_prepare_cd(uint8_t* buffer, uint8_t gpser_error_control); +uint8_t riello_prepare_tp(uint8_t* buffer, uint8_t gpser_error_control); +uint8_t riello_prepare_tb(uint8_t* buffer, uint8_t gpser_error_control); + +/* send SENTR command functions */ +uint8_t riello_prepare_shutsentr(uint8_t* buffer, uint16_t delay); +uint8_t riello_prepare_cancelsentr(uint8_t* buffer); +uint8_t riello_prepare_setrebsentr(uint8_t* buffer, uint16_t delay); +uint8_t riello_prepare_rebsentr(uint8_t* buffer, uint16_t delay); +uint8_t riello_prepare_tbsentr(uint8_t* buffer); + +/* parse GPSER ups responses */ +void riello_parse_gi(uint8_t* buffer, TRielloData* data); +void riello_parse_gn(uint8_t* buffer, TRielloData* data); +void riello_parse_rs(uint8_t* buffer, TRielloData* data, uint8_t numread); +void riello_parse_re(uint8_t* buffer, TRielloData* data); +void riello_parse_rc(uint8_t* buffer, TRielloData* data); + +/* parse SENTR ups responses */ +void riello_parse_sentr(uint8_t* buffer, TRielloData* data); + +/* communication functions */ +void riello_init_serial(void); +uint8_t riello_header(uint8_t type, uint8_t a, uint8_t* length); +uint8_t riello_tail(uint8_t type, uint8_t length); +uint8_t riello_test_nak(uint8_t type, uint8_t* buffer); +void riello_parse_serialport(uint8_t typedev, uint8_t* buffer, uint8_t checksum); +void riello_comm_setup(const char *port); + +int get_ups_statuscode(void); + +/* Shared global variables for driver subtypes */ +/* extern uint8_t foundheader; */ +extern uint16_t buf_ptr_length; +extern uint8_t wait_packet; +extern uint8_t foundnak; +extern uint8_t foundbadcrc; +extern uint8_t commbyte; +extern uint8_t requestSENTR; + +#endif /* NUT_RIELLO_H_SEEN */ diff --git a/drivers/riello_ser.c b/drivers/riello_ser.c new file mode 100644 index 0000000..8c2714f --- /dev/null +++ b/drivers/riello_ser.c @@ -0,0 +1,1116 @@ +/* + * riello_ser.c: support for Riello serial protocol based UPSes + * + * A document describing the protocol implemented by this driver can be + * found online at "http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf" + * and "http://www.networkupstools.org/ups-protocols/riello/PSSENTR-0100.pdf". + * + * Copyright (C) 2012 - Elio Parisi + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Reference of the derivative work: blazer driver + */ + +#include "config.h" /* must be the first header */ + +#include +#include + +#include "main.h" +#include "serial.h" +#include "timehead.h" +/* +// The serial driver has no need for HID structures/code currently +// (maybe there is/was a plan for sharing something between siblings). +// Note that HID is tied to libusb or libshut definitions at the moment. +#include "hidparser.h" +#include "hidtypes.h" +*/ +#include "common.h" /* for upsdebugx() etc */ +#include "riello.h" + +#define DRIVER_NAME "Riello serial driver" +#define DRIVER_VERSION "0.07" + +#define DEFAULT_OFFDELAY 5 +#define DEFAULT_BOOTDELAY 5 + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Elio Parisi ", + DRV_EXPERIMENTAL, + { NULL } +}; + +static uint8_t bufOut[BUFFER_SIZE]; +static uint8_t bufIn[BUFFER_SIZE]; + +static uint8_t gpser_error_control; +static uint8_t typeRielloProtocol; + +static uint8_t input_monophase; +static uint8_t output_monophase; + +static unsigned int offdelay = DEFAULT_OFFDELAY; +static unsigned int bootdelay = DEFAULT_BOOTDELAY; + +static TRielloData DevData; + +/********************************************************************** + * char_read (char *bytes, size_t size, int read_timeout) + * + * reads size bytes from the serial port + * + * bytes - buffer to store the data + * size - size of the data to get + * read_timeout - serial timeout (in milliseconds) + * + * return -1 on error, -2 on timeout, nb_bytes_readen on success + * + *********************************************************************/ +static ssize_t char_read (char *bytes, size_t size, int read_timeout) +{ + struct timeval serial_timeout; + fd_set readfs; + ssize_t readen = 0; + int rc = 0; + + FD_ZERO (&readfs); + FD_SET (upsfd, &readfs); + + serial_timeout.tv_usec = (read_timeout % 1000) * 1000; + serial_timeout.tv_sec = (read_timeout / 1000); + + rc = select (upsfd + 1, &readfs, NULL, NULL, &serial_timeout); + if (0 == rc) + return -2; /* timeout */ + + if (FD_ISSET (upsfd, &readfs)) { + ssize_t now = read (upsfd, bytes, size - (size_t)readen); + + if (now < 0) { + return -1; + } + else { + readen += now; + } + } + else { + return -1; + } + return readen; +} + +/********************************************************************** + * serial_read (int read_timeout) + * + * return data one byte at a time + * + * read_timeout - serial timeout (in milliseconds) + * + * returns 0 on success, -1 on error, -2 on timeout + * + **********************************************************************/ +static ssize_t serial_read (int read_timeout, unsigned char *readbuf) +{ + static unsigned char cache[512]; + static unsigned char *cachep = cache; + static unsigned char *cachee = cache; + ssize_t recv; + *readbuf = '\0'; + + /* if still data in cache, get it */ + if (cachep < cachee) { + *readbuf = *cachep++; + return 0; + /* return (int) *cachep++; */ + } + recv = char_read ((char *)cache, 1, read_timeout); + + if ((recv == -1) || (recv == -2)) + return recv; + + cachep = cache; + cachee = cache + recv; + cachep = cache; + cachee = cache + recv; + + if (recv) { + upsdebugx(5,"received: %02x", *cachep); + *readbuf = *cachep++; + return 0; + } + return -1; +} + +static void riello_serialcomm(uint8_t* arg_bufIn, uint8_t typedev) +{ + time_t realt, nowt; + uint8_t commb = 0; + + realt = time(NULL); + while (wait_packet) { + serial_read(1000, &commb); + nowt = time(NULL); + commbyte = commb; + riello_parse_serialport(typedev, arg_bufIn, gpser_error_control); + + if ((nowt - realt) > 4) + break; + } +} + +static int get_ups_nominal() +{ + uint8_t length; + + riello_init_serial(); + + length = riello_prepare_gn(&bufOut[0], gpser_error_control); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Communication error while writing to port"); + return -1; + } + + riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Get nominal Ko: bad CRC or Checksum"); + return -1; + } + + /* mandatory */ + if (!wait_packet && foundnak) { + upsdebugx (3, "Get nominal Ko: command not supported"); + return -1; + } + + upsdebugx (3, "Get nominal Ok: received byte %u", buf_ptr_length); + + riello_parse_gn(&bufIn[0], &DevData); + + return 0; +} + +static int get_ups_status() +{ + uint8_t numread, length; + + riello_init_serial(); + + length = riello_prepare_rs(&bufOut[0], gpser_error_control); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Communication error while writing to port"); + return -1; + } + + if (input_monophase) + numread = LENGTH_RS_MM; + else if (output_monophase) + numread = LENGTH_RS_TM; + else + numread = LENGTH_RS_TT; + + riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Get status Ko: bad CRC or Checksum"); + return -1; + } + + /* mandatory */ + if (!wait_packet && foundnak) { + upsdebugx (3, "Get status Ko: command not supported"); + return -1; + } + + upsdebugx (3, "Get status Ok: received byte %u", buf_ptr_length); + + riello_parse_rs(&bufIn[0], &DevData, numread); + + return 0; +} + +static int get_ups_extended() +{ + uint8_t length; + + riello_init_serial(); + + length = riello_prepare_re(&bufOut[0], gpser_error_control); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Communication error while writing to port"); + return -1; + } + + riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Get extended Ko: bad CRC or Checksum"); + return -1; + } + + /* optonal */ + if (!wait_packet && foundnak) { + upsdebugx (3, "Get extended Ko: command not supported"); + return 0; + } + + upsdebugx (3, "Get extended Ok: received byte %u", buf_ptr_length); + + riello_parse_re(&bufIn[0], &DevData); + + return 0; +} + +/* Not static, exposed via header. Not used though, currently... */ +int get_ups_statuscode() +{ + uint8_t length; + + riello_init_serial(); + + length = riello_prepare_rc(&bufOut[0], gpser_error_control); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Communication error while writing to port"); + return -1; + } + + riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Get statuscode Ko: bad CRC or Checksum"); + return -1; + } + + /* optional */ + if (!wait_packet && foundnak) { + upsdebugx (3, "Get statuscode Ko: command not supported"); + return 0; + } + + upsdebugx (3, "Get statuscode Ok: received byte %u", buf_ptr_length); + + riello_parse_rc(&bufIn[0], &DevData); + + return 0; +} + +static int get_ups_sentr() +{ + uint8_t length; + + riello_init_serial(); + + bufOut[0] = requestSENTR; + + if (requestSENTR == SENTR_EXT176) { + bufOut[1] = 103; + bufOut[2] = 1; + bufOut[3] = 0; + bufOut[4] = 24; + length = 5; + } + else + length = 1; + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Communication error while writing to port"); + return -1; + } + + riello_serialcomm(&bufIn[0], DEV_RIELLOSENTRY); + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Get sentry Ko: bad CRC or Checksum"); + return -1; + } + + /* mandatory */ + if (!wait_packet && foundnak) { + upsdebugx (3, "Get sentry Ko: command not supported"); + return -1; + } + + upsdebugx (3, "Get sentry Ok: received byte %u", buf_ptr_length); + + riello_parse_sentr(&bufIn[0], &DevData); + + return 0; +} + +static int riello_instcmd(const char *cmdname, const char *extra) +{ + uint8_t length; + uint16_t delay; + const char *delay_char; + + if (!riello_test_bit(&DevData.StatusCode[0], 1)) { + if (!strcasecmp(cmdname, "load.off")) { + delay = 0; + riello_init_serial(); + + if (typeRielloProtocol == DEV_RIELLOGPSER) + length = riello_prepare_cs(bufOut, gpser_error_control, delay); + else + length = riello_prepare_shutsentr(bufOut, delay); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Command load.off communication error"); + return STAT_INSTCMD_FAILED; + } + + riello_serialcomm(&bufIn[0], typeRielloProtocol); + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command load.off Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command load.off Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command load.off Ok"); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "load.off.delay")) { + int ipv; + delay_char = dstate_getinfo("ups.delay.shutdown"); + ipv = atoi(delay_char); + if (ipv < 0 || (intmax_t)ipv > (intmax_t)UINT16_MAX) return STAT_INSTCMD_FAILED; + delay = (uint16_t)ipv; + riello_init_serial(); + + if (typeRielloProtocol == DEV_RIELLOGPSER) + length = riello_prepare_cs(bufOut, gpser_error_control, delay); + else + length = riello_prepare_shutsentr(bufOut, delay); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Command load.off delay communication error"); + return STAT_INSTCMD_FAILED; + } + + riello_serialcomm(&bufIn[0], typeRielloProtocol); + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command load.off.delay Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command load.off.delay Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command load.off delay Ok"); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "load.on")) { + delay = 0; + riello_init_serial(); + + if (typeRielloProtocol == DEV_RIELLOGPSER) + length = riello_prepare_cr(bufOut, gpser_error_control, delay); + else { + length = riello_prepare_setrebsentr(bufOut, delay); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Command load.on communication error"); + return STAT_INSTCMD_FAILED; + } + + riello_serialcomm(&bufIn[0], typeRielloProtocol); + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command load.on Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command load.on Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + length = riello_prepare_rebsentr(bufOut, delay); + } + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Command load.on communication error"); + return STAT_INSTCMD_FAILED; + } + + riello_serialcomm(&bufIn[0], typeRielloProtocol); + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command load.on Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command load.on Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command load.on Ok"); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "load.on.delay")) { + int ipv; + delay_char = dstate_getinfo("ups.delay.reboot"); + ipv = atoi(delay_char); + if (ipv < 0 || (intmax_t)ipv > (intmax_t)UINT16_MAX) return STAT_INSTCMD_FAILED; + delay = (uint16_t)ipv; + + riello_init_serial(); + + if (typeRielloProtocol == DEV_RIELLOGPSER) + length = riello_prepare_cr(bufOut, gpser_error_control, delay); + else { + length = riello_prepare_setrebsentr(bufOut, delay); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Command load.on delay communication error"); + return STAT_INSTCMD_FAILED; + } + + riello_serialcomm(&bufIn[0], typeRielloProtocol); + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command load.on delay Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command load.on delay Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + length = riello_prepare_rebsentr(bufOut, delay); + } + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Command load.on delay communication error"); + return STAT_INSTCMD_FAILED; + } + + riello_serialcomm(&bufIn[0], typeRielloProtocol); + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command load.on.delay Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command load.on.delay Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command load.on delay Ok"); + return STAT_INSTCMD_HANDLED; + } + } + else { + if (!strcasecmp(cmdname, "shutdown.return")) { + int ipv; + delay_char = dstate_getinfo("ups.delay.shutdown"); + ipv = atoi(delay_char); + if (ipv < 0 || (intmax_t)ipv > (intmax_t)UINT16_MAX) return STAT_INSTCMD_FAILED; + delay = (uint16_t)ipv; + + riello_init_serial(); + + if (typeRielloProtocol == DEV_RIELLOGPSER) + length = riello_prepare_cs(bufOut, gpser_error_control, delay); + else + length = riello_prepare_shutsentr(bufOut, delay); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Command shutdown.return communication error"); + return STAT_INSTCMD_FAILED; + } + + riello_serialcomm(&bufIn[0], typeRielloProtocol); + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command shutdown.return Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command shutdown.return Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command shutdown.return Ok"); + return STAT_INSTCMD_HANDLED; + } + } + + if (!strcasecmp(cmdname, "shutdown.stop")) { + riello_init_serial(); + + if (typeRielloProtocol == DEV_RIELLOGPSER) + length = riello_prepare_cd(bufOut, gpser_error_control); + else + length = riello_prepare_cancelsentr(bufOut); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Command shutdown.stop communication error"); + return STAT_INSTCMD_FAILED; + } + + riello_serialcomm(&bufIn[0], typeRielloProtocol); + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command shutdown.stop Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command shutdown.stop Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command shutdown.stop Ok"); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "test.panel.start")) { + riello_init_serial(); + length = riello_prepare_tp(bufOut, gpser_error_control); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Command test.panel.start communication error"); + return STAT_INSTCMD_FAILED; + } + + riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command test.panel.start Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command test.panel.start Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command test.panel.start Ok"); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "test.battery.start")) { + riello_init_serial(); + if (typeRielloProtocol == DEV_RIELLOGPSER) + length = riello_prepare_tb(bufOut, gpser_error_control); + else + length = riello_prepare_tbsentr(bufOut); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Command test.battery.start communication error"); + return STAT_INSTCMD_FAILED; + } + + riello_serialcomm(&bufIn[0], typeRielloProtocol); + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command battery.start Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command battery.start Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command test.battery.start Ok"); + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; +} + +static int start_ups_comm() +{ + uint8_t length; + + upsdebugx (2, "entering start_ups_comm()\n"); + + riello_init_serial(); + + if (typeRielloProtocol == DEV_RIELLOGPSER) { + length = riello_prepare_gi(&bufOut[0]); + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Communication error while writing to port"); + return -1; + } + + riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); + } + else { + bufOut[0] = 192; + length = 1; + + if (ser_send_buf(upsfd, bufOut, length) == 0) { + upsdebugx (3, "Communication error while writing to port"); + return -1; + } + + riello_serialcomm(&bufIn[0], DEV_RIELLOSENTRY); + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Get identif Ko: bad CRC or Checksum"); + return 1; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Get identif Ko: command not supported"); + return 1; + } + + upsdebugx (3, "Get identif Ok: received byte %u", buf_ptr_length); + return 0; + +} + +void upsdrv_initinfo(void) +{ + int ret; + + ret = start_ups_comm(); + + if (ret < 0) + fatalx(EXIT_FAILURE, "No communication with UPS"); + else if (ret > 0) + fatalx(EXIT_FAILURE, "Bad checksum or NACK"); + else + upsdebugx(2, "Communication with UPS established"); + + if (typeRielloProtocol == DEV_RIELLOGPSER) + riello_parse_gi(&bufIn[0], &DevData); + else + riello_parse_sentr(&bufIn[0], &DevData); + + gpser_error_control = DevData.Identif_bytes[4]-0x30; + if ((DevData.Identif_bytes[0] == '1') || (DevData.Identif_bytes[0] == '2')) + input_monophase = 1; + else { + input_monophase = 0; + dstate_setinfo("input.phases", "%u", 3); + dstate_setinfo("input.phases", "%u", 3); + dstate_setinfo("input.bypass.phases", "%u", 3); + } + if ((DevData.Identif_bytes[0] == '1') || (DevData.Identif_bytes[0] == '3')) + output_monophase = 1; + else { + output_monophase = 0; + dstate_setinfo("output.phases", "%u", 3); + } + + dstate_setinfo("device.mfr", "RPS S.p.a."); + dstate_setinfo("device.model", "%s", (unsigned char*) DevData.ModelStr); + dstate_setinfo("device.serial", "%s", (unsigned char*) DevData.Identification); + dstate_setinfo("device.type", "ups"); + + dstate_setinfo("ups.mfr", "RPS S.p.a."); + dstate_setinfo("ups.model", "%s", (unsigned char*) DevData.ModelStr); + dstate_setinfo("ups.serial", "%s", (unsigned char*) DevData.Identification); + dstate_setinfo("ups.firmware", "%s", (unsigned char*) DevData.Version); + + if (typeRielloProtocol == DEV_RIELLOGPSER) { + if (get_ups_nominal() == 0) { + dstate_setinfo("ups.realpower.nominal", "%u", DevData.NomPowerKW); + dstate_setinfo("ups.power.nominal", "%u", DevData.NomPowerKVA); + dstate_setinfo("output.voltage.nominal", "%u", DevData.NominalUout); + dstate_setinfo("output.frequency.nominal", "%.1f", DevData.NomFout/10.0); + dstate_setinfo("battery.voltage.nominal", "%u", DevData.NomUbat); + dstate_setinfo("battery.capacity", "%u", DevData.NomBatCap); + } + } + else { + if (get_ups_sentr() == 0) { + dstate_setinfo("ups.realpower.nominal", "%u", DevData.NomPowerKW); + dstate_setinfo("ups.power.nominal", "%u", DevData.NomPowerKVA); + dstate_setinfo("output.voltage.nominal", "%u", DevData.NominalUout); + dstate_setinfo("output.frequency.nominal", "%.1f", DevData.NomFout/10.0); + dstate_setinfo("battery.voltage.nominal", "%u", DevData.NomUbat); + dstate_setinfo("battery.capacity", "%u", DevData.NomBatCap); + } + } + + + /* commands ----------------------------------------------- */ + dstate_addcmd("load.off"); + dstate_addcmd("load.on"); + dstate_addcmd("load.off.delay"); + dstate_addcmd("load.on.delay"); + dstate_addcmd("shutdown.return"); + dstate_addcmd("shutdown.stop"); + dstate_addcmd("test.battery.start"); + + dstate_setinfo("ups.delay.shutdown", "%u", offdelay); + dstate_setflags("ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("ups.delay.shutdown", 3); + dstate_setinfo("ups.delay.reboot", "%u", bootdelay); + dstate_setflags("ups.delay.reboot", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("ups.delay.reboot", 3); + + + if (typeRielloProtocol == DEV_RIELLOGPSER) + dstate_addcmd("test.panel.start"); + + /* install handlers */ +/* upsh.setvar = hid_set_value; setvar; */ + upsh.instcmd = riello_instcmd; +} + +void upsdrv_updateinfo(void) +{ + uint8_t getextendedOK; + static int countlost = 0; + int stat; + + upsdebugx(1, "countlost %d",countlost); + + if (countlost > 0){ + upsdebugx(1, "Communication with UPS is lost: status read failed!"); + + if (countlost == COUNTLOST) { + dstate_datastale(); + upslogx(LOG_WARNING, "Communication with UPS is lost: status read failed!"); + } + } + + if (typeRielloProtocol == DEV_RIELLOGPSER) + stat = get_ups_status(); + else + stat = get_ups_sentr(); + + if (stat < 0) { + if (countlost < COUNTLOST) + countlost++; + return; + } + + if (typeRielloProtocol == DEV_RIELLOGPSER) { + if (get_ups_extended() == 0) + getextendedOK = 1; + else + getextendedOK = 0; + } + else + getextendedOK = 1; + + if (countlost == COUNTLOST) + upslogx(LOG_NOTICE, "Communication with UPS is re-established!"); + + dstate_setinfo("input.frequency", "%.2f", DevData.Finp/10.0); + dstate_setinfo("input.bypass.frequency", "%.2f", DevData.Fbypass/10.0); + dstate_setinfo("output.frequency", "%.2f", DevData.Fout/10.0); + dstate_setinfo("battery.voltage", "%.1f", DevData.Ubat/10.0); + + if ((DevData.BatCap < 0xFFFF) && (DevData.BatTime < 0xFFFF)) { + dstate_setinfo("battery.charge", "%u", DevData.BatCap); + dstate_setinfo("battery.runtime", "%u", DevData.BatTime*60); + } + + if (DevData.Tsystem < 0xFF) + dstate_setinfo("ups.temperature", "%u", DevData.Tsystem); + + if (input_monophase) { + dstate_setinfo("input.voltage", "%u", DevData.Uinp1); + dstate_setinfo("input.bypass.voltage", "%u", DevData.Ubypass1); + } + else { + dstate_setinfo("input.L1-N.voltage", "%u", DevData.Uinp1); + dstate_setinfo("input.L2-N.voltage", "%u", DevData.Uinp2); + dstate_setinfo("input.L3-N.voltage", "%u", DevData.Uinp3); + dstate_setinfo("input.bypass.L1-N.voltage", "%u", DevData.Ubypass1); + dstate_setinfo("input.bypass.L2-N.voltage", "%u", DevData.Ubypass2); + dstate_setinfo("input.bypass.L3-N.voltage", "%u", DevData.Ubypass3); + } + + if (output_monophase) { + dstate_setinfo("output.voltage", "%u", DevData.Uout1); + dstate_setinfo("output.power.percent", "%u", DevData.Pout1); + dstate_setinfo("ups.load", "%u", DevData.Pout1); + } + else { + dstate_setinfo("output.L1-N.voltage", "%u", DevData.Uout1); + dstate_setinfo("output.L2-N.voltage", "%u", DevData.Uout2); + dstate_setinfo("output.L3-N.voltage", "%u", DevData.Uout3); + dstate_setinfo("output.L1.power.percent", "%u", DevData.Pout1); + dstate_setinfo("output.L2.power.percent", "%u", DevData.Pout2); + dstate_setinfo("output.L3.power.percent", "%u", DevData.Pout3); + dstate_setinfo("ups.load", "%u", (DevData.Pout1+DevData.Pout2+DevData.Pout3)/3); + } + + status_init(); + + /* AC Fail */ + if (riello_test_bit(&DevData.StatusCode[0], 1)) + status_set("OB"); + else + status_set("OL"); + + /* LowBatt */ + if ((riello_test_bit(&DevData.StatusCode[0], 1)) && + (riello_test_bit(&DevData.StatusCode[0], 0))) + status_set("LB"); + + /* Standby */ + if (!riello_test_bit(&DevData.StatusCode[0], 3)) + status_set("OFF"); + + /* On Bypass */ + if (riello_test_bit(&DevData.StatusCode[1], 3)) + status_set("BYPASS"); + + /* Overload */ + if (riello_test_bit(&DevData.StatusCode[4], 2)) + status_set("OVER"); + + /* Buck */ + if (riello_test_bit(&DevData.StatusCode[1], 0)) + status_set("TRIM"); + + /* Boost */ + if (riello_test_bit(&DevData.StatusCode[1], 1)) + status_set("BOOST"); + + /* Replace battery */ + if (riello_test_bit(&DevData.StatusCode[2], 0)) + status_set("RB"); + + /* Charging battery */ + if (riello_test_bit(&DevData.StatusCode[2], 2)) + status_set("CHRG"); + + status_commit(); + + dstate_dataok(); + + if (getextendedOK) { + dstate_setinfo("output.L1.power", "%u", DevData.Pout1VA); + dstate_setinfo("output.L2.power", "%u", DevData.Pout2VA); + dstate_setinfo("output.L3.power", "%u", DevData.Pout3VA); + dstate_setinfo("output.L1.realpower", "%u", DevData.Pout1W); + dstate_setinfo("output.L2.realpower", "%u", DevData.Pout2W); + dstate_setinfo("output.L3.realpower", "%u", DevData.Pout3W); + dstate_setinfo("output.L1.current", "%u", DevData.Iout1); + dstate_setinfo("output.L2.current", "%u", DevData.Iout2); + dstate_setinfo("output.L3.current", "%u", DevData.Iout3); + } + + poll_interval = 2; + + countlost = 0; +/* if (get_ups_statuscode() != 0) + upsdebugx(2, "Communication is lost"); + else { + }*/ + + /* + * poll_interval = 2; + */ +} + +void upsdrv_shutdown(void) + __attribute__((noreturn)); + +void upsdrv_shutdown(void) +{ + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ + int retry; + + /* maybe try to detect the UPS here, but try a shutdown even if + it doesn't respond at first if possible */ + + /* replace with a proper shutdown function */ + + + /* you may have to check the line status since the commands + for toggling power are frequently different for OL vs. OB */ + + /* OL: this must power cycle the load if possible */ + + /* OB: the load must remain off until the power returns */ + upsdebugx(2, "upsdrv Shutdown execute"); + + for (retry = 1; retry <= MAXTRIES; retry++) { + + if (riello_instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { + continue; + } + + if (riello_instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) { + continue; + } + + fatalx(EXIT_SUCCESS, "Shutting down"); + } + + fatalx(EXIT_FAILURE, "Shutdown failed!"); +} + + +/* +static int setvar(const char *varname, const char *val) +{ + if (!strcasecmp(varname, "ups.test.interval")) { + ser_send_buf(upsfd, ...); + return STAT_SET_HANDLED; + } + + upslogx(LOG_NOTICE, "setvar: unknown variable [%s]", varname); + return STAT_SET_UNKNOWN; +} +*/ + +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ + /* allow '-x xyzzy' */ + /* addvar(VAR_FLAG, "xyzzy", "Enable xyzzy mode"); */ + + /* allow '-x foo=' */ + /* addvar(VAR_VALUE, "foo", "Override foo setting"); */ +} + +void upsdrv_initups(void) +{ + upsdebugx(2, "entering upsdrv_initups()"); + + upsfd = ser_open(device_path); + + riello_comm_setup(device_path); + + /* probe ups type */ + + /* to get variables and flags from the command line, use this: + * + * first populate with upsdrv_buildvartable above, then... + * + * set flag foo : /bin/driver -x foo + * set variable 'cable' to '1234' : /bin/driver -x cable=1234 + * + * to test flag foo in your code: + * + * if (testvar("foo")) + * do_something(); + * + * to show the value of cable: + * + * if ((cable = getval("cable"))) + * printf("cable is set to %s\n", cable); + * else + * printf("cable is not set!\n"); + * + * don't use NULL pointers - test the return result first! + */ + + /* the upsh handlers can't be done here, as they get initialized + * shortly after upsdrv_initups returns to main. + */ + + /* don't try to detect the UPS here */ + + /* initialise communication */ +} + +void upsdrv_cleanup(void) +{ + /* free(dynamic_mem); */ + ser_close(upsfd, device_path); +} + +void riello_comm_setup(const char *port) +{ + uint8_t length; + + upsdebugx(2, "set baudrate 9600"); + ser_set_speed(upsfd, device_path, B9600); + + upsdebugx(2, "try to detect SENTR"); + riello_init_serial(); + bufOut[0] = 192; + ser_send_buf(upsfd, bufOut, 1); + + riello_serialcomm(&bufIn[0], DEV_RIELLOSENTRY); + + if (buf_ptr_length == 103) { + typeRielloProtocol = DEV_RIELLOSENTRY; + upslogx(LOG_INFO, "Connected to UPS SENTR on %s with baudrate %d", port, 9600); + return; + } + + upsdebugx(2, "try to detect GPSER"); + riello_init_serial(); + length = riello_prepare_gi(&bufOut[0]); + + ser_send_buf(upsfd, bufOut, length); + + riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); + + if (!wait_packet && !foundbadcrc && !foundnak) { + typeRielloProtocol = DEV_RIELLOGPSER; + upslogx(LOG_INFO, "Connected to UPS GPSER on %s with baudrate %d", port, 9600); + return; + } + + upsdebugx(2, "set baudrate 1200"); + ser_set_speed(upsfd, device_path, B1200); + + upsdebugx(2, "try to detect SENTR"); + riello_init_serial(); + bufOut[0] = 192; + ser_send_buf(upsfd, bufOut, 1); + + riello_serialcomm(&bufIn[0], DEV_RIELLOSENTRY); + + if (buf_ptr_length == 103) { + typeRielloProtocol = DEV_RIELLOSENTRY; + upslogx(LOG_INFO, "Connected to UPS SENTR on %s with baudrate %d", port, 1200); + return; + } + + upsdebugx(2, "try to detect GPSER"); + riello_init_serial(); + length = riello_prepare_gi(&bufOut[0]); + + ser_send_buf(upsfd, bufOut, length); + + riello_serialcomm(&bufIn[0], DEV_RIELLOGPSER); + + if (!wait_packet && !foundbadcrc && !foundnak) { + typeRielloProtocol = DEV_RIELLOGPSER; + upslogx(LOG_INFO, "Connected to UPS GPSER on %s with baudrate %d", port, 1200); + return; + } + + fatalx(EXIT_FAILURE, "Can't connect to the UPS on port %s!\n", port); +} + diff --git a/drivers/riello_usb.c b/drivers/riello_usb.c new file mode 100644 index 0000000..417ab89 --- /dev/null +++ b/drivers/riello_usb.c @@ -0,0 +1,1227 @@ +/* + * riello_usb.c: support for Riello USB protocol based UPSes + * + * A document describing the protocol implemented by this driver can be + * found online at: + * + * http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf + * + * Copyright (C) 2012 - Elio Parisi + * Copyright (C) 2016 Eaton + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Reference of the derivative work: blazer driver + */ + +#include "config.h" /* must be the first header */ + +#include + +#include "main.h" +#include "nut_libusb.h" +#include "usb-common.h" +#include "riello.h" + +#define DRIVER_NAME "Riello USB driver" +#define DRIVER_VERSION "0.07" + +#define DEFAULT_OFFDELAY 5 /*!< seconds (max 0xFF) */ +#define DEFAULT_BOOTDELAY 5 /*!< seconds (max 0xFF) */ + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Elio Parisi ", + DRV_EXPERIMENTAL, + { NULL } +}; + +static uint8_t bufOut[BUFFER_SIZE]; +static uint8_t bufIn[BUFFER_SIZE]; + +static uint8_t gpser_error_control; + +static uint8_t input_monophase; +static uint8_t output_monophase; + +/*! Time in seconds to delay before shutting down. */ +static unsigned int offdelay = DEFAULT_OFFDELAY; +static unsigned int bootdelay = DEFAULT_BOOTDELAY; + +static TRielloData DevData; + +static usb_communication_subdriver_t *usb = &usb_subdriver; +static usb_dev_handle *udev = NULL; +static USBDevice_t usbdevice; +static USBDeviceMatcher_t *reopen_matcher = NULL; +static USBDeviceMatcher_t *regex_matcher = NULL; + +static int (*subdriver_command)(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t buflen) = NULL; + +static void ussleep(useconds_t usec) +{ + + if (usec == 1) + usec = 400; + else + usec *= 1000; + + usleep(usec); +} + +static int cypress_setfeatures() +{ + int ret; + + bufOut[0] = 0xB0; + bufOut[1] = 0x4; + bufOut[2] = 0x0; + bufOut[3] = 0x0; + bufOut[4] = 0x3; + + /* Write features report */ + ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, + 0x09, /* HID_REPORT_SET = 0x09 */ + 0 + (0x03 << 8), /* HID_REPORT_TYPE_FEATURE */ + 0, (usb_ctrl_charbuf) bufOut, 0x5, 1000); + + if (ret <= 0) { + upsdebugx(3, "send: %s", ret ? nut_usb_strerror(ret) : "error"); + return ret; + } + + upsdebugx(3, "send: features report ok"); + return ret; +} + +static int Send_USB_Packet(uint8_t *send_str, uint16_t numbytes) +{ + uint8_t USB_buff_pom[10]; + int i, err, size; + /*int errno;*/ + + /* is input correct ? */ + if ((!send_str) || (!numbytes)) + return -1; + + size = 7; + + /* routine which parse report into 4-bytes packet */ + for (i=0; i<(numbytes/size); i++) { + USB_buff_pom[0] = 0x37; + USB_buff_pom[1] = send_str[(i*7)]; + USB_buff_pom[2] = send_str[(i*7)+1]; + USB_buff_pom[3] = send_str[(i*7)+2]; + USB_buff_pom[4] = send_str[(i*7)+3]; + USB_buff_pom[5] = send_str[(i*7)+4]; + USB_buff_pom[6] = send_str[(i*7)+5]; + USB_buff_pom[7] = send_str[(i*7)+6]; + + err = usb_bulk_write(udev, 0x2, (usb_ctrl_charbuf) USB_buff_pom, 8, 1000); + + if (err < 0) { + upsdebugx(3, "USB: Send_USB_Packet: send_usb_packet, err = %d %s ", err, strerror(errno)); + return err; + } + ussleep(USB_WRITE_DELAY); + } + + /* send rest of packet */ + if (numbytes % size) { + i = numbytes/size; + memset(USB_buff_pom, '0', sizeof(USB_buff_pom)); + + USB_buff_pom[0] = 0x30+(numbytes%7); + if ((i*7) 0) + upsdebugx(3, "read: %02X %02X %02X %02X %02X %02X %02X %02X", inBuf[0], inBuf[1], inBuf[2], inBuf[3], inBuf[4], inBuf[5], inBuf[6], inBuf[7]); + + if (err < 0){ + upsdebugx(3, "USB: Get_USB_Packet: send_usb_packet, err = %d %s ", err, strerror(errno)); + return err; + } + + /* copy to buffer */ + size = (unsigned char)(inBuf[0]) & 0x07; + if (size) + memcpy(buffer, &inBuf[1], size); + + if (size > INT_MAX) + return -1; + + return (int)size; +} + +static int cypress_command(uint8_t *buffer, uint8_t *buf, uint16_t length, uint16_t buflen) +{ + int loop = 0; + int ret, i = 0; + uint8_t USB_buff[BUFFER_SIZE]; + + /* read to flush buffer */ + riello_init_serial(); + + /* send packet */ + ret = Send_USB_Packet(buffer, length); + + if (ret < 0) { + upsdebugx(3, "Cypress_command send: err %d", ret ); + return ret; + } + + upsdebugx(3, "send ok"); + + memset(buf, 0, buflen); + + while ((buf_ptr_length < BUFFER_SIZE) && wait_packet) { + + memset(USB_buff, 0, sizeof(USB_buff)); + ret = Get_USB_Packet(USB_buff); + + /* + * Any errors here mean that we are unable to read a reply (which + * will happen after successfully writing a command to the UPS) + */ + if (ret < 0) { + upsdebugx(3, "Cypress_command read: err %d", ret ); + return ret; + } + + for (i = 0; i < ret; i++ ) { + commbyte = USB_buff[i]; + riello_parse_serialport(DEV_RIELLOGPSER, buf, gpser_error_control); + } + + loop++; + if (loop>300){ + wait_packet=0; + upsdebugx(1, "wait_packet reset"); + } + + ussleep(10); + } + + upsdebugx(3, "in read: %u", buf_ptr_length); + + return buf_ptr_length; +} + +static void *cypress_subdriver(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + + subdriver_command = &cypress_command; + return NULL; +} + +/* Riello (Cypress Semiconductor Corp.) */ +#define RIELLO_VENDORID 0x04b4 + +static usb_device_id_t riello_usb_id[] = { + /* various models */ + { USB_DEVICE(RIELLO_VENDORID, 0x5500), &cypress_subdriver }, + + /* Terminating entry */ + { 0, 0, NULL } +}; + + +static int device_match_func(USBDevice_t *hd, void *privdata) +{ + NUT_UNUSED_VARIABLE(privdata); + + if (subdriver_command) { + return 1; + } + + switch (is_usb_device_supported(riello_usb_id, hd)) + { + case SUPPORTED: + return 1; + + case POSSIBLY_SUPPORTED: + case NOT_SUPPORTED: + default: + return 0; + } +} + + +static USBDeviceMatcher_t device_matcher = { + &device_match_func, + NULL, + NULL +}; + + +/* + * Callback that is called by usb_device_open() that handles USB device + * settings prior to accepting the devide. At the very least claim the + * device here. Detaching the kernel driver will be handled by the + * caller, don't do this here. Return < 0 on error, 0 or higher on + * success. + */ +static int driver_callback(usb_dev_handle *handle, USBDevice_t *device, usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen) +{ + int ret = 0; + NUT_UNUSED_VARIABLE(device); + NUT_UNUSED_VARIABLE(rdbuf); + NUT_UNUSED_VARIABLE(rdlen); + +/* + if ((ret = usb_set_configuration(handle, 1)) < 0) { + upslogx(LOG_WARNING, "Can't set USB configuration: %s", nut_usb_strerror(ret)); + return -1; + } +*/ + + if ((ret = usb_claim_interface(handle, 0)) < 0) { + upslogx(LOG_WARNING, "Can't claim USB interface: %s", nut_usb_strerror(ret)); + return -1; + } + + /* TODO: HID SET_IDLE to 0 (not necessary?) */ + + return 1; +} + +/* + * Generic command processing function. Send a command and read a reply. + * Returns < 0 on error, 0 on timeout and the number of bytes read on + * success. + */ +static int riello_command(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t buflen) +{ + int ret; + + if (udev == NULL) { + ret = usb->open(&udev, &usbdevice, reopen_matcher, &driver_callback); + + upsdebugx (3, "riello_command err udev NULL : %d ", ret); + if (ret < 0) + return ret; + + upsdrv_initinfo(); /* reconnect usb cable */ + } + + ret = (*subdriver_command)(cmd, buf, length, buflen); + if (ret >= 0) { + upsdebugx (3, "riello_command ok: %u", ret); + return ret; + } + + upsdebugx (3, "riello_command err: %d", ret); + + switch (ret) + { + case ERROR_BUSY: /* Device or resource busy */ + fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver"); +#ifndef HAVE___ATTRIBUTE__NORETURN + exit(EXIT_FAILURE); /* Should not get here in practice, but compiler is afraid we can fall through */ +#endif + +#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ + case -EPERM: /* Operation not permitted */ + fatal_with_errno(EXIT_FAILURE, "Permissions problem"); +# ifndef HAVE___ATTRIBUTE__NORETURN + exit(EXIT_FAILURE); /* Should not get here in practice, but compiler is afraid we can fall through */ +# endif +#endif + + case ERROR_PIPE: /* Broken pipe */ + if (usb_clear_halt(udev, 0x81) == 0) { + upsdebugx(1, "Stall condition cleared"); + break; + } +#if (defined ETIME) && ETIME && WITH_LIBUSB_0_1 + goto fallthrough_case_etime; + case -ETIME: /* Timer expired */ + fallthrough_case_etime: +#endif + if (usb_reset(udev) == 0) { + upsdebugx(1, "Device reset handled"); + } + goto fallthrough_case_reconnect; + case ERROR_NO_DEVICE: /* No such device */ + case ERROR_ACCESS: /* Permission denied */ + case ERROR_IO: /* I/O error */ +#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ + case -ENXIO: /* No such device or address */ +#endif + case ERROR_NOT_FOUND: /* No such file or directory */ + fallthrough_case_reconnect: + /* Uh oh, got to reconnect! */ + usb->close(udev); + udev = NULL; + break; + + case ERROR_TIMEOUT: /* Connection timed out */ + upsdebugx (3, "riello_command err: Resource temporarily unavailable"); + break; + + case ERROR_OVERFLOW: /* Value too large for defined data type */ +#if EPROTO && WITH_LIBUSB_0_1 + case -EPROTO: /* Protocol error */ +#endif + break; + default: + break; + } + + return ret; +} + +static int get_ups_nominal() +{ + + uint8_t length; + int recv; + + length = riello_prepare_gn(&bufOut[0], gpser_error_control); + + recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_GN); + + if (recv < 0){ + upsdebugx (3, "Get nominal err: read byte: %d", recv); + return recv; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Get nominal Ko: bad CRC or Checksum"); + return -1; + } + + /* mandatory */ + if (!wait_packet && foundnak) { + upsdebugx (3, "Get nominal Ko: command not supported"); + return -1; + } + + upsdebugx (3, "Get nominal Ok: read byte: %d", recv); + + riello_parse_gn(&bufIn[0], &DevData); + + return 0; +} + +static int get_ups_status() +{ + uint8_t numread, length; + int recv; + + length = riello_prepare_rs(&bufOut[0], gpser_error_control); + + if (input_monophase) + numread = LENGTH_RS_MM; + else if (output_monophase) + numread = LENGTH_RS_TM; + else + numread = LENGTH_RS_TT; + + recv = riello_command(&bufOut[0], &bufIn[0], length, numread); + + if (recv < 0){ + upsdebugx (3, "Get status err: read byte: %d", recv); + return recv; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Get status Ko: bad CRC or Checksum"); + return -1; + } + + /* mandatory */ + if (!wait_packet && foundnak) { + upsdebugx (3, "Get status Ko: command not supported"); + return -1; + } + + upsdebugx (3, "Get status Ok: read byte: %d", recv); + + riello_parse_rs(&bufIn[0], &DevData, numread); + + return 0; +} + +static int get_ups_extended() +{ + uint8_t length; + int recv; + + length = riello_prepare_re(&bufOut[0], gpser_error_control); + + recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_RE); + + if (recv < 0){ + upsdebugx (3, "Get extended err: read byte: %d", recv); + return recv; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Get extended Ko: bad CRC or Checksum"); + return -1; + } + + /* optional */ + if (!wait_packet && foundnak) { + upsdebugx (3, "Get extended Ko: command not supported"); + return 0; + } + + upsdebugx (3, "Get extended Ok: read byte: %d", recv); + + riello_parse_re(&bufIn[0], &DevData); + + return 0; +} + +/* Not static, exposed via header. Not used though, currently... */ +int get_ups_statuscode() +{ + uint8_t length; + int recv; + + length = riello_prepare_rc(&bufOut[0], gpser_error_control); + + recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_RC); + + if (recv < 0){ + upsdebugx (3, "Get statuscode err: read byte: %d", recv); + return recv; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Get statuscode Ko: bad CRC or Checksum"); + return -1; + } + + /* optional */ + if (!wait_packet && foundnak) { + upsdebugx (3, "Get statuscode Ko: command not supported"); + return 0; + } + + upsdebugx (3, "Get statuscode Ok: read byte: %d", recv); + + riello_parse_rc(&bufIn[0], &DevData); + + return 0; +} + +static int riello_instcmd(const char *cmdname, const char *extra) +{ + uint8_t length; + int recv; + uint16_t delay; + const char *delay_char; + + if (!riello_test_bit(&DevData.StatusCode[0], 1)) { + + if (!strcasecmp(cmdname, "load.off")) { + delay = 0; + + length = riello_prepare_cs(bufOut, gpser_error_control, delay); + recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); + + if (recv < 0) { + upsdebugx (3, "Command load.off err: read byte: %d", recv); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command load.off Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command load.off Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command load.off Ok: read byte: %d", recv); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "load.off.delay")) { + int ipv; + delay_char = dstate_getinfo("ups.delay.shutdown"); + ipv = atoi(delay_char); + /* With a "char" in the name, might assume we fit... but :) */ + if (ipv < 0 || (intmax_t)ipv > (intmax_t)UINT16_MAX) return STAT_INSTCMD_FAILED; + delay = (uint16_t)ipv; + + length = riello_prepare_cs(bufOut, gpser_error_control, delay); + recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); + + if (recv < 0) { + upsdebugx (3, "Command load.off.delay err: read byte: %d", recv); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command load.off.delay Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command load.off.delay Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command load.off.delay Ok: read byte: %d", recv); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "load.on")) { + delay = 0; + + length = riello_prepare_cr(bufOut, gpser_error_control, delay); + recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); + + if (recv < 0) { + upsdebugx (3, "Command load.on err: read byte: %d", recv); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command load.on Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command load.on Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command load.on Ok: read byte: %d", recv); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "load.on.delay")) { + int ipv; + delay_char = dstate_getinfo("ups.delay.reboot"); + ipv = atoi(delay_char); + /* With a "char" in the name, might assume we fit... but :) */ + if (ipv < 0 || (intmax_t)ipv > (intmax_t)UINT16_MAX) return STAT_INSTCMD_FAILED; + delay = (uint16_t)ipv; + + length = riello_prepare_cr(bufOut, gpser_error_control, delay); + recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); + + if (recv < 0) { + upsdebugx (3, "Command load.on.delay err: read byte: %d", recv); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command load.on.delay Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command load.on.delay Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command load.on.delay Ok: read byte: %d", recv); + return STAT_INSTCMD_HANDLED; + } + } + else { + if (!strcasecmp(cmdname, "shutdown.return")) { + int ipv; + delay_char = dstate_getinfo("ups.delay.shutdown"); + ipv = atoi(delay_char); + /* With a "char" in the name, might assume we fit... but :) */ + if (ipv < 0 || (intmax_t)ipv > (intmax_t)UINT16_MAX) return STAT_INSTCMD_FAILED; + delay = (uint16_t)ipv; + + length = riello_prepare_cs(bufOut, gpser_error_control, delay); + recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); + + if (recv < 0) { + upsdebugx (3, "Command shutdown.return err: read byte: %d", recv); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command shutdown.return Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command shutdown.return Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command shutdown.return Ok: read byte: %d", recv); + return STAT_INSTCMD_HANDLED; + } + } + + if (!strcasecmp(cmdname, "shutdown.stop")) { + length = riello_prepare_cd(bufOut, gpser_error_control); + recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); + + if (recv < 0) { + upsdebugx (3, "Command shutdown.stop err: read byte: %d", recv); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command shutdown.stop Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command shutdown.stop Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command shutdown.stop Ok: read byte: %d", recv); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "test.panel.start")) { + length = riello_prepare_tp(bufOut, gpser_error_control); + recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); + + if (recv < 0) { + upsdebugx (3, "Command test.panel.start err: read byte: %d", recv); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command test.panel.start Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command test.panel.start Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command test.panel.start Ok: read byte: %d", recv); + return STAT_INSTCMD_HANDLED; + } + + if (!strcasecmp(cmdname, "test.battery.start")) { + length = riello_prepare_tb(bufOut, gpser_error_control); + recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF); + + if (recv < 0) { + upsdebugx (3, "Command test.battery.start err: read byte: %d", recv); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Command test.battery.start Ko: bad CRC or Checksum"); + return STAT_INSTCMD_FAILED; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Command test.battery.start Ko: command not supported"); + return STAT_INSTCMD_FAILED; + } + + upsdebugx (3, "Command test.battery.start Ok: read byte: %d", recv); + return STAT_INSTCMD_HANDLED; + } + + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); + return STAT_INSTCMD_UNKNOWN; +} + +static int start_ups_comm() +{ + uint16_t length; + int recv; + + upsdebugx (2, "entering start_ups_comm()\n"); + + cypress_setfeatures(); + + length = riello_prepare_gi(&bufOut[0]); + + recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_GI); + + if (recv < 0) { + upsdebugx (3, "Get identif err: read byte: %d", recv); + return recv; + } + + if (!wait_packet && foundbadcrc) { + upsdebugx (3, "Get identif Ko: bad CRC or Checksum"); + return 1; + } + + if (!wait_packet && foundnak) { + upsdebugx (3, "Get identif Ko: command not supported"); + return 1; + } + + upsdebugx (3, "Get identif Ok: read byte: %u", recv); + + return 0; +} + +void upsdrv_help(void) +{ + +} + + +void upsdrv_makevartable(void) +{ + +} + +void upsdrv_initups(void) +{ + const struct { + const char *name; + int (*command)(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t buflen); + } subdriver[] = { + { "cypress", &cypress_command }, + { NULL, NULL } + }; + + int ret; + char *regex_array[7]; + + char *subdrv = getval("subdriver"); + + warn_if_bad_usb_port_filename(device_path); + + regex_array[0] = getval("vendorid"); + regex_array[1] = getval("productid"); + regex_array[2] = getval("vendor"); + regex_array[3] = getval("product"); + regex_array[4] = getval("serial"); + regex_array[5] = getval("bus"); + regex_array[6] = getval("device"); + + /* pick up the subdriver name if set explicitly */ + if (subdrv) { + int i; + + if (!regex_array[0] || !regex_array[1]) { + fatalx(EXIT_FAILURE, "When specifying a subdriver, 'vendorid' and 'productid' are mandatory."); + } + + for (i = 0; subdriver[i].name; i++) { + + if (strcasecmp(subdrv, subdriver[i].name)) { + continue; + } + + subdriver_command = subdriver[i].command; + break; + } + + if (!subdriver_command) { + fatalx(EXIT_FAILURE, "Subdriver \"%s\" not found!", subdrv); + } + } + + ret = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); + + switch (ret) + { + case -1: + fatal_with_errno(EXIT_FAILURE, "USBNewRegexMatcher"); + case 0: + break; /* all is well */ + default: + fatalx(EXIT_FAILURE, "invalid regular expression: %s", regex_array[ret]); + } + + /* link the matchers */ + regex_matcher->next = &device_matcher; + + ret = usb->open(&udev, &usbdevice, regex_matcher, &driver_callback); + if (ret < 0) { + fatalx(EXIT_FAILURE, + "No supported devices found. Please check your device availability with 'lsusb'\n" + "and make sure you have an up-to-date version of NUT. If this does not help,\n" + "try running the driver with at least 'subdriver', 'vendorid' and 'productid'\n" + "options specified. Please refer to the man page for details about these options\n" + "(man 8 riello_usb).\n"); + } + + if (!subdriver_command) { + fatalx(EXIT_FAILURE, "No subdriver selected"); + } + + /* create a new matcher for later reopening */ + ret = USBNewExactMatcher(&reopen_matcher, &usbdevice); + if (ret) { + fatal_with_errno(EXIT_FAILURE, "USBNewExactMatcher"); + } + + /* link the matchers */ + reopen_matcher->next = regex_matcher; + + dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID); + dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID); +} + +void upsdrv_initinfo(void) +{ + int ret; + + ret = start_ups_comm(); + + if (ret < 0) + fatalx(EXIT_FAILURE, "No communication with UPS"); + else if (ret > 0) + fatalx(EXIT_FAILURE, "Bad checksum or NACK"); + else + upsdebugx(2, "Communication with UPS established"); + + riello_parse_gi(&bufIn[0], &DevData); + + gpser_error_control = DevData.Identif_bytes[4]-0x30; + if ((DevData.Identif_bytes[0] == '1') || (DevData.Identif_bytes[0] == '2')) + input_monophase = 1; + else { + input_monophase = 0; + dstate_setinfo("input.phases", "%u", 3); + dstate_setinfo("input.phases", "%u", 3); + dstate_setinfo("input.bypass.phases", "%u", 3); + } + if ((DevData.Identif_bytes[0] == '1') || (DevData.Identif_bytes[0] == '3')) + output_monophase = 1; + else { + output_monophase = 0; + dstate_setinfo("output.phases", "%u", 3); + } + + dstate_setinfo("device.mfr", "RPS S.p.a."); + dstate_setinfo("device.model", "%s", (unsigned char*) DevData.ModelStr); + dstate_setinfo("device.serial", "%s", (unsigned char*) DevData.Identification); + dstate_setinfo("device.type", "ups"); + + dstate_setinfo("ups.mfr", "RPS S.p.a."); + dstate_setinfo("ups.model", "%s", (unsigned char*) DevData.ModelStr); + dstate_setinfo("ups.serial", "%s", (unsigned char*) DevData.Identification); + dstate_setinfo("ups.firmware", "%s", (unsigned char*) DevData.Version); + + if (get_ups_nominal() == 0) { + dstate_setinfo("ups.realpower.nominal", "%u", DevData.NomPowerKW); + dstate_setinfo("ups.power.nominal", "%u", DevData.NomPowerKVA); + dstate_setinfo("output.voltage.nominal", "%u", DevData.NominalUout); + dstate_setinfo("output.frequency.nominal", "%.1f", DevData.NomFout/10.0); + dstate_setinfo("battery.voltage.nominal", "%u", DevData.NomUbat); + dstate_setinfo("battery.capacity", "%u", DevData.NomBatCap); + } + + /* commands ----------------------------------------------- */ + dstate_addcmd("load.off"); + dstate_addcmd("load.on"); + dstate_addcmd("load.off.delay"); + dstate_addcmd("load.on.delay"); + dstate_addcmd("shutdown.return"); + dstate_addcmd("shutdown.stop"); + dstate_addcmd("test.battery.start"); + dstate_addcmd("test.panel.start"); + + dstate_setinfo("ups.delay.shutdown", "%u", offdelay); + dstate_setflags("ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("ups.delay.shutdown", 3); + dstate_setinfo("ups.delay.reboot", "%u", bootdelay); + dstate_setflags("ups.delay.reboot", ST_FLAG_RW | ST_FLAG_STRING); + dstate_setaux("ups.delay.reboot", 3); + + /* install handlers */ +/* upsh.setvar = hid_set_value; setvar; */ + + /* note: for a transition period, these data are redundant! */ + /* dstate_setinfo("device.mfr", "skel manufacturer"); */ + /* dstate_setinfo("device.model", "longrun 15000"); */ + + upsh.instcmd = riello_instcmd; +} + +void upsdrv_shutdown(void) + __attribute__((noreturn)); + +void upsdrv_shutdown(void) +{ + /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ + int retry; + + /* maybe try to detect the UPS here, but try a shutdown even if + it doesn't respond at first if possible */ + + /* replace with a proper shutdown function */ + + + /* you may have to check the line status since the commands + for toggling power are frequently different for OL vs. OB */ + + /* OL: this must power cycle the load if possible */ + + /* OB: the load must remain off until the power returns */ + + for (retry = 1; retry <= MAXTRIES; retry++) { + + if (riello_instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) { + continue; + } + + if (riello_instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) { + continue; + } + + fatalx(EXIT_SUCCESS, "Shutting down"); + } + + fatalx(EXIT_FAILURE, "Shutdown failed!"); +} + +void upsdrv_updateinfo(void) +{ + uint8_t getextendedOK; + static int countlost = 0; + int stat; + + upsdebugx(1, "countlost %d",countlost); + + if (countlost > 0){ + upsdebugx(1, "Communication with UPS is lost: status read failed!"); + + if (countlost == COUNTLOST) { + dstate_datastale(); + upslogx(LOG_WARNING, "Communication with UPS is lost: status read failed!"); + } + } + + stat = get_ups_status(); + + upsdebugx(1, "get_ups_status() %d",stat ); + + if (stat < 0) { + if (countlost < COUNTLOST) + countlost++; + return; + } + + if (get_ups_extended() == 0) + getextendedOK = 1; + else + getextendedOK = 0; + + if (countlost == COUNTLOST) + upslogx(LOG_NOTICE, "Communication with UPS is re-established!"); + + dstate_setinfo("input.frequency", "%.2f", DevData.Finp/10.0); + dstate_setinfo("input.bypass.frequency", "%.2f", DevData.Fbypass/10.0); + dstate_setinfo("output.frequency", "%.2f", DevData.Fout/10.0); + dstate_setinfo("battery.voltage", "%.1f", DevData.Ubat/10.0); + if ((DevData.BatCap < 0xFFFF) && (DevData.BatTime < 0xFFFF)) { + dstate_setinfo("battery.charge", "%u", DevData.BatCap); + dstate_setinfo("battery.runtime", "%u", DevData.BatTime*60); + } + + if (DevData.Tsystem < 0xFF) + dstate_setinfo("ups.temperature", "%u", DevData.Tsystem); + + + if (input_monophase) { + dstate_setinfo("input.voltage", "%u", DevData.Uinp1); + dstate_setinfo("input.bypass.voltage", "%u", DevData.Ubypass1); + } + else { + dstate_setinfo("input.L1-N.voltage", "%u", DevData.Uinp1); + dstate_setinfo("input.L2-N.voltage", "%u", DevData.Uinp2); + dstate_setinfo("input.L3-N.voltage", "%u", DevData.Uinp3); + dstate_setinfo("input.bypass.L1-N.voltage", "%u", DevData.Ubypass1); + dstate_setinfo("input.bypass.L2-N.voltage", "%u", DevData.Ubypass2); + dstate_setinfo("input.bypass.L3-N.voltage", "%u", DevData.Ubypass3); + } + + if (output_monophase) { + dstate_setinfo("output.voltage", "%u", DevData.Uout1); + dstate_setinfo("output.power.percent", "%u", DevData.Pout1); + dstate_setinfo("ups.load", "%u", DevData.Pout1); + } + else { + dstate_setinfo("output.L1-N.voltage", "%u", DevData.Uout1); + dstate_setinfo("output.L2-N.voltage", "%u", DevData.Uout2); + dstate_setinfo("output.L3-N.voltage", "%u", DevData.Uout3); + dstate_setinfo("output.L1.power.percent", "%u", DevData.Pout1); + dstate_setinfo("output.L2.power.percent", "%u", DevData.Pout2); + dstate_setinfo("output.L3.power.percent", "%u", DevData.Pout3); + dstate_setinfo("ups.load", "%u", (DevData.Pout1+DevData.Pout2+DevData.Pout3)/3); + } + + status_init(); + + /* AC Fail */ + if (riello_test_bit(&DevData.StatusCode[0], 1)) + status_set("OB"); + else + status_set("OL"); + + /* LowBatt */ + if ((riello_test_bit(&DevData.StatusCode[0], 1)) && + (riello_test_bit(&DevData.StatusCode[0], 0))) + status_set("LB"); + + /* Standby */ + if (!riello_test_bit(&DevData.StatusCode[0], 3)) + status_set("OFF"); + + /* On Bypass */ + if (riello_test_bit(&DevData.StatusCode[1], 3)) + status_set("BYPASS"); + + /* Overload */ + if (riello_test_bit(&DevData.StatusCode[4], 2)) + status_set("OVER"); + + /* Buck */ + if (riello_test_bit(&DevData.StatusCode[1], 0)) + status_set("TRIM"); + + /* Boost */ + if (riello_test_bit(&DevData.StatusCode[1], 1)) + status_set("BOOST"); + + /* Replace battery */ + if (riello_test_bit(&DevData.StatusCode[2], 0)) + status_set("RB"); + + /* Charging battery */ + if (riello_test_bit(&DevData.StatusCode[2], 2)) + status_set("CHRG"); + + status_commit(); + + dstate_dataok(); + + if (getextendedOK) { + dstate_setinfo("output.L1.power", "%u", DevData.Pout1VA); + dstate_setinfo("output.L2.power", "%u", DevData.Pout2VA); + dstate_setinfo("output.L3.power", "%u", DevData.Pout3VA); + dstate_setinfo("output.L1.realpower", "%u", DevData.Pout1W); + dstate_setinfo("output.L2.realpower", "%u", DevData.Pout2W); + dstate_setinfo("output.L3.realpower", "%u", DevData.Pout3W); + dstate_setinfo("output.L1.current", "%u", DevData.Iout1); + dstate_setinfo("output.L2.current", "%u", DevData.Iout2); + dstate_setinfo("output.L3.current", "%u", DevData.Iout3); + } + + poll_interval = 2; + + countlost = 0; + +/* if (get_ups_statuscode() != 0) + upsdebugx(2, "Communication is lost"); + else { + }*/ + + /* + * ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS); + * + * if (ret < STATUS_LEN) { + * upslogx(LOG_ERR, "Short read from UPS"); + * dstate_datastale(); + * return; + * } + */ + + /* dstate_setinfo("var.name", ""); */ + + /* if (ioctl(upsfd, TIOCMGET, &flags)) { + * upslog_with_errno(LOG_ERR, "TIOCMGET"); + * dstate_datastale(); + * return; + * } + */ + + /* status_init(); + * + * if (ol) + * status_set("OL"); + * else + * status_set("OB"); + * ... + * + * status_commit(); + * + * dstate_dataok(); + */ + + /* + * poll_interval = 2; + */ +} + +void upsdrv_cleanup(void) +{ + usb->close(udev); + USBFreeExactMatcher(reopen_matcher); + USBFreeRegexMatcher(regex_matcher); + free(usbdevice.Vendor); + free(usbdevice.Product); + free(usbdevice.Serial); + free(usbdevice.Bus); + free(usbdevice.Device); +} diff --git a/drivers/safenet.c b/drivers/safenet.c index 392fa45..2715db8 100644 --- a/drivers/safenet.c +++ b/drivers/safenet.c @@ -41,7 +41,7 @@ #include "safenet.h" #define DRIVER_NAME "Generic SafeNet UPS driver" -#define DRIVER_VERSION "1.6" +#define DRIVER_VERSION "1.7" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -60,13 +60,14 @@ static union { struct safenet status; } ups; -static int ondelay = 1; /* minutes */ -static int offdelay = 30; /* seconds */ +static long ondelay = 1; /* minutes */ +static long offdelay = 30; /* seconds */ static int safenet_command(const char *command) { char reply[32]; - int i, ret; + size_t i; + ssize_t ret; /* * Get rid of whatever is in the in- and output buffers. @@ -260,7 +261,7 @@ static int instcmd(const char *cmdname, const char *extra) */ if (!strcasecmp(cmdname, "shutdown.return")) { char command[] = SHUTDOWN_RETURN; - + command[4] += ((offdelay % 1000) / 100); command[5] += ((offdelay % 100) / 10); command[6] += (offdelay % 10); @@ -288,7 +289,7 @@ static int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -297,8 +298,6 @@ void upsdrv_initinfo(void) int retry = 3; char *v; - dstate_setinfo("driver.version.internal", "%s", DRIVER_VERSION); - usleep(100000); /* @@ -330,8 +329,8 @@ void upsdrv_initinfo(void) dstate_setinfo("ups.model", "%s", ((v = getval("modelname")) != NULL) ? v : "unknown"); dstate_setinfo("ups.serial", "%s", ((v = getval("serialnumber")) != NULL) ? v : "unknown"); - dstate_setinfo("ups.delay.start", "%d", 60 * ondelay); - dstate_setinfo("ups.delay.shutdown", "%d", offdelay); + dstate_setinfo("ups.delay.start", "%ld", 60 * ondelay); + dstate_setinfo("ups.delay.shutdown", "%ld", offdelay); /* * These are the instant commands we support. @@ -510,7 +509,7 @@ void upsdrv_initups(void) } if ((ondelay < 0) || (ondelay > 9999)) { - fatalx(EXIT_FAILURE, "Start delay '%d' out of range [0..9999]", ondelay); + fatalx(EXIT_FAILURE, "Start delay '%ld' out of range [0..9999]", ondelay); } val = getval("offdelay"); @@ -519,7 +518,7 @@ void upsdrv_initups(void) } if ((offdelay < 0) || (offdelay > 999)) { - fatalx(EXIT_FAILURE, "Shutdown delay '%d' out of range [0..999]", offdelay); + fatalx(EXIT_FAILURE, "Shutdown delay '%ld' out of range [0..999]", offdelay); } } diff --git a/drivers/safenet.h b/drivers/safenet.h index 12f2040..25d4976 100644 --- a/drivers/safenet.h +++ b/drivers/safenet.h @@ -18,6 +18,9 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef NUT_SAFENET_H_SEEN +#define NUT_SAFENET_H_SEEN 1 + /* * The following commands where traced on the serial port. From these, the * COM_POLL_STAT command is just an example of how this command looks. @@ -58,3 +61,5 @@ struct safenet { char systemtest; char dunno_10; }; + +#endif /* NUT_SAFENET_H_SEEN */ diff --git a/drivers/salicru-hid.c b/drivers/salicru-hid.c new file mode 100644 index 0000000..b49b099 --- /dev/null +++ b/drivers/salicru-hid.c @@ -0,0 +1,276 @@ +/* salicru-hid.c - subdriver to monitor Salicru USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2012 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * 2013 Charles Lepple + * 2021 Francois Lacroix + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-usbhid-subdriver script. It must be customized. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" /* must be first */ + +#include "usbhid-ups.h" +#include "salicru-hid.h" +#include "main.h" /* for getval() */ +#include "usb-common.h" + +#define SALICRU_HID_VERSION "Salicru HID 0.3" +/* FIXME: experimental flag to be put in upsdrv_info */ + +/* Salicru */ +#define SALICRU_VENDORID 0x2e66 + +/* USB IDs device table */ +static usb_device_id_t salicru_usb_device_table[] = { + /* TWINPRO3/TWINRT3 (SLC-1500-TWIN PRO3) per https://github.com/networkupstools/nut/issues/1142 */ + /* SLC TWIN PRO2<=3KVA per https://github.com/networkupstools/nut/issues/450 */ + { USB_DEVICE(SALICRU_VENDORID, 0x0201), NULL }, + { USB_DEVICE(SALICRU_VENDORID, 0x0202), NULL }, + { USB_DEVICE(SALICRU_VENDORID, 0x0203), NULL }, + + /* Salicru SPS 850 HOME per https://github.com/networkupstools/nut/pull/1199 */ + /* https://www.salicru.com/sps-home.html */ + { USB_DEVICE(SALICRU_VENDORID, 0x0300), NULL }, + + /* Terminating entry */ + { 0, 0, NULL } +}; + + +/* --------------------------------------------------------------- */ +/* Vendor-specific usage table */ +/* --------------------------------------------------------------- */ + +/* SALICRU usage table */ +static usage_lkp_t salicru_usage_lkp[] = { + { NULL, 0 } +}; + +static usage_tables_t salicru_utab[] = { + salicru_usage_lkp, + hid_usage_lkp, + NULL, +}; + +/* --------------------------------------------------------------- */ +/* HID2NUT lookup table */ +/* --------------------------------------------------------------- */ + +static hid_info_t salicru_hid2nut[] = { + +#ifdef DEBUG + { "experimental.ups.powersummary.imanufacturer", 0, 0, "UPS.PowerSummary.iManufacturer", NULL, "%.0f", 0, NULL }, + { "experimental.ups.powersummary.iproduct", 0, 0, "UPS.PowerSummary.CapacityMode", NULL, "%.0f", 0, NULL }, + { "experimental.ups.powersummary.iserialnumber", 0, 0, "UPS.PowerSummary.iSerialNumber", NULL, "%.0f", 0, NULL }, + { "experimental.ups.powersummary.capacitymode", 0, 0, "UPS.PowerSummary.CapacityMode", NULL, "%.0f", 0, NULL }, + { "experimental.ups.powersummary.rechargeable", 0, 0, "UPS.PowerSummary.Rechargeable", NULL, "%.0f", 0, NULL }, + { "experimental.ups.powersummary.designcapacity", 0, 0, "UPS.PowerSummary.DesignCapacity", NULL, "%.0f", 0, NULL }, + { "experimental.ups.powersummary.fullchargecapacity", 0, 0, "UPS.PowerSummary.FullChargeCapacity", NULL, "%.0f", 0, NULL }, +#endif /* DEBUG */ + +/* A few more unknown fields + 0.043266 [D1] Path: UPS.ff010004.ff010024.ff0100d0, Type: Feature, ReportID: 0x19, Offset: 0, Size: 8, Value: 0.1 + 0.043766 [D1] Path: UPS.ff010004.ff010024.ff0100d1, Type: Feature, ReportID: 0x1a, Offset: 0, Size: 8, Value: 0 + 0.044308 [D1] Path: UPS.ff01001d.ff010019.ff010020, Type: Feature, ReportID: 0x25, Offset: 0, Size: 1, Value: 0 + 0.044762 [D1] Path: UPS.ff01001d.ff010019.ff010021, Type: Feature, ReportID: 0x2c, Offset: 0, Size: 1, Value: 0 + 0.044891 [D1] Path: UPS.ff01001d.ff010019.ff010021, Type: Input, ReportID: 0x2c, Offset: 0, Size: 1, Value: 0 + 0.045386 [D1] Path: UPS.ff01001d.ff01001a.ff010001, Type: Feature, ReportID: 0x26, Offset: 0, Size: 1, Value: 0 + 0.045888 [D1] Path: UPS.ff01001d.ff01001a.ff010002, Type: Feature, ReportID: 0x27, Offset: 0, Size: 8, Value: 1 + 0.047553 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.047659 [D1] Can't retrieve Report 28: Value too large for defined data type + 0.047796 [D1] Path: UPS.ff01001d.ff01001b.ff010040, Type: Feature, ReportID: 0x28, Offset: 0, Size: 8 + 0.048272 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.048403 [D1] Can't retrieve Report 28: Value too large for defined data type + 0.048537 [D1] Path: UPS.ff01001d.ff01001b.ff010016, Type: Input, ReportID: 0x28, Offset: 0, Size: 8 + 0.049017 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.049147 [D1] Can't retrieve Report 28: Value too large for defined data type + 0.049280 [D1] Path: UPS.ff01001d.ff01001b.ff010018, Type: Feature, ReportID: 0x28, Offset: 8, Size: 8 + 0.050944 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.051124 [D1] Can't retrieve Report 28: Value too large for defined data type + 0.051264 [D1] Path: UPS.ff01001d.ff01001b.ff010018, Type: Input, ReportID: 0x28, Offset: 8, Size: 8 + 0.052965 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.053137 [D1] Can't retrieve Report 29: Value too large for defined data type + 0.053277 [D1] Path: UPS.ff01001d.ff01001b.ff010015, Type: Feature, ReportID: 0x29, Offset: 0, Size: 8 + 0.053804 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.053954 [D1] Can't retrieve Report 29: Value too large for defined data type + 0.054091 [D1] Path: UPS.ff01001d.ff01001b.ff010015, Type: Output, ReportID: 0x29, Offset: 0, Size: 8 + 0.054530 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.054664 [D1] Can't retrieve Report 29: Value too large for defined data type + 0.054799 [D1] Path: UPS.ff01001d.ff01001b.ff010017, Type: Feature, ReportID: 0x29, Offset: 8, Size: 8 + 0.055285 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.055421 [D1] Can't retrieve Report 29: Value too large for defined data type + 0.055557 [D1] Path: UPS.ff01001d.ff01001b.ff010017, Type: Output, ReportID: 0x29, Offset: 8, Size: 8 + 0.056130 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.056273 [D1] Can't retrieve Report 2d: Value too large for defined data type + 0.056409 [D1] Path: UPS.ff01001d.ff01001b.ff010010, Type: Feature, ReportID: 0x2d, Offset: 0, Size: 1 + 0.056927 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.057074 [D1] Can't retrieve Report 2d: Value too large for defined data type + 0.057211 [D1] Path: UPS.ff01001d.ff01001b.ff01001e, Type: Feature, ReportID: 0x2d, Offset: 1, Size: 1 + 0.057676 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.057825 [D1] Can't retrieve Report 2d: Value too large for defined data type + 0.057962 [D1] Path: UPS.ff01001d.ff01001b.ff01001f, Type: Feature, ReportID: 0x2d, Offset: 2, Size: 1 + 0.058416 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.058551 [D1] Can't retrieve Report 2d: Value too large for defined data type + 0.058685 [D1] Path: UPS.ff01001d.ff01001b.ff010010, Type: Input, ReportID: 0x2d, Offset: 0, Size: 1 + 0.059156 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.059291 [D1] Can't retrieve Report 2d: Value too large for defined data type + 0.059426 [D1] Path: UPS.ff01001d.ff01001b.ff01001e, Type: Input, ReportID: 0x2d, Offset: 1, Size: 1 + 0.059957 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.060112 [D1] Can't retrieve Report 2d: Value too large for defined data type + 0.060248 [D1] Path: UPS.ff01001d.ff01001b.ff01001f, Type: Input, ReportID: 0x2d, Offset: 2, Size: 1 + 0.061944 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.062117 [D1] Can't retrieve Report 2a: Value too large for defined data type + 0.062256 [D1] Path: UPS.ff01001d.ff01001b.ff010013, Type: Feature, ReportID: 0x2a, Offset: 0, Size: 1 + 0.063941 [D2] libusb_get_report: error sending control message: Value too large for defined data type + 0.064104 [D1] Can't retrieve Report 2b: Value too large for defined data type + 0.064242 [D1] Path: UPS.ff01001d.ff01001b.ff010014, Type: Feature, ReportID: 0x2b, Offset: 0, Size: 1 +*/ + + /* Battery page */ + { "battery.type", 0, 0, "UPS.PowerSummary.iDeviceChemistry", NULL, "%s", 0, stringid_conversion }, + { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, + { "battery.charge.warning", 0, 0, "UPS.PowerSummary.WarningCapacityLimit", NULL, "%.0f", 0, NULL }, + { "battery.charge.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.RemainingCapacityLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "battery.runtime", 0, 0, "UPS.PowerSummary.RunTimeToEmpty", NULL, "%.0f", 0, NULL }, + { "battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.PowerSummary.RemainingTimeLimit", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "battery.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.0f", 0, NULL }, + { "battery.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.2f", 0, NULL }, + + /* UPS page */ + { "ups.load", 0, 0, "UPS.Output.PercentLoad", NULL, "%.0f", 0, NULL }, + { "ups.beeper.status", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "%s", 0, beeper_info }, + { "ups.test.result", 0, 0, "UPS.Output.Test", NULL, "%s", 0, test_read_info }, + { "ups.realpower.nominal", 0, 0, "UPS.Output.ConfigActivePower", NULL, "%.0f", 0, NULL }, + + /* Boolean value */ + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.ACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Charging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Discharging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.BelowRemainingCapacityLimit", NULL, NULL, HU_FLAG_QUICK_POLL, lowbatt_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.FullyCharged", NULL, NULL, 0, fullycharged_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.RemainingTimeLimitExpired", NULL, NULL, 0, timelimitexpired_info }, + { "BOOL", 0, 0, "UPS.Output.CommunicationLost", NULL, NULL, 0, commfault_info }, + { "BOOL", 0, 0, "UPS.Output.Boost", NULL, NULL, 0, boost_info }, + { "BOOL", 0, 0, "UPS.Output.Overload", NULL, NULL, 0, overload_info }, + + /* Input page */ + { "input.frequency", 0, 0, "UPS.Input.Frequency", NULL, "%.1f", 0, NULL }, + { "input.voltage.nominal", 0, 0, "UPS.Input.ConfigVoltage", NULL, "%.0f", 0, NULL }, + { "input.voltage", 0, 0, "UPS.Input.Voltage", NULL, "%.1f", 0, NULL }, + /* read-only on this model but probably R/W in other models */ +/* + { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Input.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, + { "input.transfer.high", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.Input.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_SEMI_STATIC, NULL }, +*/ + { "input.transfer.high", 0, 0, "UPS.PowerConverter.Output.HighVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + { "input.transfer.low", 0, 0, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + + /* Output page */ + { "output.frequency", 0, 0, "UPS.Output.Frequency", NULL, "%.1f", 0, NULL }, + { "output.voltage", 0, 0, "UPS.Output.Voltage", NULL, "%.1f", 0, NULL }, + { "output.voltage.nominal", 0, 0, "UPS.Output.ConfigVoltage", NULL, "%.0f", 0, NULL }, + + /* instant commands. */ +/* Need testing + { "test.battery.start.quick", 0, 0, "UPS.Output.Test", NULL, "1", HU_TYPE_CMD, NULL }, + { "test.battery.start.deep", 0, 0, "UPS.Output.Test", NULL, "2", HU_TYPE_CMD, NULL }, + { "test.battery.stop", 0, 0, "UPS.Output.Test", NULL, "3", HU_TYPE_CMD, NULL }, + { "load.off.delay", 0, 0, "UPS.Output.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, + { "load.on.delay", 0, 0, "UPS.Output.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, + { "shutdown.stop", 0, 0, "UPS.Output.DelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, + { "shutdown.reboot", 0, 0, "UPS.Output.DelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL }, + { "beeper.on", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, + { "beeper.off", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, + { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, + { "beeper.disable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "1", HU_TYPE_CMD, NULL }, + { "beeper.mute", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, +*/ + + /* Salicru Twin Pro 2 Descriptors: Sensors */ + { "ups.load", 0, 0, "UPS.PowerSummary.PercentLoad", NULL, "%.0f", 0, NULL }, + { "ups.test.result", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "%s", 0, test_read_info }, + { "ups.realpower.nominal", 0, 0, "UPS.Flow.[4].ConfigActivePower", NULL, "%.0f", 0, NULL }, + { "ups.realpower", 0, 0, "UPS.PowerConverter.Output.ActivePower", NULL, "%.0f", 0, NULL }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.Overload", NULL, NULL, 0, overload_info }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.OverTemperature", NULL, NULL, 0, overheat_info }, + { "input.frequency", 0, 0, "UPS.PowerConverter.Input.[1].Frequency", NULL, "%.1f", 0, NULL }, + { "input.voltage", 0, 0, "UPS.PowerConverter.Input.[1].Voltage", NULL, "%.1f", 0, NULL }, + { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", NULL, "%.1f", 0, NULL }, + { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.1f", 0, NULL }, + { "output.voltage.nominal", 0, 0, "UPS.PowerSummary.ConfigVoltage", NULL, "%.0f", 0, NULL }, + { "ups.test.result", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "%s", 0, test_read_info }, + + /* Salicru Twin Pro 2 Descriptors: Instant commands */ + { "test.battery.start.quick", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "1", HU_TYPE_CMD, NULL }, + { "test.battery.start.deep", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "2", HU_TYPE_CMD, NULL }, + { "test.battery.stop", 0, 0, "UPS.BatterySystem.Battery.Test", NULL, "3", HU_TYPE_CMD, NULL }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } +}; + +static const char *salicru_format_model(HIDDevice_t *hd) { + return hd->Product; +} + +static const char *salicru_format_mfr(HIDDevice_t *hd) { + return hd->Vendor ? hd->Vendor : "Salicru"; +} + +static const char *salicru_format_serial(HIDDevice_t *hd) { + return hd->Serial; +} + +/* this function allows the subdriver to "claim" a device: return 1 if + * the device is supported by this subdriver, else 0. */ +static int salicru_claim(HIDDevice_t *hd) +{ + int status = is_usb_device_supported(salicru_usb_device_table, hd); + + switch (status) + { + case POSSIBLY_SUPPORTED: + /* by default, reject, unless the productid option is given */ + if (getval("productid")) { + return 1; + } + possibly_supported("Salicru", hd); + return 0; + + case SUPPORTED: + return 1; + + case NOT_SUPPORTED: + default: + return 0; + } +} + +subdriver_t salicru_subdriver = { + SALICRU_HID_VERSION, + salicru_claim, + salicru_utab, + salicru_hid2nut, + salicru_format_model, + salicru_format_mfr, + salicru_format_serial, + fix_report_desc, +}; diff --git a/drivers/salicru-hid.h b/drivers/salicru-hid.h new file mode 100644 index 0000000..e0e6b9c --- /dev/null +++ b/drivers/salicru-hid.h @@ -0,0 +1,30 @@ +/* salicru-hid.h - subdriver to monitor Salicru USB/HID devices with NUT + * + * Copyright (C) + * 2003 - 2009 Arnaud Quette + * 2005 - 2006 Peter Selinger + * 2008 - 2009 Arjen de Korte + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef SALICRU_HID_H +#define SALICRU_HID_H + +#include "usbhid-ups.h" + +extern subdriver_t salicru_subdriver; + +#endif /* SALICRU_HID_H */ diff --git a/drivers/serial.c b/drivers/serial.c index d709e5d..60af06f 100644 --- a/drivers/serial.c +++ b/drivers/serial.c @@ -21,6 +21,7 @@ #include "timehead.h" #include "serial.h" #include "main.h" +#include "attribute.h" #include #include @@ -34,8 +35,13 @@ #include #endif +#include "nut_stdint.h" + static unsigned int comm_failures = 0; +static void ser_open_error(const char *port) + __attribute__((noreturn)); + static void ser_open_error(const char *port) { struct stat fs; @@ -57,7 +63,7 @@ static void ser_open_error(const char *port) user = getpwuid(getuid()); if (user) - printf(" Current user id: %s (%d)\n", + printf(" Current user id: %s (%d)\n", user->pw_name, (int) user->pw_uid); user = getpwuid(fs.st_uid); @@ -97,7 +103,7 @@ static void lock_set(int fd, const char *port) ret = uu_lock(xbasename(port)); if (ret != 0) - fatalx(EXIT_FAILURE, "Can't uu_lock %s: %s", xbasename(port), + fatalx(EXIT_FAILURE, "Can't uu_lock %s: %s", xbasename(port), uu_lockerr(ret)); #elif defined(HAVE_FLOCK) @@ -123,26 +129,42 @@ static void lock_set(int fd, const char *port) #endif } -int ser_open(const char *port) +/* Non fatal version of ser_open */ +int ser_open_nf(const char *port) { int fd; fd = open(port, O_RDWR | O_NOCTTY | O_EXCL | O_NONBLOCK); - if (fd < 0) - ser_open_error(port); + if (fd < 0) { + return -1; + } lock_set(fd, port); return fd; } -int ser_set_speed(int fd, const char *port, speed_t speed) +int ser_open(const char *port) +{ + int res; + + res = ser_open_nf(port); + if(res == -1) { + ser_open_error(port); + } + + return res; +} + +int ser_set_speed_nf(int fd, const char *port, speed_t speed) { struct termios tio; + NUT_UNUSED_VARIABLE(port); - if (tcgetattr(fd, &tio) != 0) - fatal_with_errno(EXIT_FAILURE, "tcgetattr(%s)", port); + if (tcgetattr(fd, &tio) != 0) { + return -1; + } tio.c_cflag = CS8 | CLOCAL | CREAD; tio.c_iflag = IGNPAR; @@ -164,6 +186,18 @@ int ser_set_speed(int fd, const char *port, speed_t speed) return 0; } +int ser_set_speed(int fd, const char *port, speed_t speed) +{ + int res; + + res = ser_set_speed_nf(fd,port,speed); + if(res == -1) { + fatal_with_errno(EXIT_FAILURE, "tcgetattr(%s)", port); + } + + return 0; +} + static int ser_set_control(int fd, int line, int state) { if (state) { @@ -228,17 +262,29 @@ int ser_close(int fd, const char *port) return 0; } -int ser_send_char(int fd, unsigned char ch) +ssize_t ser_send_char(int fd, unsigned char ch) { return ser_send_buf_pace(fd, 0, &ch, 1); } -static int send_formatted(int fd, const char *fmt, va_list va, unsigned long d_usec) +static ssize_t send_formatted(int fd, const char *fmt, va_list va, useconds_t d_usec) { int ret; char buf[LARGEBUF]; +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif ret = vsnprintf(buf, sizeof(buf), fmt, va); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif if (ret >= (int)sizeof(buf)) { upslogx(LOG_WARNING, "vsnprintf needed more than %d bytes", (int)sizeof(buf)); @@ -248,14 +294,26 @@ static int send_formatted(int fd, const char *fmt, va_list va, unsigned long d_u } /* send the results of the format string with d_usec delay after each char */ -int ser_send_pace(int fd, unsigned long d_usec, const char *fmt, ...) +ssize_t ser_send_pace(int fd, useconds_t d_usec, const char *fmt, ...) { - int ret; + ssize_t ret; va_list ap; va_start(ap, fmt); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif ret = send_formatted(fd, fmt, ap, d_usec); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif va_end(ap); @@ -263,14 +321,26 @@ int ser_send_pace(int fd, unsigned long d_usec, const char *fmt, ...) } /* send the results of the format string with no delay */ -int ser_send(int fd, const char *fmt, ...) +ssize_t ser_send(int fd, const char *fmt, ...) { - int ret; + ssize_t ret; va_list ap; va_start(ap, fmt); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif ret = send_formatted(fd, fmt, ap, 0); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif va_end(ap); @@ -278,22 +348,23 @@ int ser_send(int fd, const char *fmt, ...) } /* send buflen bytes from buf with no delay */ -int ser_send_buf(int fd, const void *buf, size_t buflen) +ssize_t ser_send_buf(int fd, const void *buf, size_t buflen) { return ser_send_buf_pace(fd, 0, buf, buflen); } /* send buflen bytes from buf with d_usec delay after each char */ -int ser_send_buf_pace(int fd, unsigned long d_usec, const void *buf, +ssize_t ser_send_buf_pace(int fd, useconds_t d_usec, const void *buf, size_t buflen) { - int ret; - size_t sent; + ssize_t ret = 0; + ssize_t sent; const char *data = buf; - for (sent = 0; sent < buflen; sent += ret) { - - ret = write(fd, &data[sent], (d_usec == 0) ? (buflen - sent) : 1); + assert(buflen < SSIZE_MAX); + for (sent = 0; sent < (ssize_t)buflen; sent += ret) { + /* Conditions above ensure that (buflen - sent) > 0 below */ + ret = write(fd, &data[sent], (d_usec == 0) ? (size_t)((ssize_t)buflen - sent) : 1); if (ret < 1) { return ret; @@ -305,30 +376,37 @@ int ser_send_buf_pace(int fd, unsigned long d_usec, const void *buf, return sent; } -int ser_get_char(int fd, void *ch, long d_sec, long d_usec) +ssize_t ser_get_char(int fd, void *ch, time_t d_sec, useconds_t d_usec) { - return select_read(fd, ch, 1, d_sec, d_usec); + /* Per standard below, we can cast here, because required ranges are + * effectively the same (and signed -1 for suseconds_t), and at most long: + * https://pubs.opengroup.org/onlinepubs/009604599/basedefs/sys/types.h.html + */ + return select_read(fd, ch, 1, d_sec, (suseconds_t)d_usec); } -int ser_get_buf(int fd, void *buf, size_t buflen, long d_sec, long d_usec) +ssize_t ser_get_buf(int fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec) { memset(buf, '\0', buflen); - return select_read(fd, buf, buflen, d_sec, d_usec); + return select_read(fd, buf, buflen, d_sec, (suseconds_t)d_usec); } /* keep reading until buflen bytes are received or a timeout occurs */ -int ser_get_buf_len(int fd, void *buf, size_t buflen, long d_sec, long d_usec) +ssize_t ser_get_buf_len(int fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec) { - int ret; - size_t recv; + ssize_t ret; + ssize_t recv; char *data = buf; + assert(buflen < SSIZE_MAX); memset(buf, '\0', buflen); - for (recv = 0; recv < buflen; recv += ret) { + for (recv = 0; recv < (ssize_t)buflen; recv += ret) { - ret = select_read(fd, &data[recv], buflen - recv, d_sec, d_usec); + ret = select_read(fd, &data[recv], + (size_t)((ssize_t)buflen - recv), + d_sec, (suseconds_t)d_usec); if (ret < 1) { return ret; @@ -340,21 +418,22 @@ int ser_get_buf_len(int fd, void *buf, size_t buflen, long d_sec, long d_usec) /* reads a line up to , discarding anything else that may follow, with callouts to the handler if anything matches the alertset */ -int ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, - const char *ignset, const char *alertset, void handler(char ch), - long d_sec, long d_usec) +ssize_t ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, + const char *ignset, const char *alertset, void handler(char ch), + time_t d_sec, useconds_t d_usec) { - int i, ret; + ssize_t i, ret; char tmp[64]; char *data = buf; - size_t count = 0, maxcount; + ssize_t count = 0, maxcount; + assert(buflen < SSIZE_MAX && buflen > 0); memset(buf, '\0', buflen); - maxcount = buflen - 1; /* for trailing \0 */ + maxcount = (ssize_t)buflen - 1; /* for trailing \0 */ while (count < maxcount) { - ret = select_read(fd, tmp, sizeof(tmp), d_sec, d_usec); + ret = select_read(fd, tmp, sizeof(tmp), d_sec, (suseconds_t)d_usec); if (ret < 1) { return ret; @@ -384,16 +463,16 @@ int ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, } /* as above, only with no alertset handling (just a wrapper) */ -int ser_get_line(int fd, void *buf, size_t buflen, char endchar, - const char *ignset, long d_sec, long d_usec) +ssize_t ser_get_line(int fd, void *buf, size_t buflen, char endchar, + const char *ignset, time_t d_sec, useconds_t d_usec) { return ser_get_line_alert(fd, buf, buflen, endchar, ignset, "", NULL, d_sec, d_usec); } -int ser_flush_in(int fd, const char *ignset, int verbose) +ssize_t ser_flush_in(int fd, const char *ignset, int verbose) { - int ret, extra = 0; + ssize_t ret, extra = 0; char ch; while ((ret = ser_get_char(fd, &ch, 0, 0)) > 0) { @@ -406,7 +485,7 @@ int ser_flush_in(int fd, const char *ignset, int verbose) if (verbose == 0) continue; - if (isprint(ch & 0xFF)) + if (isprint((unsigned char)ch & 0xFF)) upslogx(LOG_INFO, "ser_flush_in: read char %c", ch); else upslogx(LOG_INFO, "ser_flush_in: read char 0x%02x", ch); @@ -439,7 +518,19 @@ void ser_comm_fail(const char *fmt, ...) return; va_start(ap, fmt); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif ret = vsnprintf(why, sizeof(why), fmt, ap); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif va_end(ap); if ((ret < 1) || (ret >= (int) sizeof(why))) diff --git a/drivers/serial.h b/drivers/serial.h index 7edef0f..c2f5a2c 100644 --- a/drivers/serial.h +++ b/drivers/serial.h @@ -11,12 +11,18 @@ # include #endif /* HAVE_SYS_TERMIOS_H */ +#include /* for usleep() and useconds_t, latter also might be via */ +#include +#include /* for suseconds_t */ + /* limit the amount of spew that goes in the syslog when we lose the UPS */ #define SER_ERR_LIMIT 10 /* start limiting after 10 in a row */ #define SER_ERR_RATE 100 /* then only print every 100th error */ +int ser_open_nf(const char *port); int ser_open(const char *port); +int ser_set_speed_nf(int fd, const char *port, speed_t speed); int ser_set_speed(int fd, const char *port, speed_t speed); /* set the state of modem control lines */ @@ -32,41 +38,41 @@ int ser_flush_io(int fd); int ser_close(int fd, const char *port); -int ser_send_char(int fd, unsigned char ch); +ssize_t ser_send_char(int fd, unsigned char ch); /* send the results of the format string with d_usec delay after each char */ -int ser_send_pace(int fd, unsigned long d_usec, const char *fmt, ...) +ssize_t ser_send_pace(int fd, useconds_t d_usec, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4))); /* send the results of the format string with no delay */ -int ser_send(int fd, const char *fmt, ...) +ssize_t ser_send(int fd, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); /* send buflen bytes from buf with no delay */ -int ser_send_buf(int fd, const void *buf, size_t buflen); +ssize_t ser_send_buf(int fd, const void *buf, size_t buflen); /* send buflen bytes from buf with d_usec delay after each char */ -int ser_send_buf_pace(int fd, unsigned long d_usec, const void *buf, +ssize_t ser_send_buf_pace(int fd, useconds_t d_usec, const void *buf, size_t buflen); -int ser_get_char(int fd, void *ch, long d_sec, long d_usec); +ssize_t ser_get_char(int fd, void *ch, time_t d_sec, useconds_t d_usec); -int ser_get_buf(int fd, void *buf, size_t buflen, long d_sec, long d_usec); +ssize_t ser_get_buf(int fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec); /* keep reading until buflen bytes are received or a timeout occurs */ -int ser_get_buf_len(int fd, void *buf, size_t buflen, long d_sec, long d_usec); +ssize_t ser_get_buf_len(int fd, void *buf, size_t buflen, time_t d_sec, useconds_t d_usec); /* reads a line up to , discarding anything else that may follow, with callouts to the handler if anything matches the alertset */ -int ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, +ssize_t ser_get_line_alert(int fd, void *buf, size_t buflen, char endchar, const char *ignset, const char *alertset, void handler (char ch), - long d_sec, long d_usec); + time_t d_sec, useconds_t d_usec); /* as above, only with no alertset handling (just a wrapper) */ -int ser_get_line(int fd, void *buf, size_t buflen, char endchar, - const char *ignset, long d_sec, long d_usec); +ssize_t ser_get_line(int fd, void *buf, size_t buflen, char endchar, + const char *ignset, time_t d_sec, useconds_t d_usec); -int ser_flush_in(int fd, const char *ignset, int verbose); +ssize_t ser_flush_in(int fd, const char *ignset, int verbose); /* unified failure reporting: call these often */ void ser_comm_fail(const char *fmt, ...) diff --git a/drivers/skel.c b/drivers/skel.c index 4957bcd..f6d4cb3 100644 --- a/drivers/skel.c +++ b/drivers/skel.c @@ -1,18 +1,20 @@ /* anything commented is optional anything else is mandatory - + for more information, refer to: * docs/developers.txt * docs/new-drivers.txt * docs/new-names.txt - + and possibly also to: * docs/hid-subdrivers.txt for USB/HID devices * or docs/snmp-subdrivers.txt for SNMP devices */ +#include "config.h" #include "main.h" +#include "attribute.h" /* #include "serial.h" */ @@ -20,7 +22,7 @@ /* #define IGNCHARS "" */ #define DRIVER_NAME "Skeleton UPS driver" -#define DRIVER_VERSION "0.02" +#define DRIVER_VERSION "0.03" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -33,7 +35,8 @@ upsdrv_info_t upsdrv_info = { void upsdrv_initinfo(void) { - /* try to detect the UPS here - call fatal_with_errno(EXIT_FAILURE, ) if it fails */ + /* try to detect the UPS here - call fatal_with_errno(EXIT_FAILURE, ...) + * or fatalx(EXIT_FAILURE, ...) if it fails */ /* dstate_setinfo("ups.mfr", "skel manufacturer"); */ /* dstate_setinfo("ups.model", "longrun 15000"); */ @@ -54,7 +57,7 @@ void upsdrv_updateinfo(void) /* ser_send(upsfd, "foo%d", 1234); */ /* ser_send_buf(upsfd, bincmd, 12); */ - /* + /* * ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS); * * if (ret < STATUS_LEN) { @@ -91,6 +94,9 @@ void upsdrv_updateinfo(void) */ } +void upsdrv_shutdown(void) + __attribute__((noreturn)); + void upsdrv_shutdown(void) { /* tell the UPS to shut down, then return - DO NOT SLEEP HERE */ @@ -158,7 +164,7 @@ void upsdrv_initups(void) /* to get variables and flags from the command line, use this: * - * first populate with upsdrv_buildvartable above, then... + * first populate with upsdrv_makevartable() above, then... * * set flag foo : /bin/driver -x foo * set variable 'cable' to '1234' : /bin/driver -x cable=1234 diff --git a/drivers/snmp-ups-helpers.c b/drivers/snmp-ups-helpers.c new file mode 100644 index 0000000..b4116ff --- /dev/null +++ b/drivers/snmp-ups-helpers.c @@ -0,0 +1,106 @@ +/* snmp-ups-helpers.c - Shared helper functions and data mapping tables + * for NUT Generic SNMP driver core + * + * Copyright (C) + * 2015 - 2021 Eaton (author: Arnaud Quette ) + * 2016 - 2021 Eaton (author: Jim Klimov ) + * + * Sponsored by Eaton + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* NUT SNMP common functions */ +#include "common.h" /* includes "config.h" which must be the first header */ +/* +#include "config.h" +#include "main.h" +#include "nut_float.h" +#include "nut_stdint.h" +*/ +#include "snmp-ups.h" +#include "timehead.h" /* time.h => strptime() */ + +#include /* for isprint() */ + +/*********************************************************************** + * Subdrivers shared helpers functions + * Code below is primarily used in snmp-ups driver, but may be part + * of other compilation units, so separated into a stand-alone file + **********************************************************************/ + +static char su_scratch_buf[255]; + +/* Temperature handling, to convert back to Celsius */ +int temperature_unit = TEMPERATURE_UNKNOWN; + +/* Convert a US formated date (mm/dd/yyyy) to an ISO 8601 Calendar date (yyyy-mm-dd) */ +const char *su_usdate_to_isodate_info_fun(void *raw_date) +{ + const char *usdate = (char *)raw_date; + struct tm tm; + memset(&tm, 0, sizeof(struct tm)); + memset(&su_scratch_buf, 0, sizeof(su_scratch_buf)); + + upsdebugx(3, "%s: US date = %s", __func__, usdate); + + /* Try to convert from US date string to time */ + /* Note strptime returns NULL upon failure, and a ptr to the last + null char of the string upon success. Just try blindly the conversion! */ + strptime(usdate, "%m/%d/%Y", &tm); + if (strftime(su_scratch_buf, 254, "%F", &tm) != 0) { + upsdebugx(3, "%s: successfully reformated: %s", __func__, su_scratch_buf); + return su_scratch_buf; + } + + return NULL; +} + +info_lkp_t su_convert_to_iso_date_info[] = { + /* array index = FUNMAP_USDATE_TO_ISODATE: */ + { 1, "dummy", su_usdate_to_isodate_info_fun, NULL }, + { 0, NULL, NULL, NULL } +}; + +/* Process temperature value according to 'temperature_unit' */ +const char *su_temperature_read_fun(void *raw_snmp_value) +{ + const long snmp_value = *((long*)raw_snmp_value); + long celsius_value = snmp_value; + + memset(su_scratch_buf, 0, sizeof(su_scratch_buf)); + + switch (temperature_unit) { + case TEMPERATURE_KELVIN: + celsius_value = (snmp_value / 10) - 273.15; + snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", celsius_value); + break; + case TEMPERATURE_CELSIUS: + snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", (snmp_value / 10)); + break; + case TEMPERATURE_FAHRENHEIT: + celsius_value = (((snmp_value / 10) - 32) * 5) / 9; + snprintf(su_scratch_buf, sizeof(su_scratch_buf), "%.1ld", celsius_value); + break; + case TEMPERATURE_UNKNOWN: + default: + upsdebugx(1, "%s: not a known temperature unit for conversion!", __func__); + break; + } + upsdebugx(2, "%s: %.1ld => %s", __func__, (snmp_value / 10), su_scratch_buf); + return su_scratch_buf; +} diff --git a/drivers/snmp-ups.c b/drivers/snmp-ups.c index 8e17c06..fad0997 100644 --- a/drivers/snmp-ups.c +++ b/drivers/snmp-ups.c @@ -1,9 +1,11 @@ -/* snmp-ups.c - NUT Meta SNMP driver (support different MIBS) +/* snmp-ups.c - NUT Generic SNMP driver core (supports different MIBs) * - * Based on NetSNMP API (Simple Network Management Protocol V1-2) + * Based on NetSNMP API (Simple Network Management Protocol v1-2c-3) * * Copyright (C) - * 2002 - 2011 Arnaud Quette + * 2002 - 2014 Arnaud Quette + * 2015 - 2021 Eaton (author: Arnaud Quette ) + * 2016 - 2022 Eaton (author: Jim Klimov ) * 2002 - 2006 Dmitry Frolov * J.W. Hoogervorst * Niels Baggesen @@ -30,115 +32,253 @@ */ /* NUT SNMP common functions */ -#include "main.h" +#include "main.h" /* includes "config.h" which must be the first header */ +#include "nut_float.h" +#include "nut_stdint.h" #include "snmp-ups.h" #include "parseconf.h" +#include /* for isprint() */ + /* include all known mib2nut lookup tables */ #include "apc-mib.h" #include "mge-mib.h" #include "netvision-mib.h" #include "powerware-mib.h" -#include "eaton-mib.h" +#include "eaton-pdu-genesis2-mib.h" +#include "eaton-pdu-marlin-mib.h" +#include "eaton-pdu-pulizzi-mib.h" +#include "eaton-pdu-revelation-mib.h" #include "raritan-pdu-mib.h" +#include "raritan-px2-mib.h" #include "baytech-mib.h" #include "compaq-mib.h" #include "bestpower-mib.h" #include "cyberpower-mib.h" +#include "delta_ups-mib.h" +#include "huawei-mib.h" #include "ietf-mib.h" +#include "xppc-mib.h" +#include "eaton-ats16-nmc-mib.h" +#include "eaton-ats16-nm2-mib.h" +#include "apc-ats-mib.h" +#include "apc-pdu-mib.h" +#include "eaton-ats30-mib.h" +#include "emerson-avocent-pdu-mib.h" +#include "hpe-pdu-mib.h" + +/* Address API change */ +#if ( ! NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol ) && ( ! defined usmAESPrivProtocol ) +#define usmAESPrivProtocol usmAES128PrivProtocol +#endif + +#ifdef USM_PRIV_PROTO_AES_LEN +# define NUT_securityPrivProtoLen USM_PRIV_PROTO_AES_LEN +#else +# ifdef USM_PRIV_PROTO_AES128_LEN +# define NUT_securityPrivProtoLen USM_PRIV_PROTO_AES128_LEN +# else +/* FIXME: Find another way to get the size of array(?) to avoid: + * error: division 'sizeof (oid * {aka long unsigned int *}) / sizeof (oid {aka long unsigned int})' does not compute the number of array elements [-Werror=sizeof-pointer-div] + * See also https://bugs.php.net/bug.php?id=37564 for context + * which is due to most values in /usr/include/net-snmp/librarytransform_oids.h + * being defined as "oid[10]" or similar arrays, and "backwards compatibility" + * usmAESPrivProtocol name is an "oid *" pointer. + */ +# define NUT_securityPrivProtoLen (sizeof(usmAESPrivProtocol)/sizeof(oid)) +# endif +#endif static mib2nut_info_t *mib2nut[] = { - &apc, - &mge, - &netvision, - &powerware, - &aphel_genesisII, - &aphel_revelation, - &eaton_marlin, - &raritan, - &baytech, - &compaq, - &bestpower, - &cyberpower, + &apc_ats, /* This struct comes from : apc-ats-mib.c */ + &apc_pdu_rpdu, /* This struct comes from : apc-pdu-mib.c */ + &apc_pdu_rpdu2, /* This struct comes from : apc-pdu-mib.c */ + &apc_pdu_msp, /* This struct comes from : apc-pdu-mib.c */ + &apc, /* This struct comes from : apc-mib.c */ + &baytech, /* This struct comes from : baytech-mib.c */ + &bestpower, /* This struct comes from : bestpower-mib.c */ + &compaq, /* This struct comes from : compaq-mib.c */ + &cyberpower, /* This struct comes from : cyberpower-mib.c */ + &delta_ups, /* This struct comes from : delta_ups-mib.c */ + &eaton_ats16_nmc, /* This struct comes from : eaton-ats16-nmc-mib.c */ + &eaton_ats16_nm2, /* This struct comes from : eaton-ats16-nm2-mib.c */ + &eaton_ats30, /* This struct comes from : eaton-ats30-mib.c */ + &eaton_marlin, /* This struct comes from : eaton-mib.c */ + &emerson_avocent_pdu, /* This struct comes from : emerson-avocent-pdu-mib.c */ + &aphel_revelation, /* This struct comes from : eaton-mib.c */ + &aphel_genesisII, /* This struct comes from : eaton-mib.c */ + &pulizzi_switched1, /* This struct comes from : eaton-mib.c */ + &pulizzi_switched2, /* This struct comes from : eaton-mib.c */ + &hpe_pdu, /* This struct comes from : hpe-pdu-mib.c */ + &huawei, /* This struct comes from : huawei-mib.c */ + &mge, /* This struct comes from : mge-mib.c */ + &netvision, /* This struct comes from : netvision-mib.c */ + &powerware, /* This struct comes from : powerware-mib.c */ + &pxgx_ups, /* This struct comes from : powerware-mib.c */ + &raritan, /* This struct comes from : raritan-pdu-mib.c */ + &raritan_px2, /* This struct comes from : raritan-px2-mib.c */ + &xppc, /* This struct comes from : xppc-mib.c */ /* * Prepend vendor specific MIB mappings before IETF, so that * if a device supports both IETF and vendor specific MIB, - * the vendor specific one takes precedence (when mib=auto) + * the vendor specific one takes precedence (when mibs=auto) */ - &ietf, + &tripplite_ietf, /* This struct comes from : ietf-mib.c */ + &ietf, /* This struct comes from : ietf-mib.c */ /* end of structure. */ NULL }; -int input_phases, output_phases, bypass_phases; +struct snmp_session g_snmp_sess, *g_snmp_sess_p; +const char *OID_pwr_status; +int g_pwr_battery; +int pollfreq; /* polling frequency */ +int semistaticfreq; /* semistatic entry update frequency */ +static int semistatic_countdown = 0; + +static int quirk_symmetra_threephase = 0; + +/* Number of device(s): standard is "1", but talking + * to a daisychain (master device) means more than 1 + * (a directly addressable member of a daisy chain + * would be seen as a single-device chain though) + */ +static long devices_count = 1; +/* global var to handle daisychain iterations - + * changed by loops in snmp_ups_walk() and su_addcmd(); + * may be 0 for addressing certain values/commands + * across all chain devices via master (1); + * also may be 0 for non-daisychained devices + */ +static int current_device_number = 0; +/* global var to handle daisychain iterations - + * made TRUE if we resolved a "device.count" value + */ +static bool_t daisychain_enabled = FALSE; +static daisychain_info_t **daisychain_info = NULL; /* pointer to the Snmp2Nut lookup table */ mib2nut_info_t *mib2nut_info; /* FIXME: to be trashed */ snmp_info_t *snmp_info; -const char *mibname; -const char *mibvers; - -static void disable_transfer_oids(void); +alarms_info_t *alarms_info; +static const char *mibname; +static const char *mibvers; #define DRIVER_NAME "Generic SNMP UPS driver" -#define DRIVER_VERSION "0.58" +#define DRIVER_VERSION "1.21" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Arnaud Quette \n" \ + "Arnaud Quette \n" \ "Dmitry Frolov \n" \ "J.W. Hoogervorst \n" \ "Niels Baggesen \n" \ + "Jim Klimov \n" \ "Arjen de Korte ", DRV_STABLE, { NULL } }; /* FIXME: integrate MIBs info? do the same as for usbhid-ups! */ -time_t lastpoll = 0; +static time_t lastpoll = 0; -/* outlet OID index start with 0 or 1, +/* Communication status handling */ +#define COMM_UNKNOWN 0 +#define COMM_OK 1 +#define COMM_LOST 2 +static int comm_status = COMM_UNKNOWN; + +/* template OIDs index start with 0 or 1 (estimated stable for a MIB), * automatically guessed at the first pass */ -int outlet_index_base = -1; +static int template_index_base = -1; +/* Not that stable in the end... */ +static int device_template_index_base = -1; /* OID index of the 1rst daisychained device */ +static int outlet_template_index_base = -1; +static int outletgroup_template_index_base = -1; +static int ambient_template_index_base = -1; +static int device_template_offset = -1; /* sysOID location */ #define SYSOID_OID ".1.3.6.1.2.1.1.2.0" +/* Forward functions declarations */ +static void disable_transfer_oids(void); +bool_t get_and_process_data(int mode, snmp_info_t *su_info_p); +int extract_template_number(snmp_info_flags_t template_type, const char* varname); +snmp_info_flags_t get_template_type(const char* varname); + /* --------------------------------------------- * driver functions implementations * --------------------------------------------- */ void upsdrv_initinfo(void) { snmp_info_t *su_info_p; - char version[128]; - upsdebugx(1, "SNMP UPS driver : entering upsdrv_initinfo()"); + upsdebugx(1, "SNMP UPS driver: entering %s()", __func__); - snprintf(version, sizeof version, "%s (mib: %s %s)", - DRIVER_VERSION, mibname, mibvers); - dstate_setinfo("driver.version.internal", "%s", version); + dstate_setinfo("driver.version.data", "%s MIB %s", mibname, mibvers); + + if (snmp_info == NULL) { + fatalx(EXIT_FAILURE, "%s: snmp_info is not initialized", __func__); + } + + if (snmp_info[0].info_type == NULL) { + upsdebugx(1, "%s: WARNING: snmp_info is empty", __func__); + } /* add instant commands to the info database. - * outlet commands are processed during initial walk */ - for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++) + * outlet (and groups) commands are processed later, during initial walk */ + for (su_info_p = &snmp_info[0]; (su_info_p != NULL && su_info_p->info_type != NULL) ; su_info_p++) { + if (su_info_p->flags == 0UL) { + upsdebugx(4, + "SNMP UPS driver: %s: MIB2NUT mapping '%s' (OID '%s') did not define flags bits. " + "Entry would be treated as SU_FLAG_OK if available in returned data.", + __func__, + (su_info_p->info_type ? su_info_p->info_type : ""), + (su_info_p->OID ? su_info_p->OID : "") + ); + /* Treat as OK if avail, otherwise discarded */ + } + su_info_p->flags |= SU_FLAG_OK; if ((SU_TYPE(su_info_p) == SU_TYPE_CMD) - && !(su_info_p->flags & SU_OUTLET)) - dstate_addcmd(su_info_p->info_type); + && !(su_info_p->flags & SU_OUTLET) + && !(su_info_p->flags & SU_OUTLET_GROUP)) + { + /* first check that this OID actually exists */ + /* FIXME: daisychain commands support! */ + su_addcmd(su_info_p); +/* + if (nut_snmp_get(su_info_p->OID) != NULL) { + dstate_addcmd(su_info_p->info_type); + upsdebugx(1, "upsdrv_initinfo(): adding command '%s'", su_info_p->info_type); + } +*/ + } } if (testvar("notransferoids")) disable_transfer_oids(); - /* initialize all other INFO_ fields from list */ - if (snmp_ups_walk(SU_WALKMODE_INIT)) - dstate_dataok(); + if (testvar("symmetrathreephase")) + quirk_symmetra_threephase = 1; else + quirk_symmetra_threephase = 0; + + /* initialize all other INFO_ fields from list */ + if (snmp_ups_walk(SU_WALKMODE_INIT) == TRUE) { + dstate_dataok(); + comm_status = COMM_OK; + } + else { dstate_datastale(); + comm_status = COMM_LOST; + } /* setup handlers for instcmd and setvar functions */ upsh.setvar = su_setvar; @@ -147,25 +287,46 @@ void upsdrv_initinfo(void) void upsdrv_updateinfo(void) { - upsdebugx(1,"SNMP UPS driver : entering upsdrv_updateinfo()"); + upsdebugx(1,"SNMP UPS driver: entering %s()", __func__); /* only update every pollfreq */ - /* FIXME: update status (SU_STATUS_*), à la usbhid-ups, in between */ + /* FIXME: only update status (SU_STATUS_*), à la usbhid-ups, in between */ if (time(NULL) > (lastpoll + pollfreq)) { + alarm_init(); status_init(); /* update all dynamic info fields */ - if (snmp_ups_walk(SU_WALKMODE_UPDATE)) + if (snmp_ups_walk(SU_WALKMODE_UPDATE)) { + upsdebugx(1, "%s: pollfreq: Data OK", __func__); dstate_dataok(); - else + comm_status = COMM_OK; + } + else { + upsdebugx(1, "%s: pollfreq: Data STALE", __func__); dstate_datastale(); + comm_status = COMM_LOST; + } + /* Commit status first, otherwise in daisychain mode, "device.0" may + * clear the alarm count since it has an empty alarm buffer and if there + * is only one device that has alarms! */ + if (daisychain_enabled == FALSE) + alarm_commit(); status_commit(); + if (daisychain_enabled == TRUE) + alarm_commit(); /* store timestamp */ lastpoll = time(NULL); } + else { + /* Just tell the same status to upsd */ + if (comm_status == COMM_OK) + dstate_dataok(); + else + dstate_datastale(); + } } void upsdrv_shutdown(void) @@ -178,29 +339,62 @@ void upsdrv_shutdown(void) never send this command to the UPS. This is not an error, but a limitation of the interface used. */ - fatalx(EXIT_SUCCESS, "SNMP doesn't support shutdown in system halt script"); + + upsdebugx(1, "%s...", __func__); + + /* set shutdown and autostart delay */ + set_delays(); + + /* Try to shutdown with delay */ + if (su_instcmd("shutdown.return", NULL) == STAT_INSTCMD_HANDLED) { + /* Shutdown successful */ + return; + } + + /* If the above doesn't work, try shutdown.reboot */ + if (su_instcmd("shutdown.reboot", NULL) == STAT_INSTCMD_HANDLED) { + /* Shutdown successful */ + return; + } + + /* If the above doesn't work, try load.off.delay */ + if (su_instcmd("load.off.delay", NULL) == STAT_INSTCMD_HANDLED) { + /* Shutdown successful */ + return; + } + + fatalx(EXIT_FAILURE, "Shutdown failed!"); } void upsdrv_help(void) { - upsdebugx(1, "entering upsdrv_help"); + upsdebugx(1, "entering %s", __func__); } /* list flags and values that you want to receive via -x */ void upsdrv_makevartable(void) { - upsdebugx(1, "entering upsdrv_makevartable()"); + upsdebugx(1, "entering %s()", __func__); addvar(VAR_VALUE, SU_VAR_MIBS, + "NOTE: You can run the driver binary with '-x mibs=--list' for an up to date listing)\n" "Set MIB compliance (default=ietf, allowed: mge,apcc,netvision,pw,cpqpower,...)"); addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_COMMUNITY, "Set community name (default=public)"); addvar(VAR_VALUE, SU_VAR_VERSION, - "Set SNMP version (default=v1, allowed v2c)"); + "Set SNMP version (default=v1, allowed: v2c,v3)"); addvar(VAR_VALUE, SU_VAR_POLLFREQ, "Set polling frequency in seconds, to reduce network flow (default=30)"); + addvar(VAR_VALUE, SU_VAR_SEMISTATICFREQ, + "Set semistatic value update frequency in update cycles, to reduce network flow (default=10)"); + addvar(VAR_VALUE, SU_VAR_RETRIES, + "Specifies the number of Net-SNMP retries to be used in the requests (default=5)"); + addvar(VAR_VALUE, SU_VAR_TIMEOUT, + "Specifies the Net-SNMP timeout in seconds between retries (default=1)"); addvar(VAR_FLAG, "notransferoids", "Disable transfer OIDs (use on APCC Symmetras)"); + addvar(VAR_FLAG, "symmetrathreephase", + "Enable APCC three phase Symmetra quirks (use on APCC three phase Symmetras)"); addvar(VAR_VALUE, SU_VAR_SECLEVEL, "Set the securityLevel used for SNMPv3 messages (default=noAuthNoPriv, allowed: authNoPriv,authPriv)"); addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_SECNAME, @@ -208,24 +402,205 @@ void upsdrv_makevartable(void) addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_AUTHPASSWD, "Set the authentication pass phrase used for authenticated SNMPv3 messages (no default)"); addvar(VAR_VALUE | VAR_SENSITIVE, SU_VAR_PRIVPASSWD, - "Set the privacy pass phrase used for encrypted SNMPv3 messages (no default)"); - addvar(VAR_VALUE, SU_VAR_AUTHPROT, - "Set the authentication protocol (MD5 or SHA) used for authenticated SNMPv3 messages (default=MD5)"); - addvar(VAR_VALUE, SU_VAR_PRIVPROT, - "Set the privacy protocol (DES or AES) used for encrypted SNMPv3 messages (default=DES)"); + "Set the privacy pass phrase used for encrypted SNMPv3 messages (no default)"); + + /* Construct addvar() for SU_VAR_AUTHPROT: */ + { int comma = 0; + char tmp_buf[SU_LARGEBUF]; + char *p = tmp_buf; + char *pn; /* proto name to add */ + size_t remain = sizeof(tmp_buf) - 1; + int ret; + NUT_UNUSED_VARIABLE(comma); /* potentially, if no protocols are available */ + + tmp_buf[0] = '\0'; + + ret = snprintf(p, remain, "%s", + "Set the authentication protocol ("); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar()"); + } + p += ret; + remain -= (size_t)ret; + +#if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol + pn = "MD5"; + ret = snprintf(p, remain, "%s%s", (comma++ ? ", " : ""), pn ); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar(%s)", pn); + } + p += ret; + remain -= (size_t)ret; +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol + pn = "SHA"; + ret = snprintf(p, remain, "%s%s", (comma++ ? ", " : ""), pn ); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar(%s)", pn); + } + p += ret; + remain -= (size_t)ret; +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol + pn = "SHA256"; + ret = snprintf(p, remain, "%s%s", (comma++ ? ", " : ""), pn ); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar(%s)", pn); + } + p += ret; + remain -= (size_t)ret; +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol + pn = "SHA384"; + ret = snprintf(p, remain, "%s%s", (comma++ ? ", " : ""), pn ); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar(%s)", pn); + } + p += ret; + remain -= (size_t)ret; +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol + pn = "SHA512"; + ret = snprintf(p, remain, "%s%s", (comma++ ? ", " : ""), pn ); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar(%s)", pn); + } + p += ret; + remain -= (size_t)ret; +#endif + + pn = "none supported"; + ret = snprintf(p, remain, "%s", (comma++ ? "" : pn) ); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar(%s)", pn); + } + p += ret; + remain -= (size_t)ret; + + ret = snprintf(p, remain, "%s", + ") used for authenticated SNMPv3 messages (default=MD5 if available)"); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar()"); + } + p += ret; + remain -= (size_t)ret; + + addvar(VAR_VALUE, SU_VAR_AUTHPROT, tmp_buf); + } /* Construct addvar() for AUTHPROTO */ + + /* Construct addvar() for SU_VAR_PRIVPROT: */ + { int comma = 0; + char tmp_buf[SU_LARGEBUF]; + char *p = tmp_buf; + char *pn; /* proto name to add */ + size_t remain = sizeof(tmp_buf) - 1; + int ret; + NUT_UNUSED_VARIABLE(comma); /* potentially, if no protocols are available */ + + tmp_buf[0] = '\0'; + + ret = snprintf(p, remain, "%s", + "Set the privacy protocol ("); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar()"); + } + p += ret; + remain -= (size_t)ret; + +#if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol + pn = "DES"; + ret = snprintf(p, remain, "%s%s", (comma++ ? ", " : ""), pn ); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar(%s)", pn); + } + p += ret; + remain -= (size_t)ret; +#endif +#if NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol + pn = "AES"; + ret = snprintf(p, remain, "%s%s", (comma++ ? ", " : ""), pn ); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar(%s)", pn); + } + p += ret; + remain -= (size_t)ret; +#endif +#if NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 +# if NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol + pn = "AES192"; + ret = snprintf(p, remain, "%s%s", (comma++ ? ", " : ""), pn ); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar(%s)", pn); + } + p += ret; + remain -= (size_t)ret; +# endif +# if NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol + pn = "AES256"; + ret = snprintf(p, remain, "%s%s", (comma++ ? ", " : ""), pn ); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar(%s)", pn); + } + p += ret; + remain -= (size_t)ret; +# endif +#endif /* NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 */ + + pn = "none supported"; + ret = snprintf(p, remain, "%s", (comma++ ? "" : pn) ); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar(%s)", pn); + } + p += ret; + remain -= (size_t)ret; + + ret = snprintf(p, remain, "%s", + ") used for encrypted SNMPv3 messages (default=DES if available)"); + if (ret < 0 || (uintmax_t)ret > (uintmax_t)remain || (uintmax_t)ret > SIZE_MAX) { + fatalx(EXIT_FAILURE, "Could not addvar()"); + } + p += ret; + remain -= (size_t)ret; + + addvar(VAR_VALUE, SU_VAR_PRIVPROT, tmp_buf); + } /* Construct addvar() for PRIVPROTO */ + + addvar(VAR_VALUE, SU_VAR_ONDELAY, + "Set start delay time after shutdown"); + addvar(VAR_VALUE, SU_VAR_OFFDELAY, + "Set delay time before shutdown "); } void upsdrv_initups(void) { - snmp_info_t *su_info_p; + snmp_info_t *su_info_p, *cur_info_p; char model[SU_INFOSIZE]; - bool_t status; + bool_t status= FALSE; const char *mibs; + int curdev = 0; - upsdebugx(1, "SNMP UPS driver : entering upsdrv_initups()"); + upsdebugx(1, "SNMP UPS driver: entering %s()", __func__); /* Retrieve user's parameters */ mibs = testvar(SU_VAR_MIBS) ? getval(SU_VAR_MIBS) : "auto"; + if (!strcmp(mibs, "--list")) { + printf("The 'mibs' argument is '%s', so just listing the mappings this driver knows,\n" + "and for 'mibs=auto' these mappings will be tried in the following order until\n" + "the first one matches your device\n\n", mibs); + int i; + printf("%7s\t%-23s\t%-7s\t%-31s\t%-s\n", + "NUMBER", "MAPPING NAME", "VERSION", + "ENTRY POINT OID", "AUTO CHECK OID"); + for (i=0; mib2nut[i] != NULL; i++) { + printf(" %4d \t%-23s\t%7s\t%-31s\t%-s\n", (i+1), + mib2nut[i]->mib_name ? mib2nut[i]->mib_name : "" , + mib2nut[i]->mib_version ? mib2nut[i]->mib_version : "" , + mib2nut[i]->sysOID ? mib2nut[i]->sysOID : "" , + mib2nut[i]->oid_auto_check ? mib2nut[i]->oid_auto_check : "" ); + } + printf("\nOverall this driver has loaded %d MIB-to-NUT mapping tables\n", i); + exit(EXIT_SUCCESS); + } /* init SNMP library, etc... */ nut_snmp_init(progname, device_path); @@ -241,9 +616,68 @@ void upsdrv_initups(void) else pollfreq = DEFAULT_POLLFREQ; + /* init semistatic update frequency */ + if (getval(SU_VAR_SEMISTATICFREQ)) + semistaticfreq = atoi(getval(SU_VAR_SEMISTATICFREQ)); + else + semistaticfreq = DEFAULT_SEMISTATICFREQ; + if (semistaticfreq < 1) { + upsdebugx(1, "Bad %s value provided, setting to default", SU_VAR_SEMISTATICFREQ); + semistaticfreq = DEFAULT_SEMISTATICFREQ; + } + semistatic_countdown = semistaticfreq; + /* Get UPS Model node to see if there's a MIB */ +/* FIXME: extend and use match_model_OID(char *model) */ su_info_p = su_find_info("ups.model"); - status = nut_snmp_get_str(su_info_p->OID, model, sizeof(model), NULL); + /* Try to get device.model if ups.model is not available */ + if (su_info_p == NULL) + su_info_p = su_find_info("device.model"); + + if (su_info_p != NULL) { + /* Daisychain specific: we may have a template (including formatting + * string) that needs to be adapted! */ + if (strchr(su_info_p->OID, '%') != NULL) + { + upsdebugx(2, "Found template, need to be adapted"); + cur_info_p = (snmp_info_t *)malloc(sizeof(snmp_info_t)); + cur_info_p->info_type = (char *)xmalloc(SU_INFOSIZE); + cur_info_p->OID = (char *)xmalloc(SU_INFOSIZE); + snprintf((char*)cur_info_p->info_type, SU_INFOSIZE, "%s", su_info_p->info_type); + /* Use the daisychain master (0) / 1rst device index */ +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf((char*)cur_info_p->OID, SU_INFOSIZE, su_info_p->OID, 0); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + } + else { + upsdebugx(2, "Found entry, not a template %s", su_info_p->OID); + /* Otherwise, just point at what we found */ + cur_info_p = su_info_p; + } + + /* Actually get the data */ + status = nut_snmp_get_str(cur_info_p->OID, model, sizeof(model), NULL); + + /* Free our malloc, if it was dynamic */ + if (strchr(su_info_p->OID, '%') != NULL) { + if (cur_info_p->info_type != NULL) + free((char*)cur_info_p->info_type); + if (cur_info_p->OID != NULL) + free((char*)cur_info_p->OID); + if (cur_info_p != NULL) + free((char*)cur_info_p); + } + } if (status == TRUE) upslogx(0, "Detected %s on host %s (mib: %s %s)", @@ -251,10 +685,93 @@ void upsdrv_initups(void) else fatalx(EXIT_FAILURE, "%s MIB wasn't found on %s", mibs, g_snmp_sess.peername); /* FIXME: "No supported device detected" */ + + /* Init daisychain and check if support is required */ + daisychain_init(); + + /* Allocate / init the daisychain info structure (for phases only for now) + * daisychain_info[0] is the whole chain! (added +1) */ + daisychain_info = (daisychain_info_t**)malloc( + sizeof(daisychain_info_t) * (size_t)(devices_count + 1) + ); + for (curdev = 0 ; curdev <= devices_count ; curdev++) { + daisychain_info[curdev] = (daisychain_info_t*)malloc(sizeof(daisychain_info_t)); + daisychain_info[curdev]->input_phases = (long)-1; + daisychain_info[curdev]->output_phases = (long)-1; + daisychain_info[curdev]->bypass_phases = (long)-1; + } + + /* FIXME: also need daisychain awareness (so init)! + * i.e load.off.delay+load.off + device.1.load.off.delay+device.1.load.off + ... */ +/* FIXME: daisychain commands support! */ + if (su_find_info("load.off.delay")) { + /* Adds default with a delay value of '0' (= immediate) */ + dstate_addcmd("load.off"); + } + + if (su_find_info("load.on.delay")) { + /* Adds default with a delay value of '0' (= immediate) */ + dstate_addcmd("load.on"); + } + + if (su_find_info("load.off.delay") && su_find_info("load.on.delay")) { + /* Add composite instcmds (require setting multiple OID values) */ + dstate_addcmd("shutdown.return"); + dstate_addcmd("shutdown.stayoff"); + } + + /* Publish sysDescr, sysContact and sysLocation (from IETF standard paths) + * for all subdrivers that do not have one defined in their mapping + * tables (note: for lack of better knowledge, defined as read-only + * entries here, and also read-once - not updated during driver uptime) */ + + if (NULL == dstate_getinfo("device.description") + && NULL == dstate_getinfo("device.1.description") + ) { + /* sysDescr.0 */ + if (nut_snmp_get_str(".1.3.6.1.2.1.1.1.0", model, sizeof(model), NULL) == TRUE) { + upsdebugx(2, "Using IETF-MIB default to get and publish sysDescr for device.description (once)"); + dstate_setinfo("device.description", "%s", model); + } else { + upsdebugx(2, "Can't get and publish sysDescr for device.description"); + } + } + + if (NULL == dstate_getinfo("device.contact") + && NULL == dstate_getinfo("device.1.contact") + ) { + /* sysContact.0 */ + if (nut_snmp_get_str(".1.3.6.1.2.1.1.4.0", model, sizeof(model), NULL) == TRUE) { + upsdebugx(2, "Using IETF-MIB default to get and publish sysContact for device.contact (once)"); + dstate_setinfo("device.contact", "%s", model); + } else { + upsdebugx(2, "Can't get and publish sysContact for device.contact"); + } + } + + if (NULL == dstate_getinfo("device.location") + && NULL == dstate_getinfo("device.1.location") + ) { + /* sysLocation.0 */ + if (nut_snmp_get_str(".1.3.6.1.2.1.1.6.0", model, sizeof(model), NULL) == TRUE) { + upsdebugx(2, "Using IETF-MIB default to get and publish sysLocation for device.location (once)"); + dstate_setinfo("device.location", "%s", model); + } else { + upsdebugx(2, "Can't get and publish sysLocation for device.location"); + } + } + + /* set shutdown and autostart delay */ + set_delays(); } void upsdrv_cleanup(void) { + /* General cleanup */ + if (daisychain_info) + free(daisychain_info); + + /* Net-SNMP specific cleanup */ nut_snmp_cleanup(); } @@ -268,8 +785,10 @@ void nut_snmp_init(const char *type, const char *hostname) const char *community, *version; const char *secLevel = NULL, *authPassword, *privPassword; const char *authProtocol, *privProtocol; + int snmp_retries = DEFAULT_NETSNMP_RETRIES; + long snmp_timeout = DEFAULT_NETSNMP_TIMEOUT; - upsdebugx(2, "SNMP UPS driver : entering nut_snmp_init(%s)", type); + upsdebugx(2, "SNMP UPS driver: entering %s(%s)", __func__, type); /* Force numeric OIDs resolution (ie, do not resolve to textual names) * This is mostly for the convenience of debug output */ @@ -286,9 +805,34 @@ void nut_snmp_init(const char *type, const char *hostname) g_snmp_sess.peername = xstrdup(hostname); + /* Net-SNMP timeout and retries */ + if (testvar(SU_VAR_RETRIES)) { + snmp_retries = atoi(getval(SU_VAR_RETRIES)); + } + g_snmp_sess.retries = snmp_retries; + upsdebugx(2, "Setting SNMP retries to %i", snmp_retries); + + if (testvar(SU_VAR_TIMEOUT)) { + snmp_timeout = atol(getval(SU_VAR_TIMEOUT)); + } + /* We have to convert from seconds to microseconds */ + g_snmp_sess.timeout = snmp_timeout * ONE_SEC; + upsdebugx(2, "Setting SNMP timeout to %ld second(s)", snmp_timeout); + /* Retrieve user parameters */ version = testvar(SU_VAR_VERSION) ? getval(SU_VAR_VERSION) : "v1"; - + +/* Older CLANG (e.g. clang-3.4) sees short strings in str{n}cmp() + * arguments as arrays and claims out-of-bounds accesses + */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ARRAY_BOUNDS) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Warray-bounds" +#endif +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Warray-bounds" +#endif if ((strcmp(version, "v1") == 0) || (strcmp(version, "v2c") == 0)) { g_snmp_sess.version = (strcmp(version, "v1") == 0) ? SNMP_VERSION_1 : SNMP_VERSION_2c; community = testvar(SU_VAR_COMMUNITY) ? getval(SU_VAR_COMMUNITY) : "public"; @@ -296,6 +840,12 @@ void nut_snmp_init(const char *type, const char *hostname) g_snmp_sess.community_len = strlen(community); } else if (strcmp(version, "v3") == 0) { +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ARRAY_BOUNDS) +# pragma GCC diagnostic pop +#endif /* SNMP v3 related init */ g_snmp_sess.version = SNMP_VERSION_3; @@ -346,23 +896,74 @@ void nut_snmp_init(const char *type, const char *hostname) g_snmp_sess.securityAuthKeyLen = USM_AUTH_KU_LEN; authProtocol = testvar(SU_VAR_AUTHPROT) ? getval(SU_VAR_AUTHPROT) : "MD5"; +#if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol if (strcmp(authProtocol, "MD5") == 0) { g_snmp_sess.securityAuthProto = usmHMACMD5AuthProtocol; g_snmp_sess.securityAuthProtoLen = sizeof(usmHMACMD5AuthProtocol)/sizeof(oid); } - else if (strcmp(authProtocol, "SHA") == 0) { + else +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol + if (strcmp(authProtocol, "SHA") == 0) { g_snmp_sess.securityAuthProto = usmHMACSHA1AuthProtocol; g_snmp_sess.securityAuthProtoLen = sizeof(usmHMACSHA1AuthProtocol)/sizeof(oid); } else +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol + if (strcmp(authProtocol, "SHA256") == 0) { + g_snmp_sess.securityAuthProto = usmHMAC192SHA256AuthProtocol; + g_snmp_sess.securityAuthProtoLen = sizeof(usmHMAC192SHA256AuthProtocol)/sizeof(oid); + } + else +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol + if (strcmp(authProtocol, "SHA384") == 0) { + g_snmp_sess.securityAuthProto = usmHMAC256SHA384AuthProtocol; + g_snmp_sess.securityAuthProtoLen = sizeof(usmHMAC256SHA384AuthProtocol)/sizeof(oid); + } + else +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol + if (strcmp(authProtocol, "SHA512") == 0) { + g_snmp_sess.securityAuthProto = usmHMAC384SHA512AuthProtocol; + g_snmp_sess.securityAuthProtoLen = sizeof(usmHMAC384SHA512AuthProtocol)/sizeof(oid); + } + else +#endif fatalx(EXIT_FAILURE, "Bad SNMPv3 authProtocol: %s", authProtocol); /* set the authentication key to a MD5/SHA1 hashed version of our * passphrase (must be at least 8 characters long) */ - if(g_snmp_sess.securityLevel != SNMP_SEC_LEVEL_NOAUTH) { + if (g_snmp_sess.securityLevel != SNMP_SEC_LEVEL_NOAUTH) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + +/* NOTE: Net-SNMP headers just are weird like that, in the same release: +net-snmp/types.h: size_t securityAuthProtoLen; +net-snmp/library/keytools.h: int generate_Ku(const oid * hashtype, u_int hashtype_len, ... + * Should we match in configure like for "getnameinfo()" arg types? + * Currently we cast one to another, below (detecting target type could help). + */ + if ((uintmax_t)g_snmp_sess.securityAuthProtoLen > UINT_MAX) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + fatalx(EXIT_FAILURE, + "Bad SNMPv3 securityAuthProtoLen: %zu", + g_snmp_sess.securityAuthProtoLen); + } + if (generate_Ku(g_snmp_sess.securityAuthProto, - g_snmp_sess.securityAuthProtoLen, - (u_char *) authPassword, strlen(authPassword), + (u_int)g_snmp_sess.securityAuthProtoLen, + (const unsigned char *) authPassword, strlen(authPassword), g_snmp_sess.securityAuthKey, &g_snmp_sess.securityAuthKeyLen) != SNMPERR_SUCCESS) { @@ -372,24 +973,65 @@ void nut_snmp_init(const char *type, const char *hostname) privProtocol = testvar(SU_VAR_PRIVPROT) ? getval(SU_VAR_PRIVPROT) : "DES"; +#if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol if (strcmp(privProtocol, "DES") == 0) { g_snmp_sess.securityPrivProto = usmDESPrivProtocol; g_snmp_sess.securityPrivProtoLen = sizeof(usmDESPrivProtocol)/sizeof(oid); } - else if (strcmp(privProtocol, "AES") == 0) { + else +#endif +#if NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol + if (strcmp(privProtocol, "AES") == 0) { g_snmp_sess.securityPrivProto = usmAESPrivProtocol; - g_snmp_sess.securityPrivProtoLen = sizeof(usmAESPrivProtocol)/sizeof(oid); + g_snmp_sess.securityPrivProtoLen = NUT_securityPrivProtoLen; } else - fatalx(EXIT_FAILURE, "Bad SNMPv3 authProtocol: %s", authProtocol); +#endif +#if NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 +# if NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol + if (strcmp(privProtocol, "AES192") == 0) { + g_snmp_sess.securityPrivProto = usmAES192PrivProtocol; + g_snmp_sess.securityPrivProtoLen = (sizeof(usmAES192PrivProtocol)/sizeof(oid)); + } + else +# endif +# if NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol + if (strcmp(privProtocol, "AES256") == 0) { + g_snmp_sess.securityPrivProto = usmAES256PrivProtocol; + g_snmp_sess.securityPrivProtoLen = (sizeof(usmAES256PrivProtocol)/sizeof(oid)); + } + else +# endif +#endif /* NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 */ + fatalx(EXIT_FAILURE, "Bad SNMPv3 privProtocol: %s", privProtocol); /* set the privacy key to a MD5/SHA1 hashed version of our * passphrase (must be at least 8 characters long) */ - if(g_snmp_sess.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) { + if (g_snmp_sess.securityLevel == SNMP_SEC_LEVEL_AUTHPRIV) { g_snmp_sess.securityPrivKeyLen = USM_PRIV_KU_LEN; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + /* See comment on generate_Ku() a few dozen lines above */ + if ((uintmax_t)g_snmp_sess.securityAuthProtoLen > UINT_MAX) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + fatalx(EXIT_FAILURE, + "Bad SNMPv3 securityAuthProtoLen: %zu", + g_snmp_sess.securityAuthProtoLen); + } + if (generate_Ku(g_snmp_sess.securityAuthProto, - g_snmp_sess.securityAuthProtoLen, - (u_char *) privPassword, strlen(privPassword), + (u_int)g_snmp_sess.securityAuthProtoLen, + (const unsigned char *) privPassword, strlen(privPassword), g_snmp_sess.securityPrivKey, &g_snmp_sess.securityPrivKeyLen) != SNMPERR_SUCCESS) { @@ -419,126 +1061,314 @@ void nut_snmp_cleanup(void) SOCK_CLEANUP; /* wrapper not needed on Unix! */ } -struct snmp_pdu *nut_snmp_get(const char *OID) +/* Free a struct snmp_pdu * returned by nut_snmp_walk */ +static void nut_snmp_free(struct snmp_pdu ** array_to_free) +{ + struct snmp_pdu ** current_element; + + if (array_to_free != NULL) { + current_element = array_to_free; + + while (*current_element != NULL) { + snmp_free_pdu(*current_element); + current_element++; + } + + free( array_to_free ); + } +} + +/* Return a NULL terminated array of snmp_pdu * */ +static struct snmp_pdu **nut_snmp_walk(const char *OID, int max_iteration) { int status; struct snmp_pdu *pdu, *response = NULL; oid name[MAX_OID_LEN]; size_t name_len = MAX_OID_LEN; + oid * current_name; + size_t current_name_len; static unsigned int numerr = 0; + int nb_iteration = 0; + struct snmp_pdu ** ret_array = NULL; + int type = SNMP_MSG_GET; - upsdebugx(3, "nut_snmp_get(%s)", OID); + upsdebugx(3, "%s(%s)", __func__, OID); + upsdebugx(4, "%s: max. iteration = %i", __func__, max_iteration); /* create and send request. */ if (!snmp_parse_oid(OID, name, &name_len)) { - upsdebugx(2, "[%s] nut_snmp_get: %s: %s", - upsname?upsname:device_name, OID, snmp_api_errstring(snmp_errno)); + upsdebugx(2, "[%s] %s: %s: %s", + upsname?upsname:device_name, __func__, OID, snmp_api_errstring(snmp_errno)); return NULL; } - pdu = snmp_pdu_create(SNMP_MSG_GET); + current_name = name; + current_name_len = name_len; - if (pdu == NULL) - fatalx(EXIT_FAILURE, "Not enough memory"); - - snmp_add_null_var(pdu, name, name_len); - - status = snmp_synch_response(g_snmp_sess_p, pdu, &response); - - if (!response) - return NULL; - - if (!((status == STAT_SUCCESS) && (response->errstat == SNMP_ERR_NOERROR))) - { - if (mibname == NULL) { - /* We are probing for proper mib - ignore errors */ - snmp_free_pdu(response); - return NULL; + while( nb_iteration < max_iteration ) { + /* Going to a shorter OID means we are outside our sub-tree */ + if( current_name_len < name_len ) { + break; } - numerr++; + pdu = snmp_pdu_create(type); - if ((numerr == SU_ERR_LIMIT) || ((numerr % SU_ERR_RATE) == 0)) - upslogx(LOG_WARNING, "[%s] Warning: excessive poll " - "failures, limiting error reporting", - upsname?upsname:device_name); + if (pdu == NULL) { + fatalx(EXIT_FAILURE, "Not enough memory"); + } - if ((numerr < SU_ERR_LIMIT) || ((numerr % SU_ERR_RATE) == 0)) - nut_snmp_perror(g_snmp_sess_p, status, response, - "nut_snmp_get: %s", OID); + snmp_add_null_var(pdu, current_name, current_name_len); - snmp_free_pdu(response); - response = NULL; - } else { - numerr = 0; + status = snmp_synch_response(g_snmp_sess_p, pdu, &response); + + if (!response) { + break; + } + + if (!((status == STAT_SUCCESS) && (response->errstat == SNMP_ERR_NOERROR))) { + if (mibname == NULL) { + /* We are probing for proper mib - ignore errors */ + snmp_free_pdu(response); + nut_snmp_free(ret_array); + return NULL; + } + + numerr++; + + if ((numerr == SU_ERR_LIMIT) || ((numerr % SU_ERR_RATE) == 0)) { + upslogx(LOG_WARNING, "[%s] Warning: excessive poll " + "failures, limiting error reporting (OID = %s)", + upsname?upsname:device_name, OID); + } + + if ((numerr < SU_ERR_LIMIT) || ((numerr % SU_ERR_RATE) == 0)) { + if (type == SNMP_MSG_GETNEXT) { + upsdebugx(2, "=> No more OID, walk complete"); + } + else { + nut_snmp_perror(g_snmp_sess_p, status, response, + "%s: %s", __func__, OID); + } + } + + snmp_free_pdu(response); + break; + } else { + numerr = 0; + } + + nb_iteration++; + /* +1 is for the terminating NULL */ + struct snmp_pdu ** new_ret_array = realloc( + ret_array, + sizeof(struct snmp_pdu*) * ((size_t)nb_iteration+1) + ); + if (new_ret_array == NULL) { + upsdebugx(1, "%s: Failed to realloc thread", __func__); + break; + } + else { + ret_array = new_ret_array; + } + ret_array[nb_iteration-1] = response; + ret_array[nb_iteration]=NULL; + + current_name = response->variables->name; + current_name_len = response->variables->name_length; + + type = SNMP_MSG_GETNEXT; } - return response; + return ret_array; } -bool_t nut_snmp_get_str(const char *OID, char *buf, size_t buf_len, info_lkp_t *oid2info) +struct snmp_pdu *nut_snmp_get(const char *OID) +{ + struct snmp_pdu ** pdu_array; + struct snmp_pdu * ret_pdu; + + if (OID == NULL) + return NULL; + + upsdebugx(3, "%s(%s)", __func__, OID); + + pdu_array = nut_snmp_walk(OID,1); + + if(pdu_array == NULL) { + return NULL; + } + + ret_pdu = snmp_clone_pdu(*pdu_array); + + nut_snmp_free(pdu_array); + + return ret_pdu; +} + +static bool_t decode_str(struct snmp_pdu *pdu, char *buf, size_t buf_len, info_lkp_t *oid2info) { size_t len = 0; - struct snmp_pdu *pdu; - - upsdebugx(3, "Entering nut_snmp_get_str()"); + char tmp_buf[SU_LARGEBUF]; /* zero out buffer. */ memset(buf, 0, buf_len); - pdu = nut_snmp_get(OID); - if (pdu == NULL) - return FALSE; - switch (pdu->variables->type) { case ASN_OCTET_STR: case ASN_OPAQUE: len = pdu->variables->val_len > buf_len - 1 ? buf_len - 1 : pdu->variables->val_len; - memcpy(buf, pdu->variables->val.string, len); - buf[len] = '\0'; + /* Test for hexadecimal values */ + int hex = 0, x; + unsigned char *cp; + for(cp = pdu->variables->val.string, x = 0; x < (int)pdu->variables->val_len; x++, cp++) { + if (!(isprint((size_t)*cp) || isspace((size_t)*cp))) { + hex = 1; + } + } + if (hex) + snprint_hexstring(buf, buf_len, pdu->variables->val.string, pdu->variables->val_len); + else { + memcpy(buf, pdu->variables->val.string, len); + buf[len] = '\0'; + } break; case ASN_INTEGER: case ASN_COUNTER: case ASN_GAUGE: if(oid2info) { const char *str; - if((str=su_find_infoval(oid2info, *pdu->variables->val.integer))) { + /* See union netsnmp_vardata in net-snmp/types.h: "integer" is a "long*" */ + assert(sizeof(pdu->variables->val.integer) == sizeof(long*)); + /* If in future net-snmp headers val becomes not-a-pointer, + * compiler should complain about (void*) arg casting here */ + if((str = su_find_infoval(oid2info, pdu->variables->val.integer))) { strncpy(buf, str, buf_len-1); } + /* when oid2info returns NULL, don't publish the variable! */ else { - strncpy(buf, "UNKNOWN", buf_len-1); + /* strncpy(buf, "UNKNOWN", buf_len-1); */ + return FALSE; } buf[buf_len-1]='\0'; } else { - len = snprintf(buf, buf_len, "%ld", *pdu->variables->val.integer); + int ret = snprintf(buf, buf_len, "%ld", *pdu->variables->val.integer); + if (ret < 0) + upsdebugx(3, "Failed to retrieve ASN_GAUGE"); + else + len = (size_t)ret; } break; case ASN_TIMETICKS: /* convert timeticks to seconds */ - len = snprintf(buf, buf_len, "%ld", *pdu->variables->val.integer / 100); + { + int ret = snprintf(buf, buf_len, "%ld", *pdu->variables->val.integer / 100); + if (ret < 0) + upsdebugx(3, "Failed to retrieve ASN_TIMETICKS"); + else + len = (size_t)ret; + } break; case ASN_OBJECT_ID: - len = snprint_objid (buf, buf_len, pdu->variables->val.objid, pdu->variables->val_len / sizeof(oid)); + snprint_objid (tmp_buf, sizeof(tmp_buf), pdu->variables->val.objid, pdu->variables->val_len / sizeof(oid)); + upsdebugx(2, "Received an OID value: %s", tmp_buf); + /* Try to get the value of the pointed OID */ + if (nut_snmp_get_str(tmp_buf, buf, buf_len, oid2info) == FALSE) { + upsdebugx(3, "Failed to retrieve OID value, using fallback"); + /* Otherwise return the last part of the returned OID (ex: 1.2.3 => 3) */ + char *oid_leaf = strrchr(tmp_buf, '.'); + snprintf(buf, buf_len, "%s", oid_leaf+1); + upsdebugx(3, "Fallback value: %s", buf); + } break; default: - upsdebugx(2, "[%s] unhandled ASN 0x%x received from %s", - upsname?upsname:device_name, pdu->variables->type, OID); return FALSE; } - snmp_free_pdu(pdu); - return TRUE; } +bool_t nut_snmp_get_str(const char *OID, char *buf, size_t buf_len, info_lkp_t *oid2info) +{ + struct snmp_pdu *pdu; + bool_t ret; + + upsdebugx(3, "Entering %s()", __func__); + + pdu = nut_snmp_get(OID); + if (pdu == NULL) + return FALSE; + + ret = decode_str(pdu,buf,buf_len,oid2info); + + if(ret == FALSE) { + upsdebugx(2, "[%s] unhandled ASN 0x%x received from %s", + upsname?upsname:device_name, pdu->variables->type, OID); + } + + snmp_free_pdu(pdu); + + return ret; +} + + +static bool_t decode_oid(struct snmp_pdu *pdu, char *buf, size_t buf_len) +{ + /* zero out buffer. */ + memset(buf, 0, buf_len); + + switch (pdu->variables->type) { + case ASN_OBJECT_ID: + snprint_objid (buf, buf_len, pdu->variables->val.objid, + pdu->variables->val_len / sizeof(oid)); + upsdebugx(2, "OID value: %s", buf); + break; + default: + return FALSE; + } + + return TRUE; +} + +/* Return the value stored in OID, which is an OID (sysOID for example) + * and don't try to get the value pointed by this OID (no follow). + * To achieve the latter behavior, use standard nut_snmp_get_{str,int}() */ +bool_t nut_snmp_get_oid(const char *OID, char *buf, size_t buf_len) +{ + struct snmp_pdu *pdu; + bool_t ret = FALSE; + + /* zero out buffer. */ + memset(buf, 0, buf_len); + + upsdebugx(3, "Entering %s()", __func__); + + pdu = nut_snmp_get(OID); + if (pdu == NULL) + return FALSE; + + ret = decode_oid(pdu, buf, buf_len); + + if(ret == FALSE) { + upsdebugx(2, "[%s] unhandled ASN 0x%x received from %s", + upsname?upsname:device_name, pdu->variables->type, OID); + } + + snmp_free_pdu(pdu); + + return ret; +} + bool_t nut_snmp_get_int(const char *OID, long *pval) { + char tmp_buf[SU_LARGEBUF]; struct snmp_pdu *pdu; long value; char *buf; + upsdebugx(3, "Entering %s()", __func__); + pdu = nut_snmp_get(OID); if (pdu == NULL) return FALSE; @@ -561,11 +1391,22 @@ bool_t nut_snmp_get_int(const char *OID, long *pval) /* convert timeticks to seconds */ value = *pdu->variables->val.integer / 100; break; + case ASN_OBJECT_ID: + snprint_objid (tmp_buf, sizeof(tmp_buf), pdu->variables->val.objid, pdu->variables->val_len / sizeof(oid)); + upsdebugx(2, "Received an OID value: %s", tmp_buf); + /* Try to get the value of the pointed OID */ + if (nut_snmp_get_int(tmp_buf, &value) == FALSE) { + upsdebugx(3, "Failed to retrieve OID value, using fallback"); + /* Otherwise return the last part of the returned OID (ex: 1.2.3 => 3) */ + char *oid_leaf = strrchr(tmp_buf, '.'); + value = strtol(oid_leaf+1, NULL, 0); + upsdebugx(3, "Fallback value: %ld", value); + } + break; default: upslogx(LOG_ERR, "[%s] unhandled ASN 0x%x received from %s", upsname?upsname:device_name, pdu->variables->type, OID); return FALSE; - break; } snmp_free_pdu(pdu); @@ -584,11 +1425,11 @@ bool_t nut_snmp_set(const char *OID, char type, const char *value) oid name[MAX_OID_LEN]; size_t name_len = MAX_OID_LEN; - upsdebugx(1, "entering nut_snmp_set (%s, %c, %s)", OID, type, value); + upsdebugx(1, "entering %s(%s, %c, %s)", __func__, OID, type, value); if (!snmp_parse_oid(OID, name, &name_len)) { - upslogx(LOG_ERR, "[%s] nut_snmp_set: %s: %s", - upsname?upsname:device_name, OID, snmp_api_errstring(snmp_errno)); + upslogx(LOG_ERR, "[%s] %s: %s: %s", + upsname?upsname:device_name, __func__, OID, snmp_api_errstring(snmp_errno)); return FALSE; } @@ -597,8 +1438,8 @@ bool_t nut_snmp_set(const char *OID, char type, const char *value) fatalx(EXIT_FAILURE, "Not enough memory"); if (snmp_add_var(pdu, name, name_len, type, value)) { - upslogx(LOG_ERR, "[%s] nut_snmp_set: %s: %s", - upsname?upsname:device_name, OID, snmp_api_errstring(snmp_errno)); + upslogx(LOG_ERR, "[%s] %s: %s: %s", + upsname?upsname:device_name, __func__, OID, snmp_api_errstring(snmp_errno)); return FALSE; } @@ -609,7 +1450,7 @@ bool_t nut_snmp_set(const char *OID, char type, const char *value) ret = TRUE; else nut_snmp_perror(g_snmp_sess_p, status, response, - "nut_snmp_set: can't set %s", OID); + "%s: can't set %s", __func__, OID); snmp_free_pdu(response); return ret; @@ -655,17 +1496,33 @@ void nut_snmp_perror(struct snmp_session *sess, int status, upsname?upsname:device_name, buf, snmperrstr); free(snmperrstr); } else if (status == STAT_SUCCESS) { +/* Net-SNMP headers provide and consume errstat with different types: +net-snmp/output_api.h: const char *snmp_errstring(int snmp_errorno); +net-snmp/types.h: long errstat; + * Should we match in configure like for "getnameinfo()" arg types? + * Currently we cast one to another, below (detecting target type could help). + */ switch (response->errstat) { case SNMP_ERR_NOERROR: break; case SNMP_ERR_NOSUCHNAME: /* harmless */ upsdebugx(2, "[%s] %s: %s", - upsname?upsname:device_name, buf, snmp_errstring(response->errstat)); + upsname?upsname:device_name, + buf, + (response->errstat > INT_MAX + ? "(Net-SNMP errstat value is out of range)" + : snmp_errstring((int)response->errstat) + )); break; default: upslogx(LOG_ERR, "[%s] %s: Error in packet: %s", - upsname?upsname:device_name, buf, snmp_errstring(response->errstat)); + upsname?upsname:device_name, + buf, + (response->errstat > INT_MAX + ? "(Net-SNMP errstat value is out of range)" + : snmp_errstring((int)response->errstat) + )); break; } } else if (status == STAT_TIMEOUT) { @@ -690,7 +1547,15 @@ static void disable_transfer_oids(void) upslogx(LOG_INFO, "Disabling transfer OIDs"); - for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++) { + if (snmp_info == NULL) { + fatalx(EXIT_FAILURE, "%s: snmp_info is not initialized", __func__); + } + + if (snmp_info[0].info_type == NULL) { + upsdebugx(1, "%s: WARNING: snmp_info is empty", __func__); + } + + for (su_info_p = &snmp_info[0]; (su_info_p != NULL && su_info_p->info_type != NULL) ; su_info_p++) { if (!strcasecmp(su_info_p->info_type, "input.transfer.low")) { su_info_p->flags &= ~SU_FLAG_OK; continue; @@ -703,23 +1568,121 @@ static void disable_transfer_oids(void) } } -/* universal function to add or update info element. */ +/* Universal function to add or update info element. + * If value is NULL, use the default one (su_info_p->dfl) if provided */ void su_setinfo(snmp_info_t *su_info_p, const char *value) { - upsdebugx(1, "entering su_setinfo(%s)", su_info_p->info_type); + info_lkp_t *info_lkp; + char info_type[128]; /* We tweak incoming "su_info_p->info_type" value in some cases */ + +/* FIXME: Replace hardcoded 128 with a macro above (use {SU_}LARGEBUF?), + * and same macro or sizeof(info_type) below (also more 128 cases below)? + */ + + upsdebugx(1, "entering %s(%s, %s)", __func__, su_info_p->info_type, (value)?value:""); + +/* FIXME: This 20 seems very wrong (should be "128", macro or sizeof? see above) */ + memset(info_type, 0, 20); + /* pre-fill with the device name for checking */ + snprintf(info_type, 128, "device.%i", current_device_number); + + /* Daisy-chain template magic should only apply to defaulted + * entries (oid==null) or templated entries (contains .%i); + * however at this point we see exact OIDs handed down from + * su_ups_get() which instantiates a template (if needed - + * and knows it was a template) and calls su_setinfo(). + * NOTE: For setting the values (or commands) from clients + * like `upsrw`, see su_setOID() method. Here we change our + * device state records based on readings from a device. + */ + if ((daisychain_enabled == TRUE) && (devices_count > 1)) { + if (su_info_p->OID != NULL + && strstr(su_info_p->OID, ".%i") != NULL + ) { + /* Only inform, do not react so far, + * need more understanding if and when + * such situation might happen at all: + */ + upsdebugx(5, "%s: in a daisy-chained device, " + "got a templated OID %s for type %s", + __func__, su_info_p->OID, + su_info_p->info_type); + } + + /* Only append "device.X" for master and slaves, if not already done! */ + if ((current_device_number > 0) && (strstr(su_info_p->info_type, info_type) == NULL)) { + /* Special case: we remove "device" from the device collection not to + * get "device.X.device.", but "device.X." */ + if (!strncmp(su_info_p->info_type, "device.", 7)) { + upsdebugx(6, "%s: in a daisy-chained device, " + "OID %s: TRIM 'device.' from type %s (value %s)", + __func__, su_info_p->OID, + su_info_p->info_type, (value)?value:""); + snprintf(info_type, 128, "device.%i.%s", + current_device_number, su_info_p->info_type + 7); + } + else { + upsdebugx(6, "%s: in a daisy-chained device, " + "OID %s is templated: for type %s (value %s)", + __func__, su_info_p->OID, + su_info_p->info_type, (value)?value:""); + snprintf(info_type, 128, "device.%i.%s", + current_device_number, su_info_p->info_type); + } + } + else { + upsdebugx(6, "%s: in a daisy-chained device, " + "OID %s: for type %s (value %s) " + "device %d is not positive or type already " + "contains the prepared expectation: %s", + __func__, su_info_p->OID, + su_info_p->info_type, (value)?value:"", + current_device_number, + info_type + ); + snprintf(info_type, 128, "%s", su_info_p->info_type); + } + } + else { + upsdebugx(6, "%s: NOT in a daisy-chained device", __func__); + snprintf(info_type, 128, "%s", su_info_p->info_type); + } + + upsdebugx(1, "%s: using info_type '%s'", __func__, info_type); if (SU_TYPE(su_info_p) == SU_TYPE_CMD) return; - if (strcasecmp(su_info_p->info_type, "ups.status")) + /* ups.status and {ups, Lx, outlet, outlet.group}.alarm have special + * handling, not here! */ + if ((strcasecmp(su_info_p->info_type, "ups.status")) + && (strcasecmp(strrchr(su_info_p->info_type, '.'), ".alarm"))) { if (value != NULL) - dstate_setinfo(su_info_p->info_type, "%s", value); - else - dstate_setinfo(su_info_p->info_type, "%s", su_info_p->dfl); + dstate_setinfo(info_type, "%s", value); + else if (su_info_p->dfl != NULL) + dstate_setinfo(info_type, "%s", su_info_p->dfl); + else { + upsdebugx(3, "%s: no value nor default provided, aborting...", __func__); + return; + } - dstate_setflags(su_info_p->info_type, su_info_p->info_flags); - dstate_setaux(su_info_p->info_type, su_info_p->info_len); + dstate_setflags(info_type, su_info_p->info_flags); + dstate_setaux(info_type, su_info_p->info_len); + + /* Set enumerated values, only if the data has ST_FLAG_RW and there + * are lookup values */ +/* FIXME: daisychain settings support: check if applicable */ + if ((su_info_p->info_flags & ST_FLAG_RW) && su_info_p->oid2info) { + + upsdebugx(3, "%s: adding enumerated values", __func__); + + /* Loop on all existing values */ + for (info_lkp = su_info_p->oid2info; info_lkp != NULL + && info_lkp->info_value != NULL; info_lkp++) { + dstate_addenum(info_type, "%s", info_lkp->info_value); + } + } /* Commit the current value, to avoid staleness with huge * data collections on slow devices */ @@ -731,35 +1694,167 @@ void su_status_set(snmp_info_t *su_info_p, long value) { const char *info_value = NULL; - upsdebugx(2, "SNMP UPS driver : entering su_status_set()"); + upsdebugx(2, "SNMP UPS driver: entering %s()", __func__); - if ((info_value = su_find_infoval(su_info_p->oid2info, value)) != NULL) + if ((info_value = su_find_infoval(su_info_p->oid2info, &value)) != NULL) { - if (strcmp(info_value, "")) { + if (info_value[0] != '\0') { status_set(info_value); } } /* TODO: else */ } +void su_alarm_set(snmp_info_t *su_info_p, long value) +{ + const char *info_value = NULL; + const char *info_type = NULL; + char alarm_info_value[SU_LARGEBUF]; + /* number of the outlet or phase */ + int item_number = -1; + + upsdebugx(2, "SNMP UPS driver: entering %s(%s)", __func__, su_info_p->info_type); + + /* daisychain handling + * extract the template part to get the relevant 'info_type' part + * ex: device.6.L1.alarm => L1.alarm + * ex: device.6.outlet.1.alarm => outlet.1.alarm */ + if (!strncmp(su_info_p->info_type, "device.", 7)) { + info_type = strchr(su_info_p->info_type + 7, '.') + 1; + } + else + info_type = su_info_p->info_type; + + upsdebugx(2, "%s: using definition %s", __func__, info_type); + + if ((info_value = su_find_infoval(su_info_p->oid2info, &value)) != NULL + && info_value[0] != 0) + { + /* Special handling for outlet & outlet groups alarms */ + if ((su_info_p->flags & SU_OUTLET) + || (su_info_p->flags & SU_OUTLET_GROUP)) { + /* Extract template number */ + item_number = extract_template_number(su_info_p->flags, info_type); + + upsdebugx(2, "%s: appending %s %i", __func__, + (su_info_p->flags & SU_OUTLET_GROUP) ? "outlet group" : "outlet", item_number); + + /* Inject in the alarm string */ + snprintf(alarm_info_value, sizeof(alarm_info_value), + "outlet%s %i %s", (su_info_p->flags & SU_OUTLET_GROUP) ? " group" : "", + item_number, info_value); + info_value = &alarm_info_value[0]; + } + /* Special handling for phase alarms + * Note that SU_*PHASE flags are cleared, so match the 'Lx' + * start of path */ + if (info_type[0] == 'L') { + /* Extract phase number */ + item_number = atoi(info_type+1); + char alarm_info_value_more[SU_LARGEBUF + 32]; /* can sprintf() SU_LARGEBUF plus markup into here */ + + upsdebugx(2, "%s: appending phase L%i", __func__, item_number); + + /* Inject in the alarm string */ + snprintf(alarm_info_value_more, sizeof(alarm_info_value_more), + "phase L%i %s", item_number, info_value); + info_value = &alarm_info_value_more[0]; + } + + /* Set the alarm value */ + alarm_set(info_value); + } + /* TODO: else */ +} + /* find info element definition in my info array. */ snmp_info_t *su_find_info(const char *type) { snmp_info_t *su_info_p; - for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++) + if (snmp_info == NULL) { + fatalx(EXIT_FAILURE, "%s: snmp_info is not initialized", __func__); + } + + if (snmp_info[0].info_type == NULL) { + upsdebugx(1, "%s: WARNING: snmp_info is empty", __func__); + } + + for (su_info_p = &snmp_info[0]; (su_info_p != NULL && su_info_p->info_type != NULL) ; su_info_p++) if (!strcasecmp(su_info_p->info_type, type)) { - upsdebugx(3, "su_find_info: \"%s\" found", type); + upsdebugx(3, "%s: \"%s\" found", __func__, type); return su_info_p; } - upsdebugx(3, "su_find_info: unknown info type (%s)", type); + upsdebugx(3, "%s: unknown info type (%s)", __func__, type); return NULL; } +/* Counter match the sysOID using {device,ups}.model OID + * Return TRUE if this OID can be retrieved, FALSE otherwise */ +static bool_t match_model_OID() +{ + bool_t retCode = FALSE; + snmp_info_t *su_info_p, *cur_info_p; + char testOID_buf[LARGEBUF]; + + /* Try to get device.model first */ + su_info_p = su_find_info("device.model"); + /* Otherwise, try to get ups.model */ + if (su_info_p == NULL) + su_info_p = su_find_info("ups.model"); + + if (su_info_p != NULL) { + /* Daisychain specific: we may have a template (including formatting + * string) that needs to be adapted! */ + if (strchr(su_info_p->OID, '%') != NULL) + { + upsdebugx(2, "Found template, need to be adapted"); + cur_info_p = (snmp_info_t *)malloc(sizeof(snmp_info_t)); + cur_info_p->info_type = (char *)xmalloc(SU_INFOSIZE); + cur_info_p->OID = (char *)xmalloc(SU_INFOSIZE); + snprintf((char*)cur_info_p->info_type, SU_INFOSIZE, "%s", su_info_p->info_type); + /* Use the daisychain master (0) / 1rst device index */ +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf((char*)cur_info_p->OID, SU_INFOSIZE, su_info_p->OID, 0); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + } + else { + upsdebugx(2, "Found entry, not a template %s", su_info_p->OID); + /* Otherwise, just point at what we found */ + cur_info_p = su_info_p; + } + + upsdebugx(2, "Testing %s using OID %s", cur_info_p->info_type, cur_info_p->OID); + retCode = nut_snmp_get_str(cur_info_p->OID, testOID_buf, LARGEBUF, NULL); + + /* Free our malloc, if it was dynamic */ + if (strchr(su_info_p->OID, '%') != NULL) { + if (cur_info_p->info_type != NULL) + free((char*)cur_info_p->info_type); + if (cur_info_p->OID != NULL) + free((char*)cur_info_p->OID); + if (cur_info_p != NULL) + free((char*)cur_info_p); + } + } + + return retCode; +} + /* Try to find the MIB using sysOID matching. * Return a pointer to a mib2nut definition if found, NULL otherwise */ -mib2nut_info_t *match_sysoid() +static mib2nut_info_t *match_sysoid() { char sysOID_buf[LARGEBUF]; oid device_sysOID[MAX_OID_LEN]; @@ -769,50 +1864,78 @@ mib2nut_info_t *match_sysoid() int i; /* Retrieve sysOID value of this device */ - if (nut_snmp_get_str(SYSOID_OID, sysOID_buf, sizeof(sysOID_buf), NULL)) + if (nut_snmp_get_oid(SYSOID_OID, sysOID_buf, sizeof(sysOID_buf)) == TRUE) { - upsdebugx(1, "match_sysoid: device sysOID value = %s", sysOID_buf); + upsdebugx(1, "%s: device sysOID value = %s", __func__, sysOID_buf); /* Build OIDs for comparison */ if (!read_objid(sysOID_buf, device_sysOID, &device_sysOID_len)) { - upsdebugx(2, "match_sysoid: can't build device_sysOID %s: %s", - sysOID_buf, snmp_api_errstring(snmp_errno)); + upsdebugx(2, "%s: can't build device_sysOID %s: %s", + __func__, sysOID_buf, snmp_api_errstring(snmp_errno)); - return FALSE; + return NULL; } /* Now, iterate on mib2nut definitions */ for (i = 0; mib2nut[i] != NULL; i++) { - upsdebugx(1, "match_sysoid: checking MIB %s", mib2nut[i]->mib_name); + upsdebugx(1, "%s: checking MIB %s", __func__, mib2nut[i]->mib_name); if (mib2nut[i]->sysOID == NULL) continue; /* Clear variables */ - memset(mib2nut_sysOID, 0, MAX_OID_LEN); + memset(mib2nut_sysOID, 0, sizeof(mib2nut_sysOID)); mib2nut_sysOID_len = MAX_OID_LEN; if (!read_objid(mib2nut[i]->sysOID, mib2nut_sysOID, &mib2nut_sysOID_len)) { - upsdebugx(2, "match_sysoid: can't build OID %s: %s", - sysOID_buf, snmp_api_errstring(snmp_errno)); + upsdebugx(2, "%s: can't build OID %s: %s", + __func__, sysOID_buf, snmp_api_errstring(snmp_errno)); /* Try to continue anyway! */ continue; } + /* Now compare these */ - upsdebugx(1, "match_sysoid: comparing %s with %s", sysOID_buf, mib2nut[i]->sysOID); + upsdebugx(1, "%s: comparing %s with %s", __func__, sysOID_buf, mib2nut[i]->sysOID); if (!netsnmp_oid_equals(device_sysOID, device_sysOID_len, mib2nut_sysOID, mib2nut_sysOID_len)) { - upsdebugx(2, "match_sysoid: sysOID matches MIB '%s'!", mib2nut[i]->mib_name); + upsdebugx(2, "%s: sysOID matches MIB '%s'!", __func__, mib2nut[i]->mib_name); + /* Counter verify, using {ups,device}.model */ + snmp_info = mib2nut[i]->snmp_info; + + if (snmp_info == NULL) { + upsdebugx(0, "%s: WARNING: snmp_info is not initialized " + "for mapping table entry #%d \"%s\"", + __func__, i, mib2nut[i]->mib_name + ); + continue; + } + else if (snmp_info[0].info_type == NULL) { + upsdebugx(1, "%s: WARNING: snmp_info is empty " + "for mapping table entry #%d \"%s\"", + __func__, i, mib2nut[i]->mib_name); + } + + if (match_model_OID() != TRUE) + { + upsdebugx(2, "%s: testOID provided and doesn't match MIB '%s'!", __func__, mib2nut[i]->mib_name); + snmp_info = NULL; + continue; + } + else + upsdebugx(2, "%s: testOID provided and matches MIB '%s'!", __func__, mib2nut[i]->mib_name); + return mib2nut[i]; } } + /* Yell all to call for user report */ - upslogx(LOG_ERR, "No matching MIB found for sysOID '%s'! " \ - "Please report it to NUT developers, with the 'mib' paramater for your devices", + upslogx(LOG_ERR, "No matching MIB found for sysOID '%s'!\n" \ + "Please report it to NUT developers, with an 'upsc' output for your device.\n" \ + "Going back to the classic MIB detection method.", sysOID_buf); } else @@ -825,18 +1948,39 @@ mib2nut_info_t *match_sysoid() bool_t load_mib2nut(const char *mib) { int i; - char buf[LARGEBUF]; mib2nut_info_t *m2n = NULL; + /* Below we have many checks for "auto"; avoid redundant string walks: */ + bool_t mibIsAuto = (0 == strcmp(mib, "auto")); + bool_t mibSeen = FALSE; /* Did we see the MIB name while walking mib2nut[]? */ - upsdebugx(2, "SNMP UPS driver : entering load_mib2nut(%s)", mib); + upsdebugx(1, "SNMP UPS driver: entering %s(%s) to detect " + "proper MIB for device [%s] (host %s)", + __func__, mib, + upsname ? upsname : device_name, + device_path /* the "port" from config section is hostname/IP for networked drivers */ + ); /* First, try to match against sysOID, if no MIB was provided. * This should speed up init stage * (Note: sysOID points the device main MIB entry point) */ - if (!strcmp(mib, "auto")) + if (mibIsAuto) { - upsdebugx(1, "trying the new match_sysoid() method"); - m2n = match_sysoid(); + upsdebugx(2, "%s: trying the new match_sysoid() method with %s", + __func__, mib); + /* Retry at most 3 times, to maximise chances */ + for (i = 0; i < 3 ; i++) { + upsdebugx(3, "%s: trying the new match_sysoid() method: attempt #%d", + __func__, (i+1)); + if ((m2n = match_sysoid()) != NULL) + break; + + if (m2n == NULL) + upsdebugx(3, "%s: failed with new match_sysoid() method", + __func__); + else + upsdebugx(3, "%s: found something with new match_sysoid() method", + __func__); + } } /* Otherwise, revert to the classic method */ @@ -844,15 +1988,52 @@ bool_t load_mib2nut(const char *mib) { for (i = 0; mib2nut[i] != NULL; i++) { /* Is there already a MIB name provided? */ - if (strcmp(mib, "auto") && strcmp(mib, mib2nut[i]->mib_name)) { + upsdebugx(4, "%s: checking against mapping table entry #%d \"%s\"", + __func__, i, mib2nut[i]->mib_name); + if (!mibIsAuto && strcmp(mib, mib2nut[i]->mib_name)) { + /* "mib" is neither "auto" nor the name in mapping table */ + upsdebugx(2, "%s: skip the \"%s\" entry which " + "is neither \"auto\" nor a valid name in the mapping table", + __func__, mib); continue; } - upsdebugx(1, "load_mib2nut: trying classic method with '%s' mib", mib2nut[i]->mib_name); + upsdebugx(2, "%s: trying classic sysOID matching method with '%s' mib", + __func__, mib2nut[i]->mib_name); /* Classic method: test an OID specific to this MIB */ - if (!nut_snmp_get_str(mib2nut[i]->oid_auto_check, buf, sizeof(buf), NULL)) { + snmp_info = mib2nut[i]->snmp_info; + + if (snmp_info == NULL) { + upsdebugx(0, "%s: WARNING: snmp_info is not initialized " + "for mapping table entry #%d \"%s\"", + __func__, i, mib2nut[i]->mib_name + ); continue; } + else if (snmp_info[0].info_type == NULL) { + upsdebugx(1, "%s: WARNING: snmp_info is empty " + "for mapping table entry #%d \"%s\"", + __func__, i, mib2nut[i]->mib_name); + } + + /* Device might not support this MIB, but we want to + * track that the name string is valid for diags below + */ + if (!mibIsAuto) { + mibSeen = TRUE; + } + + if (match_model_OID() != TRUE) + { + upsdebugx(3, "%s: testOID provided and doesn't match MIB '%s'!", + __func__, mib2nut[i]->mib_name); + snmp_info = NULL; + continue; + } + else + upsdebugx(3, "%s: testOID provided and matches MIB '%s'!", + __func__, mib2nut[i]->mib_name); + /* MIB found */ m2n = mib2nut[i]; break; @@ -866,20 +2047,36 @@ bool_t load_mib2nut(const char *mib) OID_pwr_status = m2n->oid_pwr_status; mibname = m2n->mib_name; mibvers = m2n->mib_version; - upsdebugx(1, "load_mib2nut: using %s mib", mibname); + alarms_info = m2n->alarms_info; + upsdebugx(1, "%s: using %s MIB for device [%s] (host %s)", + __func__, mibname, + upsname ? upsname : device_name, device_path); return TRUE; } /* Did we find something or is it really an unknown mib */ - if (strcmp(mib, "auto") != 0) { - fatalx(EXIT_FAILURE, "Unknown mibs value: %s", mib); + if (!mibIsAuto) { + if (mibSeen) { + fatalx(EXIT_FAILURE, "Requested 'mibs' value '%s' " + "did not match this device [%s] (host %s)", + mib, upsname ? upsname : device_name, device_path); + } else { + /* String not seen during mib2nut[] walk - + * and if we had no hits, we walked it all + */ + fatalx(EXIT_FAILURE, "Unknown 'mibs' value: %s", mib); + } } else { - fatalx(EXIT_FAILURE, "No supported device detected"); + fatalx(EXIT_FAILURE, "No supported device detected at [%s] (host %s)", + upsname ? upsname : device_name, device_path); } + + /* Should not get here thanks to fatalx() above, but need to silence a warning */ + return FALSE; } /* find the OID value matching that INFO_* value */ -long su_find_valinfo(info_lkp_t *oid2info, char* value) +long su_find_valinfo(info_lkp_t *oid2info, const char* value) { info_lkp_t *info_lkp; @@ -887,79 +2084,189 @@ long su_find_valinfo(info_lkp_t *oid2info, char* value) (strcmp(info_lkp->info_value, "NULL")); info_lkp++) { if (!(strcmp(info_lkp->info_value, value))) { - upsdebugx(1, "su_find_valinfo: found %s (value: %s)", - info_lkp->info_value, value); + upsdebugx(1, "%s: found %s (value: %s)", + __func__, info_lkp->info_value, value); return info_lkp->oid_value; } } - upsdebugx(1, "su_find_valinfo: no matching INFO_* value for this OID value (%s)", value); + upsdebugx(1, "%s: no matching INFO_* value for this OID value (%s)", __func__, value); return -1; } -/* find the INFO_* value matching that OID value */ -const char *su_find_infoval(info_lkp_t *oid2info, long value) +/* String reformating function */ +const char *su_find_strval(info_lkp_t *oid2info, void *value) +{ +#if WITH_SNMP_LKP_FUN + /* First test if we have a generic lookup function */ + if ( (oid2info != NULL) && (oid2info->fun_vp2s != NULL) ) { + upsdebugx(2, "%s: using generic lookup function (string reformatting)", __func__); + const char * retvalue = oid2info->fun_vp2s(value); + upsdebugx(2, "%s: got value '%s'", __func__, retvalue); + return retvalue; + } + upsdebugx(1, "%s: no result value for this OID string value (%s)", __func__, (char*)value); +#else + NUT_UNUSED_VARIABLE(oid2info); + upsdebugx(1, "%s: no mapping function for this OID string value (%s)", __func__, (char*)value); +#endif // WITH_SNMP_LKP_FUN + return NULL; +} + +/* find the INFO_* value matching that OID numeric (long) value */ +const char *su_find_infoval(info_lkp_t *oid2info, void *raw_value) { info_lkp_t *info_lkp; + long value = *((long *)raw_value); +#if WITH_SNMP_LKP_FUN + /* First test if we have a generic lookup function */ + if ( (oid2info != NULL) && (oid2info->fun_vp2s != NULL) ) { + upsdebugx(2, "%s: using generic lookup function", __func__); + const char * retvalue = oid2info->fun_vp2s(raw_value); + upsdebugx(2, "%s: got value '%s'", __func__, retvalue); + return retvalue; + } +#endif // WITH_SNMP_LKP_FUN + + /* Otherwise, use the simple values mapping */ for (info_lkp = oid2info; (info_lkp != NULL) && - (strcmp(info_lkp->info_value, "NULL")); info_lkp++) { + (info_lkp->info_value != NULL) && (strcmp(info_lkp->info_value, "NULL")); info_lkp++) { if (info_lkp->oid_value == value) { - upsdebugx(1, "su_find_infoval: found %s (value: %ld)", - info_lkp->info_value, value); + upsdebugx(1, "%s: found %s (value: %ld)", + __func__, info_lkp->info_value, value); return info_lkp->info_value; } } - upsdebugx(1, "su_find_infoval: no matching INFO_* value for this OID value (%ld)", value); + upsdebugx(1, "%s: no matching INFO_* value for this OID value (%ld)", __func__, value); return NULL; } +/* FIXME: doesn't work with templates! */ static void disable_competition(snmp_info_t *entry) { snmp_info_t *p; - for(p=snmp_info; p->info_type!=NULL; p++) { + if (snmp_info == NULL) { + fatalx(EXIT_FAILURE, "%s: snmp_info is not initialized", __func__); + } + + if (snmp_info[0].info_type == NULL) { + upsdebugx(1, "%s: WARNING: snmp_info is empty", __func__); + } + + for(p = snmp_info; (p != NULL && p->info_type != NULL) ; p++) { if(p!=entry && !strcmp(p->info_type, entry->info_type)) { - upsdebugx(2, "disable_competition: disabling %s %s", - p->info_type, p->OID); + upsdebugx(2, "%s: disabling %s %s", + __func__, p->info_type, p->OID); p->flags &= ~SU_FLAG_OK; } } } -/* instantiate an snmp_info_t from a template. - * mostly (only?) useful for outlet templates. - * Note: remember to adapt info_type, OID and optionaly dfl */ -snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *new_instance) +/* set shutdown and/or start delays */ +void set_delays(void) { + int ondelay, offdelay; + char su_scratch_buf[255]; + + if (getval(SU_VAR_ONDELAY)) + ondelay = atoi(getval(SU_VAR_ONDELAY)); + else + ondelay = -1; + + if (getval(SU_VAR_OFFDELAY)) + offdelay = atoi(getval(SU_VAR_OFFDELAY)); + else + offdelay = -1; + + if (ondelay >= 0) { + sprintf(su_scratch_buf, "%d", ondelay); + su_setvar("ups.delay.start", su_scratch_buf); + } + + if (offdelay >= 0) { + sprintf(su_scratch_buf, "%d", offdelay); + su_setvar("ups.delay.shutdown", su_scratch_buf); + } +} + +/*********************************************************************** + * Template handling functions + **********************************************************************/ + +/* Test if the template is a multiple one, i.e. with a formatting string that + * contains multiple "%i". + * Return TRUE if yes (multiple "%i" found), FALSE otherwise */ +static bool_t is_multiple_template(const char *OID_template) +{ + bool_t retCode = FALSE; + char *format_char = NULL; + + if (OID_template) { + format_char = strchr(OID_template, '%'); + upsdebugx(4, "%s(%s)", __func__, OID_template); + } + else + upsdebugx(4, "%s(NULL)", __func__); + + if (format_char != NULL) { + if (strchr(format_char + 1, '%') != NULL) { + retCode = TRUE; + } + } + + upsdebugx(4, "%s: has %smultiple template definition", + __func__, (retCode == FALSE)?"not ":""); + + return retCode; +} + +/* Instantiate an snmp_info_t from a template. + * Useful for device, outlet, outlet.group and ambient templates. + * Note: remember to adapt info_type, OID and optionaly dfl */ +static snmp_info_t *instantiate_info(snmp_info_t *info_template, snmp_info_t *new_instance) +{ + upsdebugx(1, "%s(%s)", __func__, + info_template ? info_template->info_type : "n/a"); + /* sanity check */ if (info_template == NULL) return NULL; if (new_instance == NULL) new_instance = (snmp_info_t *)xmalloc(sizeof(snmp_info_t)); + /* TOTHINK: Should there be an "else" to free() + * the fields which we (re-)allocate below? */ new_instance->info_type = (char *)xmalloc(SU_INFOSIZE); - if (info_template->OID != NULL) + if (new_instance->info_type) + memset((char *)new_instance->info_type, 0, SU_INFOSIZE); + if (info_template->OID != NULL) { new_instance->OID = (char *)xmalloc(SU_INFOSIZE); - else + if (new_instance->OID) + memset((char *)new_instance->OID, 0, SU_INFOSIZE); + } + else { new_instance->OID = NULL; + } + new_instance->info_flags = info_template->info_flags; new_instance->info_len = info_template->info_len; /* FIXME: check if we need to adapt this one... */ new_instance->dfl = info_template->dfl; new_instance->flags = info_template->flags; new_instance->oid2info = info_template->oid2info; - new_instance->setvar = info_template->setvar; + upsdebugx(2, "instantiate_info: template instantiated"); return new_instance; } -/* free a dynamically allocated snmp_info_t. - * mostly (only?) useful for outlet templates */ -void free_info(snmp_info_t *su_info_p) +/* Free a dynamically allocated snmp_info_t. + * Useful for outlet and outlet.group templates */ +static void free_info(snmp_info_t *su_info_p) { /* sanity check */ if (su_info_p == NULL) @@ -974,54 +2281,495 @@ void free_info(snmp_info_t *su_info_p) free (su_info_p); } -/* return the base SNMP index (0 or 1) to start outlet iteration on the MIB, - * based on a test using a template OID */ -int base_snmp_outlet_index(const char *OID_template) +/* return the base SNMP index (0 or 1) to start template iteration on + * the MIB, based on a test using a template OID */ +static int base_snmp_template_index(const snmp_info_t *su_info_p) { - int base_index = outlet_index_base; - char test_OID[SU_INFOSIZE]; + if (!su_info_p) + return -1; - if (outlet_index_base == -1) + int base_index = -1; + char test_OID[SU_INFOSIZE]; + snmp_info_flags_t template_type = get_template_type(su_info_p->info_type); + + if (!su_info_p->OID) + return base_index; + + upsdebugx(3, "%s: OID template = %s", __func__, su_info_p->OID); + + /* Try to differentiate between template types which may have + * different indexes ; and store it to not redo it again */ + switch (template_type) { + case SU_OUTLET: + template_index_base = outlet_template_index_base; + break; + case SU_OUTLET_GROUP: + template_index_base = outletgroup_template_index_base; + break; + case SU_DAISY: + template_index_base = device_template_index_base; + break; + case SU_AMBIENT_TEMPLATE: + template_index_base = ambient_template_index_base; + break; + default: + /* we should never fall here! */ + upsdebugx(3, "%s: unknown template type '%" PRI_SU_FLAGS "' for %s", + __func__, template_type, su_info_p->info_type); + } + base_index = template_index_base; + + if (template_index_base == -1) { /* not initialised yet */ for (base_index = 0 ; base_index < 2 ; base_index++) { - sprintf(test_OID, OID_template, base_index); - if (nut_snmp_get(test_OID) != NULL) - break; +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + /* Test if this template also includes daisychain, in which case + * we just use the current device index */ + if (is_multiple_template(su_info_p->OID) == TRUE) { + if (su_info_p->flags & SU_TYPE_DAISY_1) { + snprintf(test_OID, sizeof(test_OID), su_info_p->OID, + current_device_number + device_template_offset, base_index); + } + else { + snprintf(test_OID, sizeof(test_OID), su_info_p->OID, + base_index, current_device_number + device_template_offset); + } + } + else { + snprintf(test_OID, sizeof(test_OID), su_info_p->OID, base_index); + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + if (nut_snmp_get(test_OID) != NULL) { + if (su_info_p->flags & SU_FLAG_ZEROINVALID) { + long value; + if ((nut_snmp_get_int(test_OID, &value)) && (value!=0)) { + break; + } + } + else if (su_info_p->flags & SU_FLAG_NAINVALID) { + char value[SU_BUFSIZE]; + if ((nut_snmp_get_str(test_OID, value, SU_BUFSIZE, NULL)) + && (strncmp(value, "N/A", 3))) { + break; + } + } + else { + break; + } + } } - outlet_index_base = base_index; + /* Only store if it's a template for outlets or outlets groups, + * not for daisychain (which has different index) */ + if (su_info_p->flags & SU_OUTLET) + outlet_template_index_base = base_index; + else if (su_info_p->flags & SU_OUTLET_GROUP) + outletgroup_template_index_base = base_index; + else if (su_info_p->flags & SU_AMBIENT_TEMPLATE) + ambient_template_index_base = base_index; + else + device_template_index_base = base_index; } - upsdebugx(3, "base_snmp_outlet_index: %i", outlet_index_base); + upsdebugx(3, "%s: template_index_base = %i", __func__, base_index); return base_index; } -/* return the NUT offset (increment) based on outlet_index_base - * ie (outlet_index_base == 0) => increment +1 - * (outlet_index_base == 1) => increment +0 */ -int base_nut_outlet_offset(void) +/* Try to determine the number of items (outlets, outlet groups, ...), + * using a template definition. Walk through the template until we can't + * get anymore values. I.e., if we can iterate up to 8 item, return 8 */ +static int guestimate_template_count(snmp_info_t *su_info_p) { - return (outlet_index_base==0)?1:0; + int base_index = 0; + char test_OID[SU_INFOSIZE]; + int base_count; + const char *OID_template = su_info_p->OID; + + upsdebugx(1, "%s(%s)", __func__, OID_template); + + /* Determine if OID index starts from 0 or 1? */ +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(test_OID, sizeof(test_OID), OID_template, base_index); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + if (nut_snmp_get(test_OID) == NULL) { + base_index++; + } + else { + if (su_info_p->flags & SU_FLAG_ZEROINVALID) { + long value; + if ((nut_snmp_get_int(test_OID, &value)) && (value==0)) { + base_index++; + } + } + } + + /* Now, actually iterate */ + for (base_count = 0 ; ; base_count++) { +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf(test_OID, sizeof(test_OID), OID_template, base_index + base_count); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + if (nut_snmp_get(test_OID) == NULL) + break; + } + + upsdebugx(3, "%s: %i", __func__, base_count); + return base_count; } +/* Process template definition, instantiate and get data or register + * command + * type: outlet, outlet.group, device */ +static bool_t process_template(int mode, const char* type, snmp_info_t *su_info_p) +{ + /* Default to TRUE, and leave to get_and_process_data() to set + * to FALSE when actually getting data from devices, to avoid false + * negative with server side data */ + bool_t status = TRUE; + int cur_template_number = 1; + int cur_nut_index = 0; + int template_count = 0; + int base_snmp_index = 0; + snmp_info_t cur_info_p; + char template_count_var[SU_BUFSIZE * 2]; + /* Needed *2 to fit a max size_t in snprintf() below, + * even if that should never happen */ + char tmp_buf[SU_INFOSIZE]; + + upsdebugx(1, "%s template definition found (%s)...", type, su_info_p->info_type); + + if ((strncmp(type, "device", 6)) && (devices_count > 1) && (current_device_number > 0)) { + snprintf(template_count_var, sizeof(template_count_var), "device.%i.%s.count", current_device_number, type); + } else { + snprintf(template_count_var, sizeof(template_count_var), "%s.count", type); + } + + if(dstate_getinfo(template_count_var) == NULL) { + /* FIXME: should we disable it? + * su_info_p->flags &= ~SU_FLAG_OK; + * or rely on guestimation? */ + template_count = guestimate_template_count(su_info_p); + /* Publish the count estimation */ + if (template_count > 0) { + dstate_setinfo(template_count_var, "%i", template_count); + } + } + else { + template_count = atoi(dstate_getinfo(template_count_var)); + } + upsdebugx(1, "%i instances found...", template_count); + + /* Only instantiate templates if needed! */ + if (template_count > 0) { + /* general init of data using the template */ + instantiate_info(su_info_p, &cur_info_p); + + base_snmp_index = base_snmp_template_index(su_info_p); + + for (cur_template_number = base_snmp_index ; + cur_template_number < (template_count + base_snmp_index) ; + cur_template_number++) + { + upsdebugx(1, "Processing instance %i/%i...", cur_template_number, template_count); + /* Special processing for daisychain: + * append 'device.x' to the NUT variable name, except for the + * whole daisychain ("device.0") */ + if (!strncmp(type, "device", 6)) + { + /* Device(s) 1-N (master + slave(s)) need to append 'device.x' */ + if (current_device_number > 0) { + char *ptr = NULL; + /* Another special processing for daisychain + * device collection needs special appending */ + if (!strncmp(su_info_p->info_type, "device.", 7)) + ptr = (char*)&su_info_p->info_type[7]; + else + ptr = (char*)su_info_p->info_type; + + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + "device.%i.%s", current_device_number, ptr); + } + else + { + /* Device 1 ("device.0", whole daisychain) needs no + * special processing */ + cur_nut_index = cur_template_number; +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + su_info_p->info_type, cur_nut_index); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + } + } + else if (!strncmp(type, "outlet", 6)) /* Outlet and outlet groups templates */ + { + /* Get the index of the current template instance */ + cur_nut_index = cur_template_number; + +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + /* Special processing for daisychain */ + if (daisychain_enabled == TRUE) { + /* Device(s) 1-N (master + slave(s)) need to append 'device.x' */ + if ((devices_count > 1) && (current_device_number > 0)) { + memset(&tmp_buf[0], 0, SU_INFOSIZE); + strcat(&tmp_buf[0], "device.%i."); + strcat(&tmp_buf[0], su_info_p->info_type); + + upsdebugx(4, "FORMATTING STRING = %s", &tmp_buf[0]); + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + &tmp_buf[0], current_device_number, cur_nut_index); + } + else { + /* FIXME: daisychain-whole, what to do? */ + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + su_info_p->info_type, cur_nut_index); + } + } + else { + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + su_info_p->info_type, cur_nut_index); + } + } + else if (!strncmp(type, "ambient", 7)) + { + /* FIXME: can be grouped with outlet* above */ + /* Get the index of the current template instance */ + cur_nut_index = cur_template_number; + + /* Special processing for daisychain */ + if (daisychain_enabled == TRUE) { + /* Only publish on the daisychain host */ + if ( (su_info_p->flags & SU_TYPE_DAISY_MASTER_ONLY) + && (current_device_number != 1) ) { + upsdebugx(2, "discarding variable due to daisychain master flag"); + continue; + } + + /* Device(s) 1-N (master + slave(s)) need to append 'device.x' */ + if ((devices_count > 1) && (current_device_number > 0)) { + memset(&tmp_buf[0], 0, SU_INFOSIZE); + strcat(&tmp_buf[0], "device.%i."); + strcat(&tmp_buf[0], su_info_p->info_type); + + upsdebugx(4, "FORMATTING STRING = %s", &tmp_buf[0]); + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + &tmp_buf[0], current_device_number, cur_nut_index); + } + else { + /* FIXME: daisychain-whole, what to do? */ + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + su_info_p->info_type, cur_nut_index); + } + } + else { + snprintf((char*)cur_info_p.info_type, SU_INFOSIZE, + su_info_p->info_type, cur_nut_index); + } + } + else + upsdebugx(4, "Error: unknown template type '%s", type); + + /* check if default value is also a template */ + if ((cur_info_p.dfl != NULL) && + (strstr(su_info_p->dfl, "%i") != NULL)) { + cur_info_p.dfl = (char *)xmalloc(SU_INFOSIZE); + snprintf((char *)cur_info_p.dfl, SU_INFOSIZE, su_info_p->dfl, cur_nut_index); + } + + if (cur_info_p.OID != NULL) { + /* Special processing for daisychain */ + if (!strncmp(type, "device", 6)) { + if (current_device_number > 0) { + snprintf((char *)cur_info_p.OID, SU_INFOSIZE, su_info_p->OID, current_device_number + device_template_offset); + } + /*else + * FIXME: daisychain-whole, what to do? + */ + } + else { + /* Special processing for daisychain: + * these outlet | outlet groups also include formatting info, + * so we have to check if the daisychain is enabled, and if + * the formatting info for it are in 1rst or 2nd position */ + if (daisychain_enabled == TRUE) { + if (su_info_p->flags & SU_TYPE_DAISY_1) { + snprintf((char *)cur_info_p.OID, SU_INFOSIZE, + su_info_p->OID, current_device_number + device_template_offset, cur_template_number); + } + else if (su_info_p->flags & SU_TYPE_DAISY_2) { + snprintf((char *)cur_info_p.OID, SU_INFOSIZE, + su_info_p->OID, cur_template_number + device_template_offset, + current_device_number - device_template_offset); + } + else { + /* Note: no device daisychain templating (SU_TYPE_DAISY_MASTER_ONLY)! */ + snprintf((char *)cur_info_p.OID, SU_INFOSIZE, su_info_p->OID, cur_template_number); + } + } + else { + snprintf((char *)cur_info_p.OID, SU_INFOSIZE, su_info_p->OID, cur_template_number); + } + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + /* add instant commands to the info database. */ + if (SU_TYPE(su_info_p) == SU_TYPE_CMD) { + upsdebugx(1, "Adding template command %s", cur_info_p.info_type); + /* FIXME: only add if "su_ups_get(cur_info_p) == TRUE" */ + if (mode == SU_WALKMODE_INIT) + dstate_addcmd(cur_info_p.info_type); + } + else /* get and process this data */ + status = get_and_process_data(mode, &cur_info_p); + } else { + /* server side (ABSENT) data */ + su_setinfo(&cur_info_p, NULL); + } + /* set back the flag */ + su_info_p->flags = cur_info_p.flags; + } + free((char*)cur_info_p.info_type); + if (cur_info_p.OID != NULL) + free((char*)cur_info_p.OID); + if ((cur_info_p.dfl != NULL) && + (strstr(su_info_p->dfl, "%i") != NULL)) + free((char*)cur_info_p.dfl); + } + else { + upsdebugx(1, "No %s present, discarding template definition...", type); + } + return status; +} + +/* Return the type of template, according to a variable name. + * Return: SU_OUTLET_GROUP, SU_OUTLET or 0 if not a template */ +snmp_info_flags_t get_template_type(const char* varname) +{ + if (!strncmp(varname, "outlet.group", 12)) { + upsdebugx(4, "outlet.group template"); + return SU_OUTLET_GROUP; + } + else if (!strncmp(varname, "outlet", 6)) { + upsdebugx(4, "outlet template"); + return SU_OUTLET; + } + else if (!strncmp(varname, "device", 6)) { + upsdebugx(4, "device template"); + return SU_DAISY; + } + else if (!strncmp(varname, "ambient", 7)) { + upsdebugx(4, "ambient template"); + return SU_AMBIENT_TEMPLATE; + } + else { + upsdebugx(2, "Unknown template type: %s", varname); + return 0; + } +} + +/* Extract the id number of an instantiated template. + * Example: return '1' for type = 'outlet.1.desc', -1 if unknown */ +int extract_template_number(snmp_info_flags_t template_type, const char* varname) +{ + const char* item_number_ptr = NULL; + int item_number = -1; + + if (template_type & SU_OUTLET_GROUP) + item_number_ptr = &varname[12]; + else if (template_type & SU_OUTLET) + item_number_ptr = &varname[6]; + else if (template_type & SU_DAISY) + item_number_ptr = &varname[6]; + else if (template_type & SU_AMBIENT_TEMPLATE) + item_number_ptr = &varname[7]; + else + return -1; + + item_number = atoi(++item_number_ptr); + upsdebugx(3, "%s: item %i", __func__, item_number); + return item_number; +} + +/* Extract the id number of a template from a variable name. + * Example: return '1' for type = 'outlet.1.desc' */ +static int extract_template_number_from_snmp_info_t(const char* varname) +{ + return extract_template_number(get_template_type(varname), varname); +} + +/* end of template functions */ + + /* process a single data from a walk */ bool_t get_and_process_data(int mode, snmp_info_t *su_info_p) { bool_t status = FALSE; - upsdebugx(1, "getting data: %s (%s)", su_info_p->info_type, su_info_p->OID); + upsdebugx(1, "%s: %s (%s)", __func__, + su_info_p->info_type, su_info_p->OID); /* ok, update this element. */ status = su_ups_get(su_info_p); + upsdebugx(4, "%s: su_ups_get returned %d", __func__, status); /* set stale flag if data is stale, clear if not. */ if (status == TRUE) { if (su_info_p->flags & SU_FLAG_STALE) { - upslogx(LOG_INFO, "[%s] snmp_ups_walk: data resumed for %s", - upsname?upsname:device_name, su_info_p->info_type); + upslogx(LOG_INFO, "[%s] %s: data resumed for %s", + upsname?upsname:device_name, __func__, su_info_p->info_type); su_info_p->flags &= ~SU_FLAG_STALE; } if(su_info_p->flags & SU_FLAG_UNIQUE) { /* We should be the only provider of this */ + upsdebugx(4, "%s: unique flag", __func__); disable_competition(su_info_p); su_info_p->flags &= ~SU_FLAG_UNIQUE; } @@ -1042,204 +2790,519 @@ bool_t get_and_process_data(int mode, snmp_info_t *su_info_p) return status; } +/*********************************************************************** + * Daisychain handling functions + **********************************************************************/ + +/*! + * Daisychained devices support init: + * Determine the number of device(s) and if daisychain support has to be enabled + * Set the values of devices_count (internal) and "device.count" (public) + * Return TRUE if daisychain support is enabled, FALSE otherwise */ +bool_t daisychain_init() +{ + snmp_info_t *su_info_p = NULL; + + upsdebugx(1, "Checking if daisychain support has to be enabled"); + + su_info_p = su_find_info("device.count"); + + if (su_info_p != NULL) + { + upsdebugx(1, "Found device.count entry..."); + + /* Enable daisychain if there is a device.count entry. + * This means that will have templates for entries */ + daisychain_enabled = TRUE; + + /* Try to get the OID value, if it's not a template */ + upsdebugx(3, "OID for device.count is %s", + su_info_p->OID ? su_info_p->OID : ""); + if ((su_info_p->OID != NULL) && + (strstr(su_info_p->OID, "%i") == NULL)) + { +#if WITH_SNMP_LKP_FUN + devices_count = -1; + /* First test if we have a generic lookup function + * FIXME: Check if the field type is a string? + */ + /* TODO: backport the 2x2 mapping function support + * and this would be "fun_s2l" in resulting codebase + */ + if ( (su_info_p->oid2info != NULL) && (su_info_p->oid2info->nuf_s2l != NULL) ) { + char buf[1024]; + upsdebugx(2, "%s: using generic string-to-long lookup function", __func__); + if (TRUE == nut_snmp_get_str(su_info_p->OID, buf, sizeof(buf), su_info_p->oid2info)) { + devices_count = su_info_p->oid2info->nuf_s2l(buf); + upsdebugx(2, "%s: got value '%ld'", __func__, devices_count); + } + } + + if (devices_count == -1) { +#endif /* WITH_SNMP_LKP_FUN */ + + if (nut_snmp_get_int(su_info_p->OID, &devices_count) == TRUE) + upsdebugx(1, "There are %ld device(s) present", devices_count); + else + { + upsdebugx(1, "Error: can't get the number of device(s) present!"); + upsdebugx(1, "Falling back to 1 device!"); + devices_count = 1; + } + +#if WITH_SNMP_LKP_FUN + } +#endif /* WITH_SNMP_LKP_FUN */ + } + /* Otherwise (template), use the guesstimation function to get + * the number of devices present */ + else + { + devices_count = guestimate_template_count(su_info_p); + upsdebugx(1, "Guesstimation: there are %ld device(s) present", devices_count); + } + + /* Sanity check before data publication */ + if (devices_count < 1) { + devices_count = 1; + daisychain_enabled = FALSE; + upsdebugx(1, "Devices count is less than 1!"); + upsdebugx(1, "Falling back to 1 device and disabling daisychain support!"); + } else { + /* Publish the device(s) count - even if just one + * device was recognized at this moment */ + dstate_setinfo("device.count", "%ld", devices_count); + + /* Also publish the default value for mfr and a forged model + * for device.0 (whole daisychain) */ + su_info_p = su_find_info("device.mfr"); + if (su_info_p != NULL) { + su_info_p = su_find_info("ups.mfr"); + if (su_info_p != NULL) { + su_setinfo(su_info_p, NULL); + } + } + /* Forge model using device.type and number */ + su_info_p = su_find_info("device.type"); + if ((su_info_p != NULL) && (su_info_p->dfl != NULL)) { + dstate_setinfo("device.model", "daisychain %s (1+%ld)", + su_info_p->dfl, devices_count - 1); + dstate_setinfo("device.type", "%s", su_info_p->dfl); + } + else { + dstate_setinfo("device.model", "daisychain (1+%ld)", devices_count - 1); + } + } + } + else { + daisychain_enabled = FALSE; + upsdebugx(1, "No device.count entry found, daisychain support not needed"); + } + + /* Finally, compute and store the base OID index and NUT offset */ + su_info_p = su_find_info("device.model"); + if (su_info_p != NULL) { + device_template_index_base = base_snmp_template_index(su_info_p); + upsdebugx(1, "%s: device_template_index_base = %i", __func__, device_template_index_base); + device_template_offset = device_template_index_base - 1; + upsdebugx(1, "%s: device_template_offset = %i", __func__, device_template_offset); + } + else { + upsdebugx(1, "%s: No device.model entry found.", __func__); + } + + upsdebugx(1, "%s: daisychain support is %s", __func__, + (daisychain_enabled==TRUE)?"enabled":"disabled"); + + return daisychain_enabled; +} + +/*********************************************************************** + * SNMP handling functions + **********************************************************************/ + +/* Process a data with regard to SU_OUTPHASES, SU_INPHASES and SU_BYPPHASES. + * 3phases related data are disabled if the unit is 1ph, and conversely. + * If the related phases data (input, output, bypass) is not yet valued, + * retrieve it first. + * + * type: input, output, bypass + * su_info_p: variable to process flags on + * Return 0 if OK, 1 if the caller needs to "continue" the walk loop (i.e. + * skip the present data) + */ +static int process_phase_data(const char* type, long *nb_phases, snmp_info_t *su_info_p) +{ + snmp_info_t *tmp_info_p; + char tmpOID[SU_INFOSIZE]; + char tmpInfo[SU_INFOSIZE]; + long tmpValue; + snmp_info_flags_t phases_flag = 0, single_phase_flag = 0, three_phase_flag = 0; + + /* Phase specific data */ + if (!strncmp(type, "input", 5)) { + phases_flag = SU_INPHASES; + single_phase_flag = SU_INPUT_1; + three_phase_flag = SU_INPUT_3; + } + else if (!strncmp(type, "output", 6)) { + phases_flag = SU_OUTPHASES; + single_phase_flag = SU_OUTPUT_1; + three_phase_flag = SU_OUTPUT_3; + } + else if (!strncmp(type, "input.bypass", 12)) { + phases_flag = SU_BYPPHASES; + single_phase_flag = SU_BYPASS_1; + three_phase_flag = SU_BYPASS_3; + } + else { + upsdebugx(2, "%s: unknown type '%s'", __func__, type); + return 1; + } + + /* Init the phase(s) info for this device, if not already done */ + if (*nb_phases == -1) { + upsdebugx(2, "%s phases information not initialized for device %i", + type, current_device_number); + + memset(tmpInfo, 0, SU_INFOSIZE); + + /* daisychain specifics... */ + if ( (daisychain_enabled == TRUE) && (current_device_number > 0) ) { + /* Device(s) 2-N (slave(s)) need to append 'device.x' */ + snprintf(tmpInfo, SU_INFOSIZE, + "device.%i.%s.phases", current_device_number, type); + } + else { + snprintf(tmpInfo, SU_INFOSIZE, "%s.phases", type); + } + + if (dstate_getinfo(tmpInfo) == NULL) { + /* {input,output,bypass}.phases is not yet published, + * try to get the template for it */ + snprintf(tmpInfo, SU_INFOSIZE, "%s.phases", type); + tmp_info_p = su_find_info(tmpInfo); + if (tmp_info_p != NULL) { + memset(tmpOID, 0, SU_INFOSIZE); + + /* Daisychain specific: we may have a template (including + * formatting string) that needs to be adapted! */ + if (strchr(tmp_info_p->OID, '%') != NULL) { + upsdebugx(2, "Found template, need to be adapted"); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf((char*)tmpOID, SU_INFOSIZE, tmp_info_p->OID, current_device_number + device_template_offset); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + } + else { + /* Otherwise, just point at what we found */ + upsdebugx(2, "Found entry, not a template %s", tmp_info_p->OID); + snprintf((char*)tmpOID, SU_INFOSIZE, "%s", tmp_info_p->OID); + } + /* Actually get the data */ + if (nut_snmp_get_int(tmpOID, &tmpValue) == TRUE) { + *nb_phases = tmpValue; + } + else { + upsdebugx(2, "Can't get %s value. Defaulting to 1 %s.phase", tmpInfo, type); + *nb_phases = 1; + /* FIXME: return something or process using default?! */ + } + } + else { + upsdebugx(2, "No %s entry. Defaulting to 1 %s.phase", tmpInfo, type); + *nb_phases = 1; + /* FIXME: return something or process using default?! */ + } + } + else { + *nb_phases = atoi(dstate_getinfo(tmpInfo)); + } + /* Publish the number of phase(s) */ + dstate_setinfo(tmpInfo, "%ld", *nb_phases); + upsdebugx(2, "device %i has %ld %s.phases", current_device_number, *nb_phases, type); + } + /* FIXME: what to do here? + else if (*nb_phases == 0) { + return 1; + } */ + + + /* Actual processing of phases related data */ +/* FIXME: don't clear SU_INPHASES in daisychain mode!!! ??? */ + if (su_info_p->flags & single_phase_flag) { + if (*nb_phases == 1) { + upsdebugx(1, "%s_phases is 1", type); + su_info_p->flags &= ~phases_flag; + } else { + upsdebugx(1, "%s_phases is not 1", type); + su_info_p->flags &= ~SU_FLAG_OK; + return 1; + } + } else if (su_info_p->flags & three_phase_flag) { + if (*nb_phases == 3) { + upsdebugx(1, "%s_phases is 3", type); + su_info_p->flags &= ~phases_flag; + } else { + upsdebugx(1, "%s_phases is not 3", type); + su_info_p->flags &= ~SU_FLAG_OK; + return 1; + } + } else { + upsdebugx(1, "%s_phases is %ld", type, *nb_phases); + } + return 0; /* FIXME: remap EXIT_SUCCESS to RETURN_SUCCESS */ +} + + /* walk ups variables and set elements of the info array. */ bool_t snmp_ups_walk(int mode) { + long *walked_input_phases, *walked_output_phases, *walked_bypass_phases; static unsigned long iterations = 0; snmp_info_t *su_info_p; bool_t status = FALSE; - for (su_info_p = &snmp_info[0]; su_info_p->info_type != NULL ; su_info_p++) { + if (mode == SU_WALKMODE_UPDATE) { + semistatic_countdown--; + if (semistatic_countdown < 0) + semistatic_countdown = semistaticfreq; + } - /* Check if we are asked to stop (reactivity++) */ - if (exit_flag != 0) - return TRUE; + /* Loop through all device(s) */ + /* Note: considering "unitary" and "daisy-chained" devices, we have + * several variables (and their values) that can come into play: + * devices_count == 1 (default) AND daisychain_enabled == FALSE => unitary + * devices_count > 1 (AND/OR?) daisychain_enabled == TRUE => a daisy-chain + * The current_device_number == 0 in context of daisy-chain means the + * "whole device" with composite or summary values that refer to the + * chain as a virtual power device (e.g. might be a sum of outlet counts). + * If daisychain_enabled == TRUE and current_device_number == 1 then we + * are looking at the "master" device (one that we have direct/networked + * connectivity to; the current_device_number > 1 is a slave (chained by + * some proprietary link not visible from the outside) and represented + * through the master - the slaves are not addressable directly. If the + * master dies/reboots, connection to the whole chain is interrupted. + * The dstate string names for daisychained sub-devices have the prefix + * "device." and number embedded (e.g. "device.3.input.phases") except + * for the whole (#0) virtual device, so it *seems* similar to unitary. + */ - /* skip instcmd, not linked to outlets */ - if ((SU_TYPE(su_info_p) == SU_TYPE_CMD) - && !(su_info_p->flags & SU_OUTLET)) { - upsdebugx(1, "SU_CMD_MASK => %s", su_info_p->OID); - continue; - } - /* skip elements we shouldn't show */ - if (!(su_info_p->flags & SU_FLAG_OK)) - continue; + for (current_device_number = (daisychain_enabled == FALSE && devices_count == 1 ? 1 : 0) ; + current_device_number <= devices_count; current_device_number++) + { - /* skip static elements in update mode */ - if (mode == SU_WALKMODE_UPDATE && - su_info_p->flags & SU_FLAG_STATIC) - continue; + upsdebugx(1, "%s: walking device %d", + __func__, current_device_number); - /* set default value if we cannot fetch it */ - /* and set static flag on this element. - * not applicable to outlets (need SU_FLAG_STATIC tagging) */ - if ((su_info_p->flags & SU_FLAG_ABSENT) - && !(su_info_p->flags & SU_OUTLET)) { - if (mode == SU_WALKMODE_INIT) { - if (su_info_p->dfl) { - /* Set default value if we cannot fetch it from ups. */ - su_setinfo(su_info_p, NULL); - } - su_info_p->flags |= SU_FLAG_STATIC; - } - continue; + /* reinit the alarm buffer, before */ + if (devices_count > 1) + device_alarm_init(); + + /* better safe than sorry, check sanity on every loop cycle */ + if (snmp_info == NULL) { + fatalx(EXIT_FAILURE, "%s: snmp_info is not initialized", __func__); } - /* check stale elements only on each PN_STALE_RETRY iteration. */ - if ((su_info_p->flags & SU_FLAG_STALE) && - (iterations % SU_STALE_RETRY) != 0) - continue; + if (snmp_info[0].info_type == NULL) { + upsdebugx(1, "%s: WARNING: snmp_info is empty", __func__); + } - if (su_info_p->flags & SU_INPHASES) { - upsdebugx(1, "Check input_phases"); - if (input_phases == 0) { - continue; - } - if (su_info_p->flags & SU_INPUT_1) { - if (input_phases == 1) { - upsdebugx(1, "input_phases is 1"); - su_info_p->flags &= ~SU_INPHASES; - } else { - upsdebugx(1, "input_phases is not 1"); - su_info_p->flags &= ~SU_FLAG_OK; - continue; - } - } else if (su_info_p->flags & SU_INPUT_3) { - if (input_phases == 3) { - upsdebugx(1, "input_phases is 3"); - su_info_p->flags &= ~SU_INPHASES; - } else { - upsdebugx(1, "input_phases is not 3"); - su_info_p->flags &= ~SU_FLAG_OK; - continue; - } + /* Loop through all mapping entries for the current_device_number */ + for (su_info_p = &snmp_info[0]; (su_info_p != NULL && su_info_p->info_type != NULL) ; su_info_p++) { + + /* NOTE: Effectively below we do this: + * switch(current_device_number) { + * case 0: devtype = "daisychain whole" + * case 1: devtype = "daisychain master" + * default: devtype = "daisychain slave" + * } + * with a consideration for directly-addressable + * slave devices (can be seen in chain via master, + * but also queryable alone with an IP connection) + * NOTE: until proven otherwise, "single" may mean + * both (either) a daisy-chain enabled master device + * without further connected "slave" devices, and + * a directly addressable (IP-connected) "slave". + * Possibly also an ePDU etc. that serves a MIB + * which resolves "device.count" with the selected + * subdriver. + */ + if (daisychain_enabled == TRUE) { + upsdebugx(1, "%s: processing daisy-chain device %i (%s)", + __func__, current_device_number, + (current_device_number == 1) + ? (devices_count > 1 ? "master" : "single") + : (current_device_number > 1 ? "slave" : "whole") + ); } else { - upsdebugx(1, "input_phases is %d", input_phases); + upsdebugx(1, "%s: processing unitary device (%i)", + __func__, current_device_number); } - } - if (su_info_p->flags & SU_OUTPHASES) { - upsdebugx(1, "Check output_phases"); - if (output_phases == 0) { - continue; + /* Check if we are asked to stop (reactivity++) */ + if (exit_flag != 0) { + upsdebugx(1, "%s: aborting because exit_flag was set", __func__); + return TRUE; } - if (su_info_p->flags & SU_OUTPUT_1) { - if (output_phases == 1) { - upsdebugx(1, "output_phases is 1"); - su_info_p->flags &= ~SU_OUTPHASES; - } else { - upsdebugx(1, "output_phases is not 1"); - su_info_p->flags &= ~SU_FLAG_OK; - continue; - } - } else if (su_info_p->flags & SU_OUTPUT_3) { - if (output_phases == 3) { - upsdebugx(1, "output_phases is 3"); - su_info_p->flags &= ~SU_OUTPHASES; - } else { - upsdebugx(1, "output_phases is not 3"); - su_info_p->flags &= ~SU_FLAG_OK; - continue; - } - } else { - upsdebugx(1, "output_phases is %d", output_phases); - } - } - if (su_info_p->flags & SU_BYPPHASES) { - upsdebugx(1, "Check bypass_phases"); - if (bypass_phases == 0) { - continue; - } - if (su_info_p->flags & SU_BYPASS_1) { - if (bypass_phases == 1) { - upsdebugx(1, "bypass_phases is 1"); - su_info_p->flags &= ~SU_BYPPHASES; - } else { - upsdebugx(1, "bypass_phases is not 1"); - su_info_p->flags &= ~SU_FLAG_OK; - continue; - } - } else if (su_info_p->flags & SU_BYPASS_3) { - if (input_phases == 3) { - upsdebugx(1, "bypass_phases is 3"); - su_info_p->flags &= ~SU_BYPPHASES; - } else { - upsdebugx(1, "bypass_phases is not 3"); - su_info_p->flags &= ~SU_FLAG_OK; - continue; - } - } else { - upsdebugx(1, "bypass_phases is %d", bypass_phases); - } - } - - /* process outlet template definition */ - if (su_info_p->flags & SU_OUTLET) { - upsdebugx(1, "outlet template definition found (%s)...", su_info_p->info_type); - int cur_outlet_number = 1; - int cur_nut_index = 0; - int outlet_count = 0; - snmp_info_t cur_info_p; - - if(dstate_getinfo("outlet.count") == NULL) { - /* FIXME: should we disable it? - * su_info_p->flags &= ~SU_FLAG_OK; */ - continue; - } - outlet_count = atoi(dstate_getinfo("outlet.count")); - - /* general init of data using the template */ - instantiate_info(su_info_p, &cur_info_p); - - for (cur_outlet_number = base_snmp_outlet_index(su_info_p->OID) ; - cur_outlet_number < (outlet_count + base_snmp_outlet_index(su_info_p->OID)) ; - cur_outlet_number++) + /* Skip daisychain data count */ + if (mode == SU_WALKMODE_INIT && + (!strncmp(su_info_p->info_type, "device.count", 12))) { - cur_nut_index = cur_outlet_number + base_nut_outlet_offset(); - sprintf((char*)cur_info_p.info_type, su_info_p->info_type, - cur_nut_index); - - /* check if default value is also a template */ - if ((cur_info_p.dfl != NULL) && - (strstr(su_info_p->dfl, "%i") != NULL)) { - cur_info_p.dfl = (char *)xmalloc(SU_INFOSIZE); - sprintf((char *)cur_info_p.dfl, su_info_p->dfl, cur_nut_index); - } - - if (cur_info_p.OID != NULL) { - sprintf((char *)cur_info_p.OID, su_info_p->OID, cur_outlet_number); - - /* add outlet instant commands to the info database. */ - if (SU_TYPE(su_info_p) == SU_TYPE_CMD) { - /* FIXME: only add if "su_ups_get(cur_info_p) == TRUE" */ - if (mode == SU_WALKMODE_INIT) - dstate_addcmd(cur_info_p.info_type); - } - else /* get and process this data */ - status = get_and_process_data(mode, &cur_info_p); - } else { - /* server side (ABSENT) data */ - su_setinfo(&cur_info_p, NULL); - } - /* set back the flag */ - su_info_p->flags = cur_info_p.flags; + su_info_p->flags &= ~SU_FLAG_OK; + continue; } - free((char*)cur_info_p.info_type); - if (cur_info_p.OID != NULL) - free((char*)cur_info_p.OID); - if ((cur_info_p.dfl != NULL) && - (strstr(su_info_p->dfl, "%i") != NULL)) - free((char*)cur_info_p.dfl); - } else { - /* get and process this data */ - status = get_and_process_data(mode, su_info_p); +/* FIXME: daisychain-whole, what to do? */ +/* Note that when addressing the FIXME above, + * if (current_device_number == 0 && daisychain_enabled == FALSE) + * then we'd skip it still (unitary device is at current_device_number == 1)... + */ + /* skip the whole-daisychain for now */ + if (current_device_number == 0 && daisychain_enabled == TRUE) { + upsdebugx(1, "Skipping daisychain device.0 for now..."); + continue; + } + + /* skip instcmd, not linked to outlets */ + if ((SU_TYPE(su_info_p) == SU_TYPE_CMD) + && !(su_info_p->flags & SU_OUTLET) + && !(su_info_p->flags & SU_OUTLET_GROUP)) { + upsdebugx(1, "SU_CMD_MASK => %s", su_info_p->OID); + continue; + } + /* skip elements we shouldn't show in update mode */ + if ((mode == SU_WALKMODE_UPDATE) && !(su_info_p->flags & SU_FLAG_OK)) + continue; + + /* skip semi-static elements in update mode: only parse when countdown reaches 0 */ + if ((mode == SU_WALKMODE_UPDATE) && (su_info_p->flags & SU_FLAG_SEMI_STATIC)) { + if (semistatic_countdown != 0) + continue; + upsdebugx(1, "Refreshing semi-static entry %s", su_info_p->OID); + } + + /* skip static elements in update mode */ + if ((mode == SU_WALKMODE_UPDATE) && (su_info_p->flags & SU_FLAG_STATIC)) + continue; + + /* Set default value if we cannot fetch it */ + /* and set static flag on this element. + * Not applicable to outlets (need SU_FLAG_STATIC tagging) */ + if ((su_info_p->flags & SU_FLAG_ABSENT) + && !(su_info_p->flags & SU_OUTLET) + && !(su_info_p->flags & SU_OUTLET_GROUP) + && !(su_info_p->flags & SU_AMBIENT_TEMPLATE)) + { + if (mode == SU_WALKMODE_INIT) + { + if (su_info_p->dfl) + { + if ((daisychain_enabled == TRUE) && (devices_count > 1)) + { + if (current_device_number == 0) + { + su_setinfo(su_info_p, NULL); /* FIXME: daisychain-whole, what to do? */ + } else { + status = process_template(mode, "device", su_info_p); + } + } + else { + /* Set default value if we cannot fetch it from ups. */ + su_setinfo(su_info_p, NULL); + } + } + su_info_p->flags |= SU_FLAG_STATIC; + } + continue; + } + + /* check stale elements only on each PN_STALE_RETRY iteration. */ + /* if ((su_info_p->flags & SU_FLAG_STALE) && + (iterations % SU_STALE_RETRY) != 0) + continue; + */ + /* Filter 1-phase Vs 3-phase according to {input,output,bypass}.phase. + * Non matching items are disabled, and flags are cleared at init + * time */ + /* Process input phases information */ + walked_input_phases = &daisychain_info[current_device_number]->input_phases; + if (su_info_p->flags & SU_INPHASES) { + upsdebugx(1, "Check input_phases (%ld)", *walked_input_phases); + if (process_phase_data("input", walked_input_phases, su_info_p) == 1) + continue; + } + + /* Process output phases information */ + walked_output_phases = &daisychain_info[current_device_number]->output_phases; + if (su_info_p->flags & SU_OUTPHASES) { + upsdebugx(1, "Check output_phases (%ld)", *walked_output_phases); + if (process_phase_data("output", walked_output_phases, su_info_p) == 1) + continue; + } + + /* Process bypass phases information */ + walked_bypass_phases = &daisychain_info[current_device_number]->bypass_phases; + if (su_info_p->flags & SU_BYPPHASES) { + upsdebugx(1, "Check bypass_phases (%ld)", *walked_bypass_phases); + if (process_phase_data("input.bypass", walked_bypass_phases, su_info_p) == 1) + continue; + } + + /* process template (outlet, outlet group, inc. daisychain) definition */ + if (su_info_p->flags & SU_OUTLET) { + /* Skip commands after init */ + if ((SU_TYPE(su_info_p) == SU_TYPE_CMD) && (mode == SU_WALKMODE_UPDATE)) + continue; + else + status = process_template(mode, "outlet", su_info_p); + } + else if (su_info_p->flags & SU_OUTLET_GROUP) { + /* Skip commands after init */ + if ((SU_TYPE(su_info_p) == SU_TYPE_CMD) && (mode == SU_WALKMODE_UPDATE)) + continue; + else + status = process_template(mode, "outlet.group", su_info_p); + } + else if (su_info_p->flags & SU_AMBIENT_TEMPLATE) { + /* Skip commands after init */ + if ((SU_TYPE(su_info_p) == SU_TYPE_CMD) && (mode == SU_WALKMODE_UPDATE)) + continue; + else + status = process_template(mode, "ambient", su_info_p); + } + else { +/* if (daisychain_enabled == TRUE) { + status = process_template(mode, "device", su_info_p); + } + else { +*/ /* get and process this data, including daisychain adaptation */ + status = get_and_process_data(mode, su_info_p); +/* + } +*/ + } + } /* for (su_info_p... */ + + if (devices_count > 1) { + /* commit the device alarm buffer */ + device_alarm_commit(current_device_number); + + /* reinit the alarm buffer, after, not to pollute "device.0" */ + device_alarm_init(); } - } /* for (su_info_p... */ - + } iterations++; - return status; } @@ -1248,19 +3311,200 @@ bool_t su_ups_get(snmp_info_t *su_info_p) static char buf[SU_INFOSIZE]; bool_t status; long value; + double dvalue; + const char *strValue = NULL; + struct snmp_pdu ** pdu_array; + struct snmp_pdu * current_pdu; + alarms_info_t * alarms; + int index = 0; + char *format_char = NULL; + int saved_current_device_number = -1; + snmp_info_t *tmp_info_p = NULL; - upsdebugx(2, "su_ups_get: %s %s", su_info_p->info_type, su_info_p->OID); + upsdebugx(2, "%s: %s %s", __func__, su_info_p->info_type, su_info_p->OID); + + /* Check if this is a daisychain template */ + if (su_info_p->OID != NULL + && (format_char = strchr(su_info_p->OID, '%')) != NULL + ) { + upsdebugx(3, "%s: calling instantiate_info() for " + "daisy-chain template", __func__); + tmp_info_p = instantiate_info(su_info_p, tmp_info_p); + if (tmp_info_p != NULL) { + upsdebugx(3, "%s: instantiate_info() returned " + "non-null OID: %s", + __func__, tmp_info_p->OID); + /* adapt the OID */ + if (su_info_p->OID != NULL) { +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf((char *)tmp_info_p->OID, SU_INFOSIZE, su_info_p->OID, + current_device_number + device_template_offset); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + upsdebugx(3, "%s: OID %s adapted into %s", + __func__, su_info_p->OID, + tmp_info_p->OID); + } + else { + free_info(tmp_info_p); + return FALSE; + } + + /* adapt info_type */ + if (su_info_p->info_type != NULL) { + snprintf((char *)tmp_info_p->info_type, + SU_INFOSIZE, "%s", + su_info_p->info_type); + } + else { + free_info(tmp_info_p); + return FALSE; + } + + su_info_p = tmp_info_p; + } + else { + upsdebugx(2, "%s: can't instantiate template", __func__); + return FALSE; + } + } + else { + /* Non-templated OID, still may be aimed at a + * daisy-chained device (master of the chain + * makes sense for IETF device.contact etc.). + * BUT: It could be a direct request for earlier + * resolved OID. So check for "device.N." too. + */ + if (daisychain_enabled == TRUE + && devices_count > 1 + && current_device_number > 0 + ) { + /* So we had a literal OID string, originally + * Check for "device.N." in the string: */ + char * varname = su_info_p->info_type; + if (!strncmp(varname, "device.", 7) + && (varname[7] >= '0' && varname[7] <= '9') + ) { + upsdebugx(2, "%s: keeping original " + "current device == %d for " + "non-templated daisy value", + __func__, current_device_number); + } else { + upsdebugx(2, "%s: would fake " + "current device == 1 for " + "non-templated daisy value " + "instead of %d", + __func__, current_device_number); + saved_current_device_number = current_device_number; + } + /* At this point we applied no hacks yet, + * just stashed a non-negative value into + * saved_current_device_number + */ + } + } if (!strcasecmp(su_info_p->info_type, "ups.status")) { - +/* FIXME: daisychain status support! */ + upsdebugx(2, "%s: requesting nut_snmp_get_int() for " + "ups.status, with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); status = nut_snmp_get_int(su_info_p->OID, &value); if (status == TRUE) { su_status_set(su_info_p, value); upsdebugx(2, "=> value: %ld", value); } + else + upsdebugx(2, "=> Failed"); + + free_info(tmp_info_p); + return status; + } + + /* Handle 'ups.alarm', 'outlet.n.alarm' and 3phase 'Lx.alarm', + * nothing else! */ + if (!strcmp(strrchr(su_info_p->info_type, '.'), ".alarm")) { + + upsdebugx(2, "Processing alarm: %s", su_info_p->info_type); + +/* FIXME: daisychain alarms support! */ + upsdebugx(2, "%s: requesting nut_snmp_get_int() for " + "some alarm, with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); + status = nut_snmp_get_int(su_info_p->OID, &value); + if (status == TRUE) + { + su_alarm_set(su_info_p, value); + upsdebugx(2, "=> value: %ld", value); + } else upsdebugx(2, "=> Failed"); + free_info(tmp_info_p); + return status; + } + + /* Walk a subtree (array) of alarms, composed of OID references. + * The object referenced should not be accessible, but rather when + * present, this means that the alarm condition is TRUE. + * Only present in powerware-mib.c for now */ + if (!strcasecmp(su_info_p->info_type, "ups.alarms")) { + upsdebugx(2, "%s: requesting nut_snmp_get_int() for " + "ups.alarms, with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); + status = nut_snmp_get_int(su_info_p->OID, &value); + if (status == TRUE) { + upsdebugx(2, "=> %ld alarms present", value); + if( value > 0 ) { + pdu_array = nut_snmp_walk(su_info_p->OID, INT_MAX); + if(pdu_array == NULL) { + upsdebugx(2, "=> Walk failed"); + return FALSE; + } + + current_pdu = pdu_array[index]; + while(current_pdu) { + /* Retrieve the OID name, for comparison */ + if (decode_oid(current_pdu, buf, sizeof(buf)) == TRUE) { + alarms = alarms_info; + while( alarms->OID ) { + if(!strcmp(buf, alarms->OID)) { + upsdebugx(3, "Alarm OID found => %s", alarms->OID); + /* Check for ups.status value */ + if (alarms->status_value) { + upsdebugx(3, "Alarm value (status) found => %s", alarms->status_value); + status_set(alarms->status_value); + } + /* Check for ups.alarm value */ + if (alarms->alarm_value) { + upsdebugx(3, "Alarm value (alarm) found => %s", alarms->alarm_value); + alarm_set(alarms->alarm_value); + } + break; + } + alarms++; + } + } + index++; + current_pdu = pdu_array[index]; + } + nut_snmp_free(pdu_array); + } + } + else { + upsdebugx(2, "=> Failed"); + } + + free_info(tmp_info_p); return status; } @@ -1268,9 +3512,13 @@ bool_t su_ups_get(snmp_info_t *su_info_p) if (!strcasecmp(su_info_p->info_type, "ambient.temperature")) { float temp=0; + upsdebugx(2, "%s: requesting nut_snmp_get_int() for " + "ambient.temperature, with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); status = nut_snmp_get_int(su_info_p->OID, &value); if(status != TRUE) { + free_info(tmp_info_p); return status; } @@ -1294,279 +3542,490 @@ bool_t su_ups_get(snmp_info_t *su_info_p) snprintf(buf, sizeof(buf), "%.1f", temp); su_setinfo(su_info_p, buf); + free_info(tmp_info_p); return TRUE; } - if (su_info_p->info_flags == 0) { + /* special treatment for element without oid but with default value */ + if (su_info_p->OID == NULL && su_info_p->dfl != NULL) { + status = TRUE; + /* FIXME: strlcpy() would fit here safer; not used in NUT yet */ + strncpy(buf, su_info_p->dfl, sizeof(buf) - 1); + buf[sizeof(buf) - 1] = '\0'; + } + else if (su_info_p->info_flags & ST_FLAG_STRING) { + upsdebugx(2, "%s: requesting nut_snmp_get_str(), " + "with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); + status = nut_snmp_get_str(su_info_p->OID, buf, + sizeof(buf), su_info_p->oid2info); + if (status == TRUE) { + if (quirk_symmetra_threephase) { + if (!strcasecmp(su_info_p->info_type, "input.transfer.low") + || !strcasecmp(su_info_p->info_type, "input.transfer.high")) { + /* Convert from three phase line-to-line voltage to line-to-neutral voltage */ + double tmp_dvalue = atof(buf); + tmp_dvalue = tmp_dvalue * 0.707; + snprintf(buf, sizeof(buf), "%.2f", tmp_dvalue); + } + } + /* Check if there is a string reformating function */ + const char *fmt_buf = NULL; + if ((fmt_buf = su_find_strval(su_info_p->oid2info, buf)) != NULL) { + snprintf(buf, sizeof(buf), "%s", fmt_buf); + } + } + } else { + upsdebugx(2, "%s: requesting nut_snmp_get_int(), " + "with%s daisy template originally", + __func__, (format_char!=NULL ? "" : "out")); status = nut_snmp_get_int(su_info_p->OID, &value); if (status == TRUE) { - if (su_info_p->flags&SU_FLAG_NEGINVALID && value<0) { + if ((su_info_p->flags&SU_FLAG_NEGINVALID && value<0) + || (su_info_p->flags&SU_FLAG_ZEROINVALID && value==0)) { su_info_p->flags &= ~SU_FLAG_OK; if(su_info_p->flags&SU_FLAG_UNIQUE) { disable_competition(su_info_p); su_info_p->flags &= ~SU_FLAG_UNIQUE; } + free_info(tmp_info_p); return FALSE; } - if (su_info_p->flags & SU_FLAG_SETINT) { - upsdebugx(1, "setvar %s", su_info_p->OID); - *su_info_p->setvar = value; + /* Check if there is a value to be looked up */ + if ((strValue = su_find_infoval(su_info_p->oid2info, &value)) != NULL) + snprintf(buf, sizeof(buf), "%s", strValue); + else { + /* Check if there is a need to publish decimal too, + * i.e. if switching to integer does not cause a + * loss of precision. + * FIXME: Use remainder? is (dvalue%1.0)>0 cleaner? + */ + dvalue = value * su_info_p->info_len; + if (f_equal((int)dvalue, dvalue)) + snprintf(buf, sizeof(buf), "%i", (int)dvalue); + else + snprintf(buf, sizeof(buf), "%.2f", (float)dvalue); } - snprintf(buf, sizeof(buf), "%.2f", value * su_info_p->info_len); } - } else { - status = nut_snmp_get_str(su_info_p->OID, buf, sizeof(buf), su_info_p->oid2info); } if (status == TRUE) { + if (saved_current_device_number >= 0) { + current_device_number = 1; + } su_setinfo(su_info_p, buf); + if (saved_current_device_number >= 0) { + current_device_number = saved_current_device_number; + } upsdebugx(2, "=> value: %s", buf); } - else + else { upsdebugx(2, "=> Failed"); + } + free_info(tmp_info_p); return status; } -/* set r/w INFO_ element to a value. */ -int su_setvar(const char *varname, const char *val) +/* Common function for setting OIDs, from a NUT variable name, + * used by su_setvar() and su_instcmd() + * Params: + * @mode: SU_MODE_INSTCMD for instant commands, SU_MODE_SETVAR for settings + * @varname: name of variable or command to set the OID from + * @val: value for settings, NULL for commands + + * Returns + * STAT_SET_HANDLED if OK, + * STAT_SET_INVALID or STAT_SET_UNKNOWN if the command / setting is not supported + * STAT_SET_FAILED otherwise + */ +static int su_setOID(int mode, const char *varname, const char *val) { snmp_info_t *su_info_p = NULL; bool_t status; int retval = STAT_SET_FAILED; + int cmd_offset = 0; + long value = -1; + /* normal (default), outlet, or outlet group variable */ + snmp_info_flags_t vartype = 0; + int daisychain_device_number = -1; + /* variable without the potential "device.X" prefix, to find the template */ + char *tmp_varname = NULL; + char setOID[SU_INFOSIZE]; + /* Used for potentially appending "device.X." to {outlet,outlet.group}.count */ + char template_count_var[SU_BUFSIZE]; - upsdebugx(2, "entering su_setvar(%s, %s)", varname, val); + upsdebugx(2, "entering %s(%s, %s, %s)", __func__, + (mode==SU_MODE_INSTCMD)?"instcmd":"setvar", varname, val); - if (strncmp(varname, "outlet", 6)) - su_info_p = su_find_info(varname); + memset(setOID, 0, SU_INFOSIZE); + memset(template_count_var, 0, SU_BUFSIZE); + + /* Check if it's a daisychain setting (device.x.varname), + * or a non-daisy setting/value/cmd for the "master" unit + * (as un-numbered device.varname), or something else? + */ + if (!strncmp(varname, "device", 6)) { + if (varname[7] >= '0' && varname[7] <= '9') { + /* Extract the (single-digit) device number + * TODO: use strtol() or similar to support multi-digit + * chains and offset tmp_varname inside varname properly + */ + daisychain_device_number = atoi(&varname[7]); + /* Point at the command, without the "device.x" prefix */ + tmp_varname = strdup(&varname[9]); + snprintf(template_count_var, 10, "%s", varname); + + upsdebugx(2, "%s: got a daisychain %s (%s) for device %i", + __func__, (mode==SU_MODE_INSTCMD)?"command":"setting", + tmp_varname, daisychain_device_number); + + if (daisychain_device_number > devices_count) + upsdebugx(2, "%s: item is out of bound (%i / %ld)", + __func__, daisychain_device_number, devices_count); + } + else { + /* Note: below we check if "OID" contains ".%i" template text + * like we do in su_setinfo(); eventually we might get vendor + * MIBs that do expose device.contact/location/description + * for each link in the chain... + */ + if (daisychain_enabled == TRUE) { + /* Is the original "device.varname" backed by a templated + * OID string, so we can commonly set e.g. device.contact + * for everything in the chain? + */ + su_info_p = su_find_info(varname); + if (su_info_p + && (su_info_p->OID == NULL || strstr(su_info_p->OID, ".%i") != NULL) + ) { + /* is templated or defaulted */ + daisychain_device_number = 0; + } else { + daisychain_device_number = 1; + } + + upsdebugx(2, "%s: got an un-numbered daisychain %s (%s), " + "directing it to %s", + __func__, (mode==SU_MODE_INSTCMD)?"command":"setting", + varname, + (daisychain_device_number==1)?"'master' device":"all devices" + ); + } else { + /* No daisy, no poppy */ + daisychain_device_number = 0; + } + tmp_varname = strdup(varname); + } + } else { + daisychain_device_number = 0; + tmp_varname = strdup(varname); + } + + /* skip the whole-daisychain for now: + * will send the settings to all devices in the daisychain */ + if ((daisychain_enabled == TRUE) && (devices_count > 1) && (daisychain_device_number == 0)) { + upsdebugx(2, "daisychain %s for device.0 are not yet supported!", + (mode==SU_MODE_INSTCMD)?"command":"setting"); + free(tmp_varname); + return STAT_SET_INVALID; + } + + /* Check if it is outlet / outlet.group, or standard variable */ + if (strncmp(tmp_varname, "outlet", 6)) { + su_info_p = su_find_info(tmp_varname); + /* what if e.g. "device.x.contact" is not found as a "contact"? */ + if (!su_info_p && strcmp(tmp_varname, varname)) { + upsdebugx(2, + "%s: did not find info for daisychained entry %s, " + "retrying with original varname %s", + __func__, tmp_varname, varname); + su_info_p = su_find_info(varname); + + /* Still nothing? Try to revert from "device.1." + * as a daisychain master? */ + if (!su_info_p + && daisychain_enabled == TRUE + && devices_count > 1 + && daisychain_device_number == 1 + && !strncmp(varname, "device.1.", 9) + ) { + char tmp_buf[SU_INFOSIZE]; + snprintf(tmp_buf, sizeof(tmp_buf), + "device.%s", (varname + 9)); + su_info_p = su_find_info(tmp_buf); + if (su_info_p) { + upsdebugx(2, "%s: finally found " + "as daisy master revert %s", + __func__, tmp_buf); + free(tmp_varname); + tmp_varname = strdup(tmp_buf); + } + } + } + } else { + /* is indeed an outlet.* or device.x.outlet.* */ snmp_info_t *tmp_info_p; - char *outlet_number_ptr = strchr(varname, '.'); - int outlet_number = atoi(++outlet_number_ptr); - if (dstate_getinfo("outlet.count") == NULL) { - upsdebugx(2, "su_setvar: can't get outlet.count..."); + /* Point the outlet or outlet group number in the string */ + const char *item_number_ptr = NULL; + /* Store the target outlet or group number */ + int item_number = extract_template_number_from_snmp_info_t(tmp_varname); + /* Store the total number of outlets or outlet groups */ + int total_items = -1; + + /* Check if it is outlet / outlet.group */ + vartype = get_template_type(tmp_varname); + if (vartype == SU_OUTLET_GROUP) { + snprintfcat(template_count_var, SU_BUFSIZE, "outlet.group.count"); + total_items = atoi(dstate_getinfo(template_count_var)); + item_number_ptr = &tmp_varname[12]; + } + else { + snprintfcat(template_count_var, SU_BUFSIZE, "outlet.count"); + total_items = atoi(dstate_getinfo(template_count_var)); + item_number_ptr = &tmp_varname[6]; + } + upsdebugx(3, "Using count variable '%s'", template_count_var); + item_number = atoi(++item_number_ptr); + upsdebugx(3, "%s: item %i / %i", __func__, item_number, total_items); + + /* ensure the item number is supported (filtered upstream though)! */ + if (item_number > total_items) { + /* out of bound item number */ + upsdebugx(2, "%s: item is out of bound (%i / %i)", + __func__, item_number, total_items); return STAT_SET_INVALID; } + /* find back the item template */ + char *item_varname = (char *)xmalloc(SU_INFOSIZE); + snprintf(item_varname, SU_INFOSIZE, "%s.%s%s", + (vartype == SU_OUTLET)?"outlet":"outlet.group", + "%i", strchr(item_number_ptr++, '.')); - upsdebugx(3, "su_setvar: outlet %i / %i", outlet_number, - atoi(dstate_getinfo("outlet.count"))); - - /* ensure the outlet number is supported (filtered upstream though)! */ - if (outlet_number > atoi(dstate_getinfo("outlet.count"))) { - /* out of bound outlet number */ - upsdebugx(2, "su_setvar: outlet is out of bound (%i / %s)", - outlet_number, dstate_getinfo("outlet.count")); - return STAT_SET_INVALID; - } - /* find back the outlet template */ - char *outlet_varname = (char *)xmalloc(SU_INFOSIZE); - sprintf(outlet_varname, "outlet.%s%s", "%i", strchr(outlet_number_ptr++, '.')); - upsdebugx(3, "su_setvar: searching for template\"%s\"", outlet_varname); - tmp_info_p = su_find_info(outlet_varname); - free(outlet_varname); + upsdebugx(3, "%s: searching for template\"%s\"", __func__, item_varname); + tmp_info_p = su_find_info(item_varname); + free(item_varname); /* for an snmp_info_t instance */ su_info_p = instantiate_info(tmp_info_p, su_info_p); /* check if default value is also a template */ if ((su_info_p->dfl != NULL) && - (strstr(tmp_info_p->dfl, "%i") != NULL)) { + (strstr(tmp_info_p->dfl, "%i") != NULL)) + { su_info_p->dfl = (char *)xmalloc(SU_INFOSIZE); - sprintf((char *)su_info_p->dfl, tmp_info_p->dfl, - outlet_number - base_nut_outlet_offset()); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + snprintf((char *)su_info_p->dfl, SU_INFOSIZE, tmp_info_p->dfl, + item_number); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif } /* adapt the OID */ if (su_info_p->OID != NULL) { - sprintf((char *)su_info_p->OID, tmp_info_p->OID, - outlet_number - base_nut_outlet_offset()); - } - /* else, don't return STAT_SET_INVALID since we can be setting - * a server side variable! */ + if (mode==SU_MODE_INSTCMD) { + /* Workaround buggy Eaton Pulizzi implementation + * which have different offsets index for data & commands! */ + if (su_info_p->flags & SU_CMD_OFFSET) { + upsdebugx(3, "Adding command offset"); + cmd_offset++; + } + } - /* adapt info_type */ - if (su_info_p->info_type != NULL) - sprintf((char *)su_info_p->info_type, "%s", varname); + /* Special processing for daisychain: + * these outlet | outlet groups also include formatting info, + * so we have to check if the daisychain is enabled, and if + * the formatting info for it are in 1rst or 2nd position */ +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + if (daisychain_enabled == TRUE) { + /* Note: daisychain_enabled == TRUE means that we have + * daisychain template. However: + * * when there are multiple devices, offset "-1" applies + * since device.0 is a fake and actual devices start at + * index 1 + * * when there is only 1 device, offset doesn't apply since + * the device index is "0" + */ + int daisychain_offset = 0; + if (devices_count > 1) + daisychain_offset = 1; + + if (su_info_p->flags & SU_TYPE_DAISY_1) { + snprintf((char *)su_info_p->OID, SU_INFOSIZE, tmp_info_p->OID, + daisychain_device_number - daisychain_offset, item_number); + } + else { + snprintf((char *)su_info_p->OID, SU_INFOSIZE, tmp_info_p->OID, + item_number, daisychain_device_number - daisychain_offset); + } + } + else { + snprintf((char *)su_info_p->OID, SU_INFOSIZE, tmp_info_p->OID, item_number); + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + } + /* else, don't return STAT_SET_INVALID for mode==SU_MODE_SETVAR since we + * can be setting a server side variable! */ + else { + if (mode==SU_MODE_INSTCMD) { + free_info(su_info_p); + return STAT_INSTCMD_UNKNOWN; + } + else { + /* adapt info_type */ + if (su_info_p->info_type != NULL) + snprintf((char *)su_info_p->info_type, SU_INFOSIZE, "%s", tmp_varname); + } + } } + /* Sanity check */ if (!su_info_p || !su_info_p->info_type || !(su_info_p->flags & SU_FLAG_OK)) { - upsdebugx(2, "su_setvar: info element unavailable %s", varname); - if (!strncmp(varname, "outlet", 6)) - free_info(su_info_p); + upsdebugx(2, "%s: info element unavailable %s", __func__, varname); + + /* Free template (outlet and outlet.group) */ + free_info(su_info_p); + + if (tmp_varname != NULL) + free(tmp_varname); return STAT_SET_UNKNOWN; } - if (!(su_info_p->info_flags & ST_FLAG_RW) || su_info_p->OID == NULL) { - upsdebugx(2, "su_setvar: not writable %s", varname); - - if (!strncmp(varname, "outlet", 6)) - free_info(su_info_p); - - return STAT_SET_INVALID; + /* set value into the device, using the provided one, or the default one otherwise */ + if (mode==SU_MODE_INSTCMD) { + /* Sanity check: commands should either have a value or a default */ + if ( (val == NULL) && (su_info_p->dfl == NULL) ) { + upsdebugx(1, "%s: cannot execute command '%s': a provided or default value is needed!", __func__, varname); + return STAT_SET_INVALID; + } } - /* set value into the device */ if (su_info_p->info_flags & ST_FLAG_STRING) { - status = nut_snmp_set_str(su_info_p->OID, val); - } else { - status = nut_snmp_set_int(su_info_p->OID, strtol(val, NULL, 0)); + status = nut_snmp_set_str(su_info_p->OID, val ? val : su_info_p->dfl); + } + else { + if (mode==SU_MODE_INSTCMD) { + if ( !str_to_long(val ? val : su_info_p->dfl, &value, 10) ) { + upsdebugx(1, "%s: cannot execute command '%s': value is not a number!", __func__, varname); + return STAT_SET_INVALID; + } + } + else { + /* non string data may imply a value lookup */ + if (su_info_p->oid2info) { + value = su_find_valinfo(su_info_p->oid2info, val ? val : su_info_p->dfl); + } + else { + /* Convert value and apply multiplier */ + if ( !str_to_long(val, &value, 10) ) { + upsdebugx(1, "%s: cannot set '%s': value is not a number!", __func__, varname); + return STAT_SET_INVALID; + } + value = (long)((double)value / su_info_p->info_len); + } + } + /* Actually apply the new value */ + if (SU_TYPE(su_info_p) == SU_TYPE_TIME) { + status = nut_snmp_set_time(su_info_p->OID, value); + } + else { + status = nut_snmp_set_int(su_info_p->OID, value); + } } - if (status == FALSE) - upsdebugx(1, "su_setvar: cannot set value %s for %s", val, su_info_p->OID); + /* Process result */ + if (status == FALSE) { + if (mode==SU_MODE_INSTCMD) + upsdebugx(1, "%s: cannot execute command '%s'", __func__, varname); + else + upsdebugx(1, "%s: cannot set value %s on OID %s", __func__, val, su_info_p->OID); + + retval = STAT_SET_FAILED; + } else { retval = STAT_SET_HANDLED; - upsdebugx(1, "su_setvar: successfully set %s to \"%s\"", varname, val); + if (mode==SU_MODE_INSTCMD) + upsdebugx(1, "%s: successfully sent command %s", __func__, varname); + else { + upsdebugx(1, "%s: successfully set %s to \"%s\"", __func__, varname, val); - /* update info array */ - su_setinfo(su_info_p, val); + /* update info array: call dstate_setinfo, since flags and aux are + * already published, and this saves us some processing */ + dstate_setinfo(varname, "%s", val); + } } - if (!strncmp(varname, "outlet", 6)) + + /* Free template (outlet and outlet.group) */ + if (!strncmp(tmp_varname, "outlet", 6)) free_info(su_info_p); + free(tmp_varname); return retval; } +/* set r/w INFO_ element to a value. + * FIXME: make a common function with su_instcmd! */ +int su_setvar(const char *varname, const char *val) +{ + return su_setOID(SU_MODE_SETVAR, varname, val); +} + +/* Daisychain-aware function to add instant commands: + * Every command that is valid for a device has to be added for device.0 + * This then allows to composite commands, called on device.0 and executed + * on all devices of the daisychain */ +int su_addcmd(snmp_info_t *su_info_p) +{ + upsdebugx(2, "entering %s(%s)", __func__, su_info_p->info_type); + + if (daisychain_enabled == TRUE) { +/* FIXME?: daisychain */ + for (current_device_number = 1 ; current_device_number <= devices_count ; + current_device_number++) + { + process_template(SU_WALKMODE_INIT, "device", su_info_p); + } + } + else { + if (nut_snmp_get(su_info_p->OID) != NULL) { + dstate_addcmd(su_info_p->info_type); + upsdebugx(1, "%s: adding command '%s'", __func__, su_info_p->info_type); + } + } + return 0; +} + /* process instant command and take action. */ int su_instcmd(const char *cmdname, const char *extradata) { - snmp_info_t *su_info_p = NULL; - int status; - int retval = STAT_INSTCMD_FAILED; - - upsdebugx(2, "entering su_instcmd(%s, %s)", cmdname, extradata); - - if (strncmp(cmdname, "outlet", 6)) - su_info_p = su_find_info(cmdname); - else { - snmp_info_t *tmp_info_p; - char *outlet_number_ptr = strchr(cmdname, '.'); - int outlet_number = atoi(++outlet_number_ptr); - if (dstate_getinfo("outlet.count") == NULL) { - upsdebugx(2, "su_instcmd: can't get outlet.count..."); - return STAT_INSTCMD_UNKNOWN; - } - - upsdebugx(3, "su_instcmd: outlet %i / %i", outlet_number, - atoi(dstate_getinfo("outlet.count"))); - - /* ensure the outlet number is supported! */ - if (outlet_number > atoi(dstate_getinfo("outlet.count"))) { - /* out of bound outlet number */ - upsdebugx(2, "su_instcmd: outlet is out of bound (%i / %s)", - outlet_number, dstate_getinfo("outlet.count")); - return STAT_INSTCMD_INVALID; - } - - /* find back the outlet template */ - char *outlet_cmdname = (char *)xmalloc(SU_INFOSIZE); - sprintf(outlet_cmdname, "outlet.%s%s", "%i", strchr(outlet_number_ptr++, '.')); - upsdebugx(3, "su_instcmd: searching for template\"%s\"", outlet_cmdname); - tmp_info_p = su_find_info(outlet_cmdname); - free(outlet_cmdname); - - /* for an snmp_info_t instance */ - su_info_p = instantiate_info(tmp_info_p, su_info_p); - - /* check if default value is also a template */ - if ((su_info_p->dfl != NULL) && - (strstr(tmp_info_p->dfl, "%i") != NULL)) { - su_info_p->dfl = (char *)xmalloc(SU_INFOSIZE); - sprintf((char *)su_info_p->dfl, tmp_info_p->dfl, - outlet_number - base_nut_outlet_offset()); - } - /* adapt the OID */ - if (su_info_p->OID != NULL) { - sprintf((char *)su_info_p->OID, tmp_info_p->OID, - outlet_number - base_nut_outlet_offset()); - } else { - free_info(su_info_p); - return STAT_INSTCMD_UNKNOWN; - } - } - - if (!su_info_p || !su_info_p->info_type || !(su_info_p->flags & SU_FLAG_OK)) { - upsdebugx(2, "su_instcmd: %s unavailable", cmdname); - - if (!strncmp(cmdname, "outlet", 6)) - free_info(su_info_p); - - return STAT_INSTCMD_UNKNOWN; - } - - /* set value. */ - if (su_info_p->info_flags & ST_FLAG_STRING) { - status = nut_snmp_set_str(su_info_p->OID, extradata ? extradata : su_info_p->dfl); - } else { - status = nut_snmp_set_int(su_info_p->OID, extradata ? atoi(extradata) : su_info_p->info_len); - } - - if (status == FALSE) - upsdebugx(1, "su_instcmd: cannot set value for %s", cmdname); - else { - retval = STAT_INSTCMD_HANDLED; - upsdebugx(1, "su_instcmd: successfully sent command %s", cmdname); - } - - if (!strncmp(cmdname, "outlet", 6)) - free_info(su_info_p); - - return retval; -} - -/* TODO: complete rewrite */ -void su_shutdown_ups(void) -{ - int sdtype = 0; - long pwr_status; - - if (nut_snmp_get_int(OID_pwr_status, &pwr_status) == FALSE) - fatalx(EXIT_FAILURE, "cannot determine UPS status"); - - if (testvar(SU_VAR_SDTYPE)) - sdtype = atoi(getval(SU_VAR_SDTYPE)); - - /* logic from newapc.c */ - switch (sdtype) { - case 3: /* shutdown with grace period */ - upslogx(LOG_INFO, "sending delayed power off command to UPS"); - su_instcmd("shutdown.stayoff", "0"); - break; - case 2: /* instant shutdown */ - upslogx(LOG_INFO, "sending power off command to UPS"); - su_instcmd("load.off", "0"); - break; - case 1: - /* Send a combined set of shutdown commands which can work better */ - /* if the UPS gets power during shutdown process */ - /* Specifically it sends both the soft shutdown 'S' */ - /* and the powerdown after grace period - '@000' commands */ -/* upslogx(LOG_INFO, "UPS - sending shutdown/powerdown"); - if (pwr_status == g_pwr_battery) - su_ups_instcmd(CMD_SOFTDOWN, 0, 0); - su_ups_instcmd(CMD_SDRET, 0, 0); - break; -*/ - default: - /* if on battery... */ -/* if (pwr_status == su_find_valinfo(info_lkp_t *oid2info, "OB")) { - upslogx(LOG_INFO, - "UPS is on battery, sending shutdown command..."); - su_ups_instcmd(CMD_SOFTDOWN, 0, 0); - } else { - upslogx(LOG_INFO, "UPS is online, sending shutdown+return command..."); - su_ups_instcmd(CMD_SDRET, 0, 0); - } -*/ - break; - } + return su_setOID(SU_MODE_INSTCMD, cmdname, extradata); } /* FIXME: the below functions can be removed since these were for loading * the mib2nut information from a file instead of the .h definitions... */ /* return 1 if usable, 0 if not */ -static int parse_mibconf_args(int numargs, char **arg) +static int parse_mibconf_args(size_t numargs, char **arg) { bool_t ret; @@ -1586,9 +4045,9 @@ static int parse_mibconf_args(int numargs, char **arg) } if (ret == FALSE) - upslogx(LOG_ERR, "su_setvar: cannot set value %s for %s", arg[4], arg[3]); + upslogx(LOG_ERR, "%s: cannot set value %s for %s", __func__, arg[4], arg[3]); else - upsdebugx(1, "su_setvar: successfully set %s to \"%s\"", arg[0], arg[4]); + upsdebugx(1, "%s: successfully set %s to \"%s\"", __func__, arg[0], arg[4]); return 1; } @@ -1598,18 +4057,20 @@ static int parse_mibconf_args(int numargs, char **arg) return 1; } + /* called for fatal errors in parseconf like malloc failures */ static void mibconf_err(const char *errmsg) { upslogx(LOG_ERR, "Fatal error in parseconf (*mib.conf): %s", errmsg); } + /* load *mib.conf into an snmp_info_t structure */ void read_mibconf(char *mib) { char fn[SMALLBUF]; PCONF_CTX_t ctx; - upsdebugx(2, "SNMP UPS driver : entering read_mibconf(%s)", mib); + upsdebugx(2, "SNMP UPS driver: entering %s(%s)", __func__, mib); snprintf(fn, sizeof(fn), "%s/snmp/%s.conf", CONFPATH, mib); diff --git a/drivers/snmp-ups.h b/drivers/snmp-ups.h index d130428..6ac670a 100644 --- a/drivers/snmp-ups.h +++ b/drivers/snmp-ups.h @@ -4,11 +4,14 @@ * * Copyright (C) * 2002-2010 Arnaud Quette + * 2015-2021 Eaton (author: Arnaud Quette ) + * 2016-2021 Eaton (author: Jim Klimov ) * 2002-2006 Dmitry Frolov - * J.W. Hoogervorst + * J.W. Hoogervorst * Niels Baggesen * - * Sponsored by MGE UPS SYSTEMS + * Sponsored by Eaton + * and originally by MGE UPS SYSTEMS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,12 +30,14 @@ */ /* TODO list: -- add syscontact/location (to all mib.h or centralized?) - complete shutdown - add enum values to OIDs. -- optimize network flow by constructing one big packet (calling snmp_add_null_var -for each OID request we made), instead of sending many small packets -- add support for registration and traps (manager mode), +- optimize network flow by: + 1) caching OID values (as in usbhid-ups) with timestamping and lifetime + 2) constructing one big packet (calling snmp_add_null_var + for each OID request we made), instead of sending many small packets +- add support for registration and traps (manager mode) + => Issue: 1 trap listener for N snmp-ups drivers! - complete mib2nut data (add all OID translation to NUT) - externalize mib2nut data in .m2n files and load at driver startup using parseconf()... - adjust information logging. @@ -75,11 +80,23 @@ for each OID request we made), instead of sending many small packets #include #include +#ifndef ONE_SEC +/* This macro name disappeared from net-snmp sources and headers + * after v5.9 tag, and was replaced by explicit expression below: */ +# define ONE_SEC (1000L * 1000L) +#endif + /* Force numeric OIDs by disabling MIB loading */ +#ifdef DISABLE_MIB_LOADING +# undef DISABLE_MIB_LOADING +#endif #define DISABLE_MIB_LOADING 1 - -#define DEFAULT_POLLFREQ 30 /* in seconds */ +/* Parameters default values */ +#define DEFAULT_POLLFREQ 30 /* in seconds */ +#define DEFAULT_NETSNMP_RETRIES 5 +#define DEFAULT_NETSNMP_TIMEOUT 1 /* in seconds */ +#define DEFAULT_SEMISTATICFREQ 10 /* in snmpwalk update cycles */ /* use explicit booleans */ #ifndef FALSE @@ -98,10 +115,46 @@ typedef int bool_t; /* typedef void (*interpreter)(char *, char *, int); */ +#ifndef WITH_SNMP_LKP_FUN +/* Recent addition of fun/nuf hooks in info_lkp_t is not well handled by + * all corners of the codebase, e.g. not by DMF. So at least until that + * is fixed, (TODO) we enable those bits of code only optionally during + * a build for particular usage. Conversely, experimenters can define + * this macro to a specific value while building the codebase and see + * what happens under different conditions ;) + */ +# if (defined WITH_DMFMIB) && (WITH_DMFMIB != 0) +# define WITH_SNMP_LKP_FUN 0 +# else +# define WITH_SNMP_LKP_FUN 1 +# endif +#endif + +#ifndef WITH_SNMP_LKP_FUN_DUMMY +# define WITH_SNMP_LKP_FUN_DUMMY 0 +#endif + /* for lookup between OID values and INFO_ value */ typedef struct { - int oid_value; /* OID value */ - const char *info_value; /* INFO_* value */ + int oid_value; /* SNMP OID value */ + const char *info_value; /* NUT INFO_* value */ +#if WITH_SNMP_LKP_FUN +/* FIXME: Currently we do not have a way to provide custom C code + * via DMF - keep old approach until we get the ability, e.g. by + * requiring a LUA implementation to be passed alongside C lookups. + */ +/* + * Currently there are a few cases using a "fun_vp2s" type of lookup + * function, while the "nuf_s2l" type was added for completeness but + * is not really handled and does not have real consumers in the + * existing NUT codebase (static mib2nut tables in *-mib.c files). + * Related to su_find_infoval() (long* => string), su_find_valinfo() + * (string => long) and su_find_strval() (char* => string) routines + * defined below. + */ + const char *(*fun_vp2s)(void *snmp_value); /* optional SNMP to NUT mapping function, converting a pointer to SNMP data (e.g. numeric or string) into a NUT string */ + long (*nuf_s2l)(const char *nut_value); /* optional NUT to SNMP mapping function, converting a NUT string into SNMP numeric data */ +#endif /* WITH_SNMP_LKP_FUN */ } info_lkp_t; /* Structure containing info about one item that can be requested @@ -109,69 +162,131 @@ typedef struct { use sprintf with given format string. If unit is not NONE, values are converted according to the multiplier table */ +typedef uint32_t snmp_info_flags_t; /* To extend when 32 bits become too congested */ +#define PRI_SU_FLAGS PRIu32 + typedef struct { - const char *info_type; /* INFO_ or CMD_ element */ - int info_flags; /* flags to set in addinfo */ - float info_len; /* length of strings if STR, - * cmd value if CMD, multiplier otherwise. */ - const char *OID; /* SNMP OID or NULL */ - const char *dfl; /* default value */ - unsigned long flags; /* my flags */ - info_lkp_t *oid2info; /* lookup table between OID and NUT values */ - int *setvar; /* variable to set for SU_FLAG_SETINT */ + char *info_type; /* INFO_ or CMD_ element */ + int info_flags; /* flags to set in addinfo: see ST_FLAG_* + * defined in include/extstate.h */ + double info_len; /* length of strings if ST_FLAG_STRING, + * multiplier otherwise. */ + char *OID; /* SNMP OID or NULL */ + char *dfl; /* default value */ + snmp_info_flags_t flags; /* snmp-ups internal flags: see SU_* bit-shifts + * defined below (SU_FLAG*, SU_TYPE*, SU_STATUS* + * and others for outlets, phases, daisy-chains, + * etc.) + * NOTE that some *-mib.c mappings can specify + * a zero in this field... better fix that in + * favor of explicit values with a meaning! + * Current code treats such zero values as + * "OK if avail, otherwise discarded". + * NOTE: With C99+ a "long" is guaranteed to be + * at least 4 bytes; consider "unsigned long long" + * when/if we get more than 32 flag values. + */ + info_lkp_t *oid2info; /* lookup table between OID and NUT values */ } snmp_info_t; -#define SU_FLAG_OK (1 << 0) /* show element to upsd. */ -#define SU_FLAG_STATIC (1 << 1) /* retrieve info only once. */ -#define SU_FLAG_ABSENT (1 << 2) /* data is absent in the device, +/* "flags" bits 0..9 */ +#define SU_FLAG_OK (1UL << 0) /* show element to upsd - + * internal to snmp driver */ +#define SU_FLAG_STATIC (1UL << 1) /* retrieve info only once. */ +#define SU_FLAG_ABSENT (1UL << 2) /* data is absent in the device, * use default value. */ -#define SU_FLAG_STALE (1 << 3) /* data stale, don't try too often. */ -#define SU_FLAG_NEGINVALID (1 << 4) /* Invalid if negative value */ -#define SU_FLAG_UNIQUE (1 << 5) /* There can be only be one +#define SU_FLAG_STALE (1UL << 3) /* data stale, don't try too often - + * internal to snmp driver */ +#define SU_FLAG_NEGINVALID (1UL << 4) /* Invalid if negative value */ +#define SU_FLAG_UNIQUE (1UL << 5) /* There can be only be one * provider of this info, * disable the other providers */ -#define SU_FLAG_SETINT (1 << 6) /* save value */ -#define SU_OUTLET (1 << 7) /* outlet template definition */ +/* Note: older releases defined the following flag, but removed it by 2.7.5: + * #define SU_FLAG_SETINT (1UL << 6)*/ /* save value */ +#define SU_FLAG_ZEROINVALID (1UL << 6) /* Invalid if "0" value */ +#define SU_FLAG_NAINVALID (1UL << 7) /* Invalid if "N/A" value */ +#define SU_CMD_OFFSET (1UL << 8) /* Add +1 to the OID index */ + +#define SU_FLAG_SEMI_STATIC (1UL << 9) /* Refresh this entry once in several walks + * (for R/W values user can set on device, + * like descriptions or contacts) */ + /* Notes on outlet templates usage: * - outlet.count MUST exist and MUST be declared before any outlet template + * Otherwise, the driver will try to determine it by itself... * - the first outlet template MUST NOT be a server side variable (ie MUST have * a valid OID) in order to detect the base SNMP index (0 or 1) */ -/* status string components */ -#define SU_STATUS_PWR (0 << 8) /* indicates power status element */ -#define SU_STATUS_BATT (1 << 8) /* indicates battery status element */ -#define SU_STATUS_CAL (2 << 8) /* indicates calibration status element */ -#define SU_STATUS_RB (3 << 8) /* indicates replace battery status element */ -#define SU_STATUS_NUM_ELEM 4 -#define SU_STATUS_INDEX(t) (((t) >> 8) & 7) +/* "flags" bit 10 */ +#define SU_OUTLET_GROUP (1UL << 10) /* outlet group template definition */ +#define SU_OUTLET (1UL << 11) /* outlet template definition */ /* Phase specific data */ -#define SU_PHASES (0x3F << 12) -#define SU_INPHASES (0x3 << 12) -#define SU_INPUT_1 (1 << 12) /* only if 1 input phase */ -#define SU_INPUT_3 (1 << 13) /* only if 3 input phases */ -#define SU_OUTPHASES (0x3 << 14) -#define SU_OUTPUT_1 (1 << 14) /* only if 1 output phase */ -#define SU_OUTPUT_3 (1 << 15) /* only if 3 output phases */ -#define SU_BYPPHASES (0x3 << 16) -#define SU_BYPASS_1 (1 << 16) /* only if 1 bypass phase */ -#define SU_BYPASS_3 (1 << 17) /* only if 3 bypass phases */ +/* "flags" bits 12..17 */ +#define SU_PHASES (0x0000003F << 12) +#define SU_INPHASES (0x00000003 << 12) +#define SU_INPUT_1 (1UL << 12) /* only if 1 input phase */ +#define SU_INPUT_3 (1UL << 13) /* only if 3 input phases */ +#define SU_OUTPHASES (0x00000003 << 14) +#define SU_OUTPUT_1 (1UL << 14) /* only if 1 output phase */ +#define SU_OUTPUT_3 (1UL << 15) /* only if 3 output phases */ +#define SU_BYPPHASES (0x00000003 << 16) +#define SU_BYPASS_1 (1UL << 16) /* only if 1 bypass phase */ +#define SU_BYPASS_3 (1UL << 17) /* only if 3 bypass phases */ /* FIXME: use input.phases and output.phases to replace this */ - /* hints for su_ups_set, applicable only to rw vars */ -#define SU_TYPE_INT (0 << 18) /* cast to int when setting value */ -#define SU_TYPE_STRING (1 << 18) /* cast to string. FIXME: redundant with ST_FLAG_STRING */ -#define SU_TYPE_TIME (2 << 18) /* cast to int */ -#define SU_TYPE_CMD (3 << 18) /* instant command */ -#define SU_TYPE(t) ((t)->flags & (7 << 18)) +/* "flags" bits 18..20 */ +#define SU_TYPE_INT (1UL << 18) /* cast to int when setting value */ +#define SU_TYPE_TIME (1UL << 19) /* cast to int */ +#define SU_TYPE_CMD (1UL << 20) /* instant command */ +/* The following helper macro is used like: + * if (SU_TYPE(su_info_p) == SU_TYPE_CMD) { ... } + */ +#define SU_TYPE(t) ((t)->flags & (7UL << 18)) +/* Daisychain template definition */ +/* the following 2 flags specify the position of the daisychain device index + * in the formatting string. This is useful when considering daisychain with + * templates, such as outlets / outlets groups, which already have a format + * string specifier */ +/* "flags" bits 21..23 (and 24 reserved for DMF) */ +#define SU_TYPE_DAISY_1 (1UL << 21) /* Daisychain index is the 1st %i specifier in a template with more than one */ +#define SU_TYPE_DAISY_2 (1UL << 22) /* Daisychain index is the 2nd %i specifier in a template with more than one */ +#define SU_TYPE_DAISY(t) ((t)->flags & (11UL << 21)) /* Mask the SU_TYPE_DAISY_{1,2,MASTER_ONLY} but not SU_DAISY */ +#define SU_DAISY (1UL << 23) /* Daisychain template definition - set at run-time for devices with detected "device.count" over 1 */ +/* NOTE: Previously SU_DAISY had same bit-flag value as SU_TYPE_DAISY_2 */ +#define SU_TYPE_DAISY_MASTER_ONLY (1UL << 24) /* Only valid for daisychain master (device.1) */ + +/* Free slot: (1UL << 25) */ + +#define SU_AMBIENT_TEMPLATE (1UL << 26) /* ambient template definition */ + +/* Reserved slot -- to import from DMF branch codebase: +//#define SU_FLAG_FUNCTION (1UL << 27) +*/ + +/* status string components + * FIXME: these should be removed, since there is no added value. + * Ie, this can be guessed from info->type! */ + +/* "flags" bits 28..31 */ +#define SU_STATUS_PWR (1UL << 28) /* indicates power status element */ +#define SU_STATUS_BATT (1UL << 29) /* indicates battery status element */ +#define SU_STATUS_CAL (1UL << 30) /* indicates calibration status element */ +#define SU_STATUS_RB (1UL << 31) /* indicates replace battery status element */ +#define SU_STATUS_NUM_ELEM 4 /* Obsolete? No references found in codebase */ +#define SU_STATUS_INDEX(t) (((unsigned long)(t) >> 28) & 15UL) + +/* Despite similar names, definitons below are not among the bit-flags ;) */ #define SU_VAR_COMMUNITY "community" #define SU_VAR_VERSION "snmp_version" +#define SU_VAR_RETRIES "snmp_retries" +#define SU_VAR_TIMEOUT "snmp_timeout" +#define SU_VAR_SEMISTATICFREQ "semistaticfreq" #define SU_VAR_MIBS "mibs" #define SU_VAR_POLLFREQ "pollfreq" -#define SU_VAR_SDTYPE "sdtype" /* SNMP v3 related parameters */ #define SU_VAR_SECLEVEL "secLevel" #define SU_VAR_SECNAME "secName" @@ -180,6 +295,8 @@ typedef struct { #define SU_VAR_AUTHPROT "authProtocol" #define SU_VAR_PRIVPROT "privProtocol" +#define SU_VAR_ONDELAY "ondelay" +#define SU_VAR_OFFDELAY "offdelay" #define SU_INFOSIZE 128 #define SU_BUFSIZE 32 @@ -192,10 +309,20 @@ typedef struct { #define SU_WALKMODE_INIT 0 #define SU_WALKMODE_UPDATE 1 +/* modes for su_setOID */ +#define SU_MODE_INSTCMD 1 +#define SU_MODE_SETVAR 2 + /* log spew limiters */ #define SU_ERR_LIMIT 10 /* start limiting after this many errors in a row */ #define SU_ERR_RATE 100 /* only print every nth error once limiting starts */ +typedef struct { + const char * OID; + const char *status_value; /* when not NULL, set ups.status to this */ + const char *alarm_value; /* when not NULL, set ups.alarm to this */ +} alarms_info_t; + typedef struct { const char *mib_name; const char *mib_version; @@ -204,7 +331,7 @@ typedef struct { snmp_info_t *snmp_info; /* pointer to the good Snmp2Nut lookup data */ const char *sysOID; /* OID to match against sysOID, aka MIB * main entry point */ - + alarms_info_t *alarms_info; } mib2nut_info_t; /* Common SNMP functions */ @@ -213,10 +340,12 @@ void nut_snmp_cleanup(void); struct snmp_pdu *nut_snmp_get(const char *OID); bool_t nut_snmp_get_str(const char *OID, char *buf, size_t buf_len, info_lkp_t *oid2info); +bool_t nut_snmp_get_oid(const char *OID, char *buf, size_t buf_len); bool_t nut_snmp_get_int(const char *OID, long *pval); bool_t nut_snmp_set(const char *OID, char type, const char *value); bool_t nut_snmp_set_str(const char *OID, const char *value); bool_t nut_snmp_set_int(const char *OID, long value); +bool_t nut_snmp_set_time(const char *OID, long value); void nut_snmp_perror(struct snmp_session *sess, int status, struct snmp_pdu *response, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 4, 5))); @@ -227,26 +356,76 @@ void su_init_instcmds(void); void su_setuphandlers(void); /* need to deal with external function ptr */ void su_setinfo(snmp_info_t *su_info_p, const char *value); void su_status_set(snmp_info_t *, long value); +void su_alarm_set(snmp_info_t *, long value); snmp_info_t *su_find_info(const char *type); bool_t snmp_ups_walk(int mode); bool_t su_ups_get(snmp_info_t *su_info_p); bool_t load_mib2nut(const char *mib); -const char *su_find_infoval(info_lkp_t *oid2info, long value); -long su_find_valinfo(info_lkp_t *oid2info, char* value); +/* Practical logic around lookup functions, see fun_vp2s and nuf_s2l + * fields in struct info_lkp_t */ +const char *su_find_infoval(info_lkp_t *oid2info, void *value); +long su_find_valinfo(info_lkp_t *oid2info, const char* value); +const char *su_find_strval(info_lkp_t *oid2info, void *value); + +/***************************************************** + * Common conversion structs and functions provided by snmp-ups-helpers.c + * so they can be used and so "shared" by different subdrivers + *****************************************************/ + +const char *su_usdate_to_isodate_info_fun(void *raw_date); +extern info_lkp_t su_convert_to_iso_date_info[]; +/* Name the mapping location in that array for consumers to reference */ +#define FUNMAP_USDATE_TO_ISODATE 0 + +/* Process temperature value according to 'temperature_unit' */ +const char *su_temperature_read_fun(void *raw_snmp_value); + +/* Temperature handling, to convert back to Celsius (NUT standard) */ +extern int temperature_unit; + +#define TEMPERATURE_UNKNOWN 0 +#define TEMPERATURE_CELSIUS 1 +#define TEMPERATURE_KELVIN 2 +#define TEMPERATURE_FAHRENHEIT 3 + +/***************************************************** + * End of Subdrivers shared helpers functions + *****************************************************/ int su_setvar(const char *varname, const char *val); int su_instcmd(const char *cmdname, const char *extradata); void su_shutdown_ups(void); +void set_delays(void); + void read_mibconf(char *mib); -struct snmp_session g_snmp_sess, *g_snmp_sess_p; -const char *OID_pwr_status; -int g_pwr_battery; -int pollfreq; /* polling frequency */ +extern struct snmp_session g_snmp_sess, *g_snmp_sess_p; +extern const char *OID_pwr_status; +extern int g_pwr_battery; +extern int pollfreq; /* polling frequency */ extern int input_phases, output_phases, bypass_phases; +extern int semistaticfreq; /* semistatic entry update frequency */ + +/* pointer to the Snmp2Nut lookup table */ +extern mib2nut_info_t *mib2nut_info; +/* FIXME: to be trashed */ +extern snmp_info_t *snmp_info; +extern alarms_info_t *alarms_info; + +/* Common daisychain structure and functions */ + +bool_t daisychain_init(void); +int su_addcmd(snmp_info_t *su_info_p); + +/* Structure containing info about each daisychain device, including phases + * for input, output and bypass */ +typedef struct { + long input_phases; + long output_phases; + long bypass_phases; +} daisychain_info_t; #endif /* SNMP_UPS_H */ - diff --git a/drivers/socomec_jbus.c b/drivers/socomec_jbus.c new file mode 100644 index 0000000..fd1f865 --- /dev/null +++ b/drivers/socomec_jbus.c @@ -0,0 +1,492 @@ +/* socomec_jbus.c - Driver for Socomec JBUS UPS + * + * Copyright (C) + * 2021 Thanos Chatziathanassiou + * + * Based on documentation found freely on + * https://www.socomec.com/files/live/sites/systemsite/files/GB-JBUS-MODBUS-for-Delphys-MP-and-Delphys-MX-operating-manual.pdf + * but with dubious legal license. The document itself states: + * ``CAUTION : “This is a product for restricted sales distribution to informed partners. + * Installation restrictions or additional measures may be needed to prevent disturbances'' + * YMMV + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "main.h" +#include + +#define DRIVER_NAME "Socomec jbus driver" +#define DRIVER_VERSION "0.06" + +#define CHECK_BIT(var,pos) ((var) & (1<<(pos))) +#define MODBUS_SLAVE_ID 1 +#define BATTERY_RUNTIME_CRITICAL 15 + +/* Variables */ +static modbus_t *modbus_ctx = NULL; + +static int mrir(modbus_t * arg_ctx, int addr, int nb, uint16_t * dest); + +/* driver description structure */ +upsdrv_info_t upsdrv_info = { + DRIVER_NAME, + DRIVER_VERSION, + "Thanos Chatziathanassiou \n", + DRV_BETA, + {NULL} +}; + +void upsdrv_initinfo(void) +{ + upsdebugx(2, "upsdrv_initinfo"); + + uint16_t tab_reg[12]; + int r; + + dstate_setinfo("device.mfr", "socomec jbus"); + dstate_setinfo("device.model", "Socomec Generic"); + + upsdebugx(2, "initial read"); + + /* + this is a neat trick, but not really helpful right now + https://stackoverflow.com/questions/25811662/spliting-an-hex-into-2-hex-values/41733170#41733170 + uint8_t *lowbyte; + uint8_t *hibyte; + */ + + r = mrir(modbus_ctx, 0x1000, 12, tab_reg); + + if (r == -1) { + fatalx(EXIT_FAILURE, "failed to read UPS code from JBUS. r is %d error %s", r, modbus_strerror(errno)); + } + + upsdebugx(2, "read UPS Code %d", tab_reg[0]); + + if (tab_reg[1]) { + upsdebugx(2, "read UPS Power %d (kVA * 10)", tab_reg[1]); + dstate_setinfo("ups.power", "%u", tab_reg[1]*100 ); + } + + /* known Socomec Models */ + switch (tab_reg[0]) { + case 130: + dstate_setinfo("ups.model", "%s", "DIGYS"); + break; + + case 515: + dstate_setinfo("ups.model", "%s", "DELPHYS MX"); + break; + + case 516: + dstate_setinfo("ups.model", "%s", "DELPHYS MX elite"); + break; + + default: + dstate_setinfo("ups.model", "Unknown Socomec JBUS. Send id %u and specify the model", tab_reg[0]); + } + + if (tab_reg[3] && tab_reg[4] && tab_reg[5] && tab_reg[6] && tab_reg[7]) { + dstate_setinfo("ups.serial", "%c%c%c%c%c%c%c%c%c%c", + (tab_reg[3]&0xFF), (tab_reg[3]>>8), + (tab_reg[4]&0xFF), (tab_reg[4]>>8), + (tab_reg[5]&0xFF), (tab_reg[5]>>8), + (tab_reg[6]&0xFF), (tab_reg[6]>>8), + (tab_reg[7]&0xFF), (tab_reg[7]>>8) + ); + } + + /* upsh.instcmd = instcmd; */ + /* upsh.setvar = setvar; */ +} + +void upsdrv_updateinfo(void) +{ + upsdebugx(2, "upsdrv_updateinfo"); + + uint16_t tab_reg[64]; + int r; + + status_init(); + + /* ups configuration */ + r = mrir(modbus_ctx, 0x10E0, 32, tab_reg); + + if (r == -1 || !tab_reg[0]) { + upsdebugx(2, "Did not receive any data from the UPS at 0x10E0 ! Going stale r is %d error %s", r, modbus_strerror(errno)); + dstate_datastale(); + return; + } + + dstate_setinfo("input.voltage", "%u", tab_reg[0]); + dstate_setinfo("output.voltage", "%u", tab_reg[1]); + dstate_setinfo("input.frequency", "%u", tab_reg[2]); + dstate_setinfo("output.frequency", "%u", tab_reg[3]); + + upsdebugx(2, "battery capacity (Ah * 10) %u", tab_reg[8]); + upsdebugx(2, "battery elements %u", tab_reg[9]); + + /* time and date */ + r = mrir(modbus_ctx, 0x1360, 4, tab_reg); + if (r == -1) { + upsdebugx(2, "Did not receive any data from the UPS at 0x1360 ! Ignoring ? r is %d error %s", r, modbus_strerror(errno)); + } + + dstate_setinfo("ups.time", "%02d:%02d:%02d", (tab_reg[1]&0xFF), (tab_reg[0]>>8), (tab_reg[0]&0xFF) ); + dstate_setinfo("ups.date", "%04d/%02d/%02d", (tab_reg[3]+2000), (tab_reg[2]>>8), (tab_reg[1]>>8) ); + + /* ups status */ + r = mrir(modbus_ctx, 0x1020, 6, tab_reg); + + if (r == -1) { + upsdebugx(2, "Did not receive any data from the UPS at 0x1020 ! Ignoring ? r is %d error %s", r, modbus_strerror(errno)); + /* + dstate_datastale(); + return; + */ + } + + if (CHECK_BIT(tab_reg[0], 0)) + upsdebugx(2, "Rectifier Input supply present"); + if (CHECK_BIT(tab_reg[0], 1)) + upsdebugx(2, "Inverter ON "); + if (CHECK_BIT(tab_reg[0], 2)) + upsdebugx(2, "Rectifier ON"); + if (CHECK_BIT(tab_reg[0], 3)) + upsdebugx(2, "Load protected by inverter"); + if (CHECK_BIT(tab_reg[0], 4)) + upsdebugx(2, "Load on automatic bypass"); + if (CHECK_BIT(tab_reg[0], 5)) + upsdebugx(2, "Load on battery"); + if (CHECK_BIT(tab_reg[0], 6)) + upsdebugx(2, "Remote controls disable"); + if (CHECK_BIT(tab_reg[0], 7)) + upsdebugx(2, "Eco-mode ON"); + + if (CHECK_BIT(tab_reg[0], 14)) + upsdebugx(2, "Battery Test failed"); + if (CHECK_BIT(tab_reg[0], 15)) + upsdebugx(2, "Battery near end of backup time"); + if (CHECK_BIT(tab_reg[0], 16)) + upsdebugx(2, "Battery disacharged"); + + if (CHECK_BIT(tab_reg[1], 0)) + upsdebugx(2, "Battery OK"); + if (CHECK_BIT(tab_reg[1], 10)) + upsdebugx(2, "Bypass input supply present"); + if (CHECK_BIT(tab_reg[1], 11)) + upsdebugx(2, "Battery charging"); + if (CHECK_BIT(tab_reg[1], 12)) + upsdebugx(2, "Bypass input frequency out of tolerance"); + + if (CHECK_BIT(tab_reg[2], 0)) + upsdebugx(2, "Unit operating"); + + if (CHECK_BIT(tab_reg[3], 0)) + upsdebugx(2, "Maintenance mode active"); + + if (CHECK_BIT(tab_reg[4], 0)) + upsdebugx(2, "Boost charge ON"); + if (CHECK_BIT(tab_reg[4], 2)) + upsdebugx(2, "Inverter switch closed"); + if (CHECK_BIT(tab_reg[4], 3)) + upsdebugx(2, "Bypass breaker closed"); + if (CHECK_BIT(tab_reg[4], 4)) + upsdebugx(2, "Maintenance bypass breaker closed"); + if (CHECK_BIT(tab_reg[4], 5)) + upsdebugx(2, "Remote maintenance bypass breaker closed"); + if (CHECK_BIT(tab_reg[4], 6)) + upsdebugx(2, "Output breaker closed (Q3)"); + if (CHECK_BIT(tab_reg[4], 9)) + upsdebugx(2, "Unit working"); + if (CHECK_BIT(tab_reg[4], 12)) + upsdebugx(2, "normal mode active"); + + /* alarms */ + r = mrir(modbus_ctx, 0x1040, 4, tab_reg); + + alarm_init(); + + if (r == -1) { + upsdebugx(2, "Did not receive any data from the UPS at 0x1040 ! Ignoring ? r is %d error %s", r, modbus_strerror(errno)); + /* + dstate_datastale(); + return; + */ + } + + if (CHECK_BIT(tab_reg[0], 0)) { + upsdebugx(2, "General Alarm"); + alarm_set("General Alarm present."); + } + if (CHECK_BIT(tab_reg[0], 1)) { + upsdebugx(2, "Battery failure"); + alarm_set("Battery failure."); + } + if (CHECK_BIT(tab_reg[0], 2)) { + upsdebugx(2, "UPS overload"); + alarm_set("Overload fault."); + } + if (CHECK_BIT(tab_reg[0], 4)) { + upsdebugx(2, "Control failure (com, internal supply...)"); + alarm_set("Control failure (com, internal supply...)"); + } + if (CHECK_BIT(tab_reg[0], 5)) { + upsdebugx(2, "Rectifier input supply out of tolerance "); + alarm_set("Rectifier input supply out of tolerance."); + } + if (CHECK_BIT(tab_reg[0], 6)) { + upsdebugx(2, "Bypass input supply out of tolerance "); + alarm_set("Bypass input supply out of tolerance."); + } + if (CHECK_BIT(tab_reg[0], 7)) { + upsdebugx(2, "Over temperature alarm "); + alarm_set("Over temperature fault."); + } + if (CHECK_BIT(tab_reg[0], 8)) { + upsdebugx(2, "Maintenance bypass closed"); + alarm_set("Maintenance bypass closed."); + } + if (CHECK_BIT(tab_reg[0], 10)) { + upsdebugx(2, "Battery charger fault"); + alarm_set("Battery charger fault."); + } + + if (CHECK_BIT(tab_reg[1], 1)) + upsdebugx(2, "Improper condition of use"); + if (CHECK_BIT(tab_reg[1], 2)) + upsdebugx(2, "Inverter stopped for overload (or bypass transfer)"); + if (CHECK_BIT(tab_reg[1], 3)) + upsdebugx(2, "Microprocessor control system"); + if (CHECK_BIT(tab_reg[1], 5)) + upsdebugx(2, "Synchronisation fault (PLL fault)"); + if (CHECK_BIT(tab_reg[1], 6)) + upsdebugx(2, "Rectifier input supply fault"); + if (CHECK_BIT(tab_reg[1], 7)) + upsdebugx(2, "Rectifier preventive alarm"); + if (CHECK_BIT(tab_reg[1], 9)) + upsdebugx(2, "Inverter preventive alarm"); + if (CHECK_BIT(tab_reg[1], 10)) + upsdebugx(2, "Charger general alarm"); + if (CHECK_BIT(tab_reg[1], 13)) + upsdebugx(2, "Bypass preventive alarm"); + if (CHECK_BIT(tab_reg[1], 15)) { + upsdebugx(2, "Imminent STOP"); + alarm_set("Imminent STOP."); + } + + if (CHECK_BIT(tab_reg[2], 12)) { + upsdebugx(2, "Servicing alarm"); + alarm_set("Servicing alarm."); + } + if (CHECK_BIT(tab_reg[2], 15)) + upsdebugx(2, "Battery room alarm"); + + if (CHECK_BIT(tab_reg[3], 0)) { + upsdebugx(2, "Maintenance bypass alarm"); + alarm_set("Maintenance bypass."); + } + if (CHECK_BIT(tab_reg[3], 1)) { + upsdebugx(2, "Battery discharged"); + alarm_set("Battery discharged."); + } + if (CHECK_BIT(tab_reg[3], 3)) + upsdebugx(2, "Synoptic alarm"); + if (CHECK_BIT(tab_reg[3], 4)) { + upsdebugx(2, "Critical Rectifier fault"); + alarm_set("Critical Rectifier fault."); + } + if (CHECK_BIT(tab_reg[3], 6)) { + upsdebugx(2, "Critical Inverter fault"); + alarm_set("Critical Inverter fault."); + } + if (CHECK_BIT(tab_reg[3], 10)) + upsdebugx(2, "ESD activated"); + if (CHECK_BIT(tab_reg[3], 11)) { + upsdebugx(2, "Battery circuit open"); + alarm_set("Battery circuit open."); + } + if (CHECK_BIT(tab_reg[3], 14)) { + upsdebugx(2, "Bypass critical alarm"); + alarm_set("Bypass critical alarm."); + } + + /* measurements */ + r = mrir(modbus_ctx, 0x1060, 48, tab_reg); + + if (r == -1) { + upsdebugx(2, "Did not receive any data from the UPS at 0x1060 ! Ignoring ? r is %d error %s", r, modbus_strerror(errno)); + /* + dstate_datastale(); + return; + */ + } + + if (tab_reg[1] == 0xFFFF && tab_reg[2] == 0xFFFF) { + /* this a 1-phase model */ + dstate_setinfo("input.phases", "1" ); + dstate_setinfo("ups.load", "%u", tab_reg[0] ); + + dstate_setinfo("input.bypass.voltage", "%u", tab_reg[6] ); + + dstate_setinfo("output.voltage", "%u", tab_reg[9] ); + + if (tab_reg[15] != 0xFFFF) + dstate_setinfo("output.current", "%u", tab_reg[15] ); + } + else { + /* this a 3-phase model */ + dstate_setinfo("input.phases", "3" ); + + dstate_setinfo("ups.load", "%u", tab_reg[3] ); + + dstate_setinfo("ups.L1.load", "%u", tab_reg[0] ); + dstate_setinfo("ups.L2.load", "%u", tab_reg[1] ); + dstate_setinfo("ups.L3.load", "%u", tab_reg[2] ); + + dstate_setinfo("input.bypass.L1-N.voltage", "%u", tab_reg[6] ); + dstate_setinfo("input.bypass.L2-N.voltage", "%u", tab_reg[7] ); + dstate_setinfo("input.bypass.L3-N.voltage", "%u", tab_reg[8] ); + + dstate_setinfo("output.L1-N.voltage", "%u", tab_reg[9] ); + dstate_setinfo("output.L2-N.voltage", "%u", tab_reg[10] ); + dstate_setinfo("output.L3-N.voltage", "%u", tab_reg[11] ); + + if (tab_reg[15] != 0xFFFF) + dstate_setinfo("output.L1.current", "%u", tab_reg[15] ); + + if (tab_reg[16] != 0xFFFF) + dstate_setinfo("output.L2.current", "%u", tab_reg[16] ); + + if (tab_reg[17] != 0xFFFF) + dstate_setinfo("output.L3.current", "%u", tab_reg[17] ); + } + + dstate_setinfo("battery.charge", "%u", tab_reg[4] ); + dstate_setinfo("battery.capacity", "%u", (tab_reg[5]/10) ); + dstate_setinfo("battery.voltage", "%.2f", (double) (tab_reg[20]) / 10); + dstate_setinfo("battery.current", "%.2f", (double) (tab_reg[24]) / 10 ); + dstate_setinfo("battery.runtime", "%u", tab_reg[23] ); + + dstate_setinfo("input.bypass.frequency", "%u", (tab_reg[18]/10) ); + dstate_setinfo("output.frequency", "%u", (tab_reg[19]/10) ); + + if (tab_reg[22] != 0xFFFF) { + dstate_setinfo("ambient.1.present", "yes"); + dstate_setinfo("ambient.1.temperature", "%u", tab_reg[22] ); + } + + if (tab_reg[23] == 0xFFFF) { + /* battery.runtime == 0xFFFF means we're on mains */ + status_set("OL"); + } + else if (tab_reg[23] > BATTERY_RUNTIME_CRITICAL) { + /* we still have mora than BATTERY_RUNTIME_CRITICAL min left ? */ + status_set("OB"); + } + else { + status_set("LB"); + } + + /*TODO: + --essential + ups.status TRIM/BOOST/OVER + ups.alarm + + --dangerous + ups.shutdown + shutdown.return + shutdown.stop + shutdown.reboot + shutdown.reboot.graceful + bypass.start + beeper.enable + beeper.disable + */ + + alarm_commit(); + status_commit(); + dstate_dataok(); + + return; +} + +void upsdrv_shutdown(void) + __attribute__((noreturn)); + +void upsdrv_shutdown(void) +{ + fatalx(EXIT_FAILURE, "shutdown not supported"); +} + +void upsdrv_help(void) +{ +} + +/* list flags and values that you want to receive via -x */ +void upsdrv_makevartable(void) +{ +} + +void upsdrv_initups(void) +{ + int r; + upsdebugx(2, "upsdrv_initups"); + + modbus_ctx = modbus_new_rtu(device_path, 9600, 'N', 8, 1); + if (modbus_ctx == NULL) + fatalx(EXIT_FAILURE, "Unable to create the libmodbus context"); + + r = modbus_set_slave(modbus_ctx, MODBUS_SLAVE_ID); /* slave ID */ + if (r < 0) { + modbus_free(modbus_ctx); + fatalx(EXIT_FAILURE, "Invalid modbus slave ID %d",MODBUS_SLAVE_ID); + } + + if (modbus_connect(modbus_ctx) == -1) { + modbus_free(modbus_ctx); + fatalx(EXIT_FAILURE, "modbus_connect: unable to connect: %s", modbus_strerror(errno)); + } + +} + +void upsdrv_cleanup(void) +{ + if (modbus_ctx != NULL) { + modbus_close(modbus_ctx); + modbus_free(modbus_ctx); + } +} + +/* Modbus Read Input Registers */ +static int mrir(modbus_t * arg_ctx, int addr, int nb, uint16_t * dest) +{ + int r, i; + + /* zero out the thing, because we might have reused it */ + for (i=0; i + Copyright (C) 2004 Silvino B. Magalhães + 2019 Roberto Panerai Velloso This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,7 +24,12 @@ 2005/06/30 - Version 0.41 - patch for solaris compability 2005/07/01 - Version 0.50 - add internal e external shutdown programming 2005/08/18 - Version 0.60 - save external shutdown programming to ups, - and support new cables for solis 3 + and support new cables for solis 3 + 2015/09/19 - Version 0.65 - patch for correct reading for Microsol Back-Ups BZ1200-BR + 2017/12/21 - Version 0.66 - remove memory leaks (unfreed strdup()s); + remove ser_flush_in calls that were causing desync issues; + other minor improvements in source code. + (see the version control logs for more recent updates) Microsol contributed with UPS Solis 1.5 HS 1.5 KVA for my tests. @@ -31,29 +37,33 @@ */ +#include "main.h" /* Includes "config.h", must be first */ + #include #include - -#include "main.h" +#include "nut_stdint.h" #include "serial.h" +#include "nut_float.h" #include "solis.h" #include "timehead.h" #define DRIVER_NAME "Microsol Solis UPS driver" -#define DRIVER_VERSION "0.61" +#define DRIVER_VERSION "0.68" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, - "Silvino B. Magalhães ", + "Silvino B. Magalhães " \ + "Roberto Panerai Velloso ", DRV_STABLE, { NULL } }; #define false 0 #define true 1 -#define ENDCHAR 13 /* replies end with CR */ +#define RESP_END 0xFE +#define ENDCHAR 13 /* replies end with CR */ /* solis commands */ #define CMD_UPSCONT 0xCC #define CMD_SHUT 0xDD @@ -90,11 +100,11 @@ upsdrv_info_t upsdrv_info = { #define NO_EVENT "No events\n" #define UPS_TIME "UPS internal Time %0d:%02d:%02d\n" #define PRG_DAYS "Programming Shutdown Sun Mon Tue Wed Thu Fri Sat\n" -#define PRG_ONON "External shutdown programming ative\n" -#define PRG_ONOU "Internal shutdown programming ative\n" +#define PRG_ONON "External shutdown programming active\n" +#define PRG_ONOU "Internal shutdown programming atcive\n" #define TIME_OFF "UPS Time power off %02d:%02d\n" #define TIME_ON "UPS Time power on %02d:%02d\n" -#define PRG_ONOF "Shutdown programming not atived\n" +#define PRG_ONOF "Shutdown programming not activated\n" #define TODAY_DD "Shutdown today at %02d:%02d\n" #define SHUT_NOW "Shutdown now!\n" #endif @@ -102,327 +112,173 @@ upsdrv_info_t upsdrv_info = { #define FMT_DAYS " %d %d %d %d %d %d %d\n" /* convert standard days string to firmware format */ -static char* convdays( char *cop ) -{ +static char* convert_days(char *cop) { + static char alt[8]; - char *stra; - char alt[8]; - int i, ish, fim, iw; - iw = weekn; - if ( iw == 6) + int ish, fim; + if (weekn >= 6 || weekn < 0) ish = 0; else - ish = 1 + iw; + ish = 1 + weekn; fim = 7 - ish; /* rotate left only 7 bits */ - for(i=0; i < fim; i++) { - alt[i] = cop[i+ish]; - } + memcpy(alt, &cop[ish], (size_t)fim); - if ( ish > 0 ) { - - for(i=0; i < ish; i++) { - alt[i+fim] = cop[i]; - } - } + if (ish > 0) + memcpy(&alt[fim], cop, (size_t)ish); alt[7] = 0; /* string terminator */ - - stra = strdup( alt ); - return stra; + + return alt; } -static int IsBinary(char ch ) -{ - if( ch == '1' || ch == '0' ) - return 1; - else - return 0; +inline static int is_binary(char ch ) { + return ( ch == '1' || ch == '0' ); } /* convert string to binary */ -static int Binary( char *nome ) -{ +static uint8_t str2bin( char *binStr ) { + uint8_t result = 0; + int i; - char ch, cc; - int cont=0, nint = 1, tobin=0; - int ex, nbin; - - while( *nome && ( cont < 7 ) ) { - ch = *nome; - if( !(IsBinary( ch ) ) ) - nint = 0; + for (i = 0; i < 7; ++i) { + char ch = binStr[i]; + if (is_binary(ch)) + result += ( (ch - '0') << (6 - i) ); else - { - if( ch == '1') { - cc = 1; - ex = (6 - cont); - nbin = cc<> (5 - weekn - i)) & 0x01; - switch ( iw ) - { - case 0: /* sunday */ - { - alt[0] = ( ( ( dweek & 0x20 ) == 0x20 ) ); - alt[1] = ( ( ( dweek & 0x10 ) == 0x10 ) ); - alt[2] = ( ( ( dweek & 0x08 ) == 0x08 ) ); - alt[3] = ( ( ( dweek & 0x04 ) == 0x04 ) ); - alt[4] = ( ( ( dweek & 0x02 ) == 0x02 ) ); - alt[5] = ( ( ( dweek & 0x01 ) == 0x01 ) ); - alt[6] = ( ( ( dweek & 0x40 ) == 0x40 ) ); - break; - } - case 1: - { - alt[0] = ( ( ( dweek & 0x10 ) == 0x10 ) ); - alt[1] = ( ( ( dweek & 0x08 ) == 0x08 ) ); - alt[2] = ( ( ( dweek & 0x04 ) == 0x04 ) ); - alt[3] = ( ( ( dweek & 0x02 ) == 0x02 ) ); - alt[4] = ( ( ( dweek & 0x01 ) == 0x01 ) ); - alt[5] = ( ( ( dweek & 0x40 ) == 0x40 ) ); - alt[6] = ( ( ( dweek & 0x20 ) == 0x20 ) ); - break; - } - case 2: - { - alt[0] = ( ( ( dweek & 0x08 ) == 0x08 ) ); - alt[1] = ( ( ( dweek & 0x04 ) == 0x04 ) ); - alt[2] = ( ( ( dweek & 0x02 ) == 0x02 ) ); - alt[3] = ( ( ( dweek & 0x01 ) == 0x01 ) ); - alt[4] = ( ( ( dweek & 0x40 ) == 0x40 ) ); - alt[5] = ( ( ( dweek & 0x20 ) == 0x20 ) ); - alt[6] = ( ( ( dweek & 0x10 ) == 0x10 ) ); - break; - } - case 3: - { - alt[0] = ( ( ( dweek & 0x04 ) == 0x04 ) ); - alt[1] = ( ( ( dweek & 0x02 ) == 0x02 ) ); - alt[2] = ( ( ( dweek & 0x01 ) == 0x01 ) ); - alt[3] = ( ( ( dweek & 0x40 ) == 0x40 ) ); - alt[4] = ( ( ( dweek & 0x20 ) == 0x20 ) ); - alt[5] = ( ( ( dweek & 0x10 ) == 0x10 ) ); - alt[6] = ( ( ( dweek & 0x08 ) == 0x08 ) ); - break; - } - case 4: - { - alt[0] = ( ( ( dweek & 0x02 ) == 0x02 ) ); - alt[1] = ( ( ( dweek & 0x01 ) == 0x01 ) ); - alt[2] = ( ( ( dweek & 0x40 ) == 0x40 ) ); - alt[3] = ( ( ( dweek & 0x20 ) == 0x20 ) ); - alt[4] = ( ( ( dweek & 0x10 ) == 0x10 ) ); - alt[5] = ( ( ( dweek & 0x08 ) == 0x08 ) ); - alt[6] = ( ( ( dweek & 0x04 ) == 0x04 ) ); - break; - } - case 5: - { - alt[0] = ( ( ( dweek & 0x01 ) == 0x01 ) ); - alt[1] = ( ( ( dweek & 0x40 ) == 0x40 ) ); - alt[2] = ( ( ( dweek & 0x20 ) == 0x20 ) ); - alt[3] = ( ( ( dweek & 0x10 ) == 0x10 ) ); - alt[4] = ( ( ( dweek & 0x08 ) == 0x08 ) ); - alt[5] = ( ( ( dweek & 0x04 ) == 0x04 ) ); - alt[6] = ( ( ( dweek & 0x02 ) == 0x02 ) ); - break; - } - case 6: /* saturday */ - { - alt[0] = ( ( ( dweek & 0x40 ) == 0x40 ) ); - alt[1] = ( ( ( dweek & 0x20 ) == 0x20 ) ); - alt[2] = ( ( ( dweek & 0x10 ) == 0x10 ) ); - alt[3] = ( ( ( dweek & 0x08 ) == 0x08 ) ); - alt[4] = ( ( ( dweek & 0x04 ) == 0x04 ) ); - alt[5] = ( ( ( dweek & 0x02 ) == 0x02 ) ); - alt[6] = ( ( ( dweek & 0x01 ) == 0x01 ) ); - } + for (i = 0; i < weekn+1; ++i) + alt[i+(6-weekn)] = (dweek >> (6 - i)) & 0x01; - } - - for(i=0; i < 7; i++) { - if( alt[i] == 0 ) - alt[i] = '0'; - if( alt[i] == 1 ) - alt[i] = '1'; - } + for (i=0; i < 7; i++) + alt[i] += '0'; alt[7] = 0; /* string terminator */ - keewd = Binary ( alt ); - - return keewd; + return str2bin(alt); } -static int IsHour( char *strx, int qual ) -{ +static int is_hour(char *hour, int qual) { + int hora, min; - int hora=0, min = 0; - - if ((strlen(strx) != 5) || (sscanf(strx, "%d:%d", &hora, &min) != 2)) { + if ((strlen(hour) != 5) || + (sscanf(hour, "%d:%d", &hora, &min) != 2)) return -1; - } - if( qual ) { + if (qual) { dhour = hora; dmin = min; - } - else - { + } else { lhour = hora; lmin = min; } return 1; - } - -static void sendshut( void ) -{ - +static void send_shutdown( void ) { int i; - for(i=0; i < 10; i++) + for (i = 0; i < 10; i++) ser_send_char(upsfd, CMD_SHUT ); upslogx(LOG_NOTICE, "Ups shutdown command sent"); printf("Ups shutdown command sent\n"); - } /* save config ups */ -static void confups( void ) -{ - +static void save_ups_config( void ) { int i, chks = 0; - ConfigPack[0] = 0xCF; - ConfigPack[1] = ihour; - ConfigPack[2] = imin; - ConfigPack[3] = isec; - ConfigPack[4] = lhour; - ConfigPack[5] = lmin; - ConfigPack[6] = dhour; - ConfigPack[7] = dmin; - ConfigPack[8] = weekn << 5; - ConfigPack[8] = ConfigPack[8] | dian; - ConfigPack[9] = mesn << 4; - ConfigPack[9] = ConfigPack[9] | ( anon - BASE_YEAR ); - ConfigPack[10] = DaysOffWeek; + /* FIXME? Check for overflows with int => char truncations? + * See also microsol-common.c for very similar code + */ + ConfigPack[0] = (unsigned char)0xCF; + ConfigPack[1] = (unsigned char)ihour; + ConfigPack[2] = (unsigned char)imin; + ConfigPack[3] = (unsigned char)isec; + ConfigPack[4] = (unsigned char)lhour; + ConfigPack[5] = (unsigned char)lmin; + ConfigPack[6] = (unsigned char)dhour; + ConfigPack[7] = (unsigned char)dmin; + ConfigPack[8] = (unsigned char)(weekn << 5); + ConfigPack[8] = (unsigned char)ConfigPack[8] | (unsigned char)dian; + ConfigPack[9] = (unsigned char)(mesn << 4); + ConfigPack[9] = (unsigned char)ConfigPack[9] | (unsigned char)( anon - BASE_YEAR ); + ConfigPack[10] = (unsigned char)DaysOffWeek; /* MSB zero */ ConfigPack[10] = ConfigPack[10] & (~(0x80)); - for(i=0; i < 11; i++) - chks = chks + ConfigPack[i]; + for (i=0; i < 11; i++) + chks += ConfigPack[i]; - ConfigPack[11] = chks % 256; + /* FIXME? Does truncation to char have same effect as %256 ? */ + ConfigPack[11] = (unsigned char)(chks % 256); - for(i=0; i < 12; i++) - ser_send_char(upsfd, ConfigPack[i] ); - + for (i=0; i < 12; i++) + ser_send_char(upsfd, ConfigPack[i]); } /* print UPS internal variables */ -static void prnInfo( void ) -{ +static void print_info( void ) { + printf(UPS_DATE, Year, Month, Day); + printf(SYS_DATE, anon, mesn, dian, seman); + printf(UPS_TIME, ihour, imin, isec); - int sunday=0, monday=0, tuesday=0, wednesday=0, thursday=0, friday=0, saturday=0; - unsigned char dweek; + if (prgups > 0) { + /*sunday, monday, tuesday, wednesday, thursday, friday, saturday*/ + int week_days[7] = {0, 0, 0, 0, 0, 0, 0}; + int i; - printf( UPS_DATE, Year, Month, Day ); - printf( SYS_DATE, anon, mesn, dian, seman ); - - printf( UPS_TIME, ihour, imin, isec); - - dweek = DaysStd; - - if( prgups > 0 ) { - /* this is the string to binary standard */ - sunday = ( ( dweek & 0x40 ) == 0x40 ); - monday = ( ( dweek & 0x20 ) == 0x20 ); - tuesday = ( ( dweek & 0x10 ) == 0x10 ); - wednesday = ( ( dweek & 0x08 ) == 0x08 ); - thursday = ( ( dweek & 0x04 ) == 0x04 ); - friday = ( ( dweek & 0x02 ) == 0x02 ); - saturday = ( ( dweek & 0x01 ) == 0x01 ); + for (i = 0; i < 7; ++i) + week_days[i] = (DaysStd >> (6 - i)) & 0x01; - if( prgups == 3) - printf( PRG_ONOU ); + if (prgups == 3) + printf(PRG_ONOU); else - printf( PRG_ONON ); - printf( TIME_ON, lhour, lmin); - printf( TIME_OFF, dhour, dmin); - printf( PRG_DAYS ); - printf( FMT_DAYS, sunday, monday, tuesday, wednesday, thursday, friday, saturday); - } - else - printf( PRG_ONOF ); + printf(PRG_ONON); + printf(TIME_ON, lhour, lmin); + printf(TIME_OFF, dhour, dmin); + printf(PRG_DAYS); + printf(FMT_DAYS, + week_days[0], week_days[1], week_days[2], + week_days[3], week_days[4], week_days[5], + week_days[6]); + } else + printf(PRG_ONOF); } /* is today shutdown day ? */ -static int IsToday( unsigned char dweek, int nweek) -{ - - switch ( nweek ) - { - case 0: /* sunday */ - return ( ( ( dweek & 0x40 ) == 0x40 ) ); - case 1: - return ( ( ( dweek & 0x20 ) == 0x20 ) ); - case 2: - return ( ( ( dweek & 0x10 ) == 0x10 ) ); - case 3: - return ( ( ( dweek & 0x08 ) == 0x08 ) ); - case 4: - return ( ( ( dweek & 0x04 ) == 0x04 ) ); - case 5: - return ( ( ( dweek & 0x02 ) == 0x02 ) ); - case 6: /* saturday */ - return ( ( ( dweek & 0x01 ) == 0x01 ) ); - } - - return 0; - +inline static int is_today( unsigned char dweek, int nweek) { + return (dweek >> (6 - nweek)) & 0x01; } -static void AutonomyCalc( int iauto ) /* all models */ -{ - - int indice, indd, lim, min, max, inf, sup, indc, bx, ipo =0; +/* all models */ +static void autonomy_calc( int iauto ) { + int indice, indd, lim, min, max, inf, sup, indc, bx, ipo = 0; bx = bext[iauto]; indice = RecPack[3]; indd = indice - 139; - if( UtilPower > 20 ) - ipo = ( UtilPower - 51 ) / 100; + if (UtilPower > 20) + ipo = (UtilPower - 51) / 100; indc = auton[iauto].maxi; @@ -435,44 +291,39 @@ static void AutonomyCalc( int iauto ) /* all models */ lim = max - 139; sup = max + 1; - if( UtilPower <= 20 ) { + if (UtilPower <= 20) { Autonomy = 170; - maxauto = 170; - } - else - { + maxauto = 170; + } else { maxauto = auton[iauto].mm[ipo][lim]; - if( indice > inf && indice < sup ) { + if( indice > inf && indice < sup ) Autonomy = auton[iauto].mm[ipo][indd]; - } - else - { - if( indice > max ) Autonomy = maxauto; - if( indice < min ) Autonomy = 0; + else { + if (indice > max) + Autonomy = maxauto; + if (indice < min) + Autonomy = 0; } } - - if( BattExtension > 0 && iauto < 4 ) - Autonomy = ( Autonomy * ( BattExtension + bx ) * 1.0 / bx ); + if (BattExtension > 0 && iauto < 4) + Autonomy = ( Autonomy * ( BattExtension + bx ) * 1.0 / bx ); } -static void ScanReceivePack( void ) -{ - - int aux, im, ov = 0; +static void scan_received_pack(void) { + int aux, im, ov; /* model independent data */ - Year = ( RecPack[ 19 ] & 0x0F ) + BASE_YEAR; - Month = ( RecPack[ 19 ] & 0xF0 ) >> 4; - Day = ( RecPack[ 18 ] & 0x1F ); + Year = (RecPack[ 19 ] & 0x0F) + BASE_YEAR; + Month = (RecPack[ 19 ] & 0xF0) >> 4; + Day = (RecPack[ 18 ] & 0x1F); DaysOnWeek = RecPack[17]; /* Days of week if in UPS shutdown programming mode */ - if( prgups == 3 ) { - DaysStd = revertdays( DaysOnWeek ); - + if (prgups == 3) { + DaysStd = revert_days( DaysOnWeek ); + /* time for programming UPS off */ dhour = RecPack[15]; dmin = RecPack[16]; @@ -480,50 +331,59 @@ static void ScanReceivePack( void ) lhour = RecPack[13]; lmin = RecPack[14]; } - + /* UPS internal time */ ihour = RecPack[11]; imin = RecPack[10]; isec = RecPack[9]; - - if( ( ( 0x01 & RecPack[ 20 ] ) == 0x01 ) ) - Out220 = 1; - CriticBatt = ( ( 0x04 & RecPack[ 20 ] ) == 0x04 ); - InversorOn = ( ( 0x08 & RecPack[ 20 ] ) == 0x08 ); - SuperHeat = ( ( 0x10 & RecPack[ 20 ] ) == 0x10 ); - SourceFail = ( ( 0x20 & RecPack[ 20 ] ) == 0x20 ); - OverCharge = ( ( 0x80 & RecPack[ 20 ] ) == 0x80 ); - if( ( ( 0x40 & RecPack[ 20 ] ) == 0x40 ) ) + if ((0x01 & RecPack[20]) == 0x01) + Out220 = 1; + + CriticBatt = (0x04 & RecPack[20]) == 0x04; + InversorOn = (0x08 & RecPack[20]) == 0x08; + SuperHeat = (0x10 & RecPack[20]) == 0x10; + SourceFail = (0x20 & RecPack[20]) == 0x20; + OverCharge = (0x80 & RecPack[20]) == 0x80; + + if ((0x40 & RecPack[20]) == 0x40) InputValue = 1; else InputValue = 0; - Temperature = ( 0x7F & RecPack[ 4 ]); - if( ( ( 0x80 & RecPack[ 4 ] ) == 0x80 ) ) - Temperature = Temperature - 128; + + Temperature = 0x7F & RecPack[4]; + if (0x80 & RecPack[4]) + Temperature -= 128; /* model dependent data */ im = inds[imodel]; ov = Out220; - if( RecPack[ 6 ] >= 194 ) - InVoltage = RecPack[ 6 ] * ctab[imodel].m_involt194[0] + ctab[imodel].m_involt194[1]; - else - InVoltage = RecPack[ 6 ] * ctab[imodel].m_involt193[0] + ctab[imodel].m_involt193[1]; - + if (SolisModel != 16) { + if (RecPack[6] >= 194) + InVoltage = RecPack[6] * ctab[imodel].m_involt194[0] + ctab[imodel].m_involt194[1]; + else + InVoltage = RecPack[6] * ctab[imodel].m_involt193[0] + ctab[imodel].m_involt193[1]; + } else { + /* Code InVoltage for STAY1200_USB */ + if ((RecPack[20] & 0x1) == 0) /* IsOutVoltage 220 */ + InVoltage = RecPack[2] * ctab[imodel].m_involt193[0] + ctab[imodel].m_involt193[1]; + else + InVoltage = RecPack[2] * ctab[imodel].m_involt193[0] + ctab[imodel].m_involt193[1] - 3.0; + } + BattVoltage = RecPack[ 3 ] * ctab[imodel].m_battvolt[0] + ctab[imodel].m_battvolt[1]; - + NominalPower = nompow[im]; - if( SourceFail ) { + + if (SourceFail) { OutVoltage = RecPack[ 1 ] * ctab[imodel].m_outvolt_i[ov][0] + ctab[imodel].m_outvolt_i[ov][1]; OutCurrent = RecPack[ 5 ] * ctab[imodel].m_outcurr_i[ov][0] + ctab[imodel].m_outcurr_i[ov][1]; AppPower = ( RecPack[ 5 ] * RecPack[ 1 ] ) * ctab[imodel].m_appp_i[ov][0] + ctab[imodel].m_appp_i[ov][1]; UtilPower = ( RecPack[ 7 ] + RecPack[ 8 ] * 256 ) * ctab[imodel].m_utilp_i[ov][0] + ctab[imodel].m_utilp_i[ov][1]; InCurrent = 0; - } - else - { + } else { OutVoltage = RecPack[ 1 ] * ctab[imodel].m_outvolt_s[ov][0] + ctab[imodel].m_outvolt_s[ov][1]; OutCurrent = RecPack[ 5 ] * ctab[imodel].m_outcurr_s[ov][0] + ctab[imodel].m_outcurr_s[ov][1]; AppPower = ( RecPack[ 5 ] * RecPack[ 1 ] ) * ctab[imodel].m_appp_s[ov][0] + ctab[imodel].m_appp_s[ov][1]; @@ -532,39 +392,88 @@ static void ScanReceivePack( void ) + OutCurrent *( OutVoltage * 1.0 / InVoltage ); } - aux = ( RecPack[ 21 ] + RecPack[ 22 ] * 256 ); - if( aux > 0 ) + if (SolisModel == 16) { + int configRelay = (RecPack[6] & 0x38) >> 3; + double TENSAO_SAIDA_F1_MR[8] = { 1.1549, 1.0925, 0.0, 0.0, 1.0929, 1.0885, 0.0, 0.8654262224145391 }; + double TENSAO_SAIDA_F2_MR[8] = { -6.9157, 11.026, 10.43, 0.0, -0.6109, 12.18, 0.0, 13.677}; + + const double TENSAO_SAIDA_F2_MI[8] ={ 5.59, 9.47, 13.7, 0.0, 0.0, 0.0, 0.0, 0.0 }; + const double TENSAO_SAIDA_F1_MI[8] = { 7.9, 9.1, 17.6, 0.0, 0.0, 0.0, 0.0, 0.0 }; + + const double corrente_saida_F1_MR = 0.12970000389100012; + const double corrente_saida_F2_MR = 0.5387060281204546; + /* double corrente_saida_F1_MI = 0.1372; + double corrente_saida_F2_MI = 0.3456; */ + + if (SourceFail) { + if (RecPack[20] == 0) { + double a = RecPack[1] * 2; + a /= 128.0; + /* a = double sqrt(a); */ + OutVoltage = RecPack[1] * a * TENSAO_SAIDA_F1_MI[configRelay] + TENSAO_SAIDA_F2_MI[configRelay]; + } + } else { + OutCurrent = (float)(corrente_saida_F1_MR * RecPack[5] + corrente_saida_F2_MR); + OutVoltage = RecPack[1] * TENSAO_SAIDA_F1_MR[configRelay] + TENSAO_SAIDA_F2_MR[configRelay]; + AppPower = OutCurrent * OutVoltage; + + double RealPower = (RecPack[7] + RecPack[8] * 256); + + double potVA1 = 5.968 * AppPower - 284.36; + double potVA2 = 7.149 * AppPower - 567.18; + double potLin = 0.1664 * RealPower + 49.182; + double potRe = 0.1519 * RealPower + 32.644; + if (fabs(potVA1 - RealPower) < fabs(potVA2 - RealPower)) + RealPower = potLin; + else + RealPower = potRe; + + if (OutCurrent < 0.7) + RealPower = AppPower; + + if (AppPower < RealPower) { + double f = AppPower; + AppPower = RealPower; + RealPower = f; + } + } + } + + aux = (RecPack[ 21 ] + RecPack[ 22 ] * 256); + if (aux > 0) InFreq = ctab[imodel].m_infreq * 1.0 / aux; - else + + /* Specific for STAY1200_USB */ + if (SolisModel == 16) { + InFreq = ((float)(0.37 * (257 - (aux >> 8)))); + } else InFreq = 0; - + /* input voltage offset */ - if( InVoltage < InVolt_offset ) { /* all is equal 30 */ + if (InVoltage < InVolt_offset) { /* all is equal 30 */ InFreq = 0; InVoltage = 0; InCurrent = 0; } /* app power offset */ - if( AppPower < ctab[imodel].m_appp_offset ) { + if (AppPower < ctab[imodel].m_appp_offset) { AppPower = 0; UtilPower = 0; ChargePowerFactor = 0; OutCurrent = 0; } - - if( im < 3 ) - AutonomyCalc( im ); - else - { - if( BattExtension == 80 ) - AutonomyCalc( im + 1 ); + + if (im < 3) + autonomy_calc(im); + else { + if (BattExtension == 80 && im == 3) + autonomy_calc(im + 1); else - AutonomyCalc( im ); + autonomy_calc(im); } /* model independent data */ - batcharge = ( Autonomy / maxauto ) * 100.0; upscharge = ( AppPower / NominalPower ) * 100.0; @@ -572,45 +481,46 @@ static void ScanReceivePack( void ) batcharge = 100.0; OutFreq = 60; - if( !( InversorOn ) ) { + if (!InversorOn) { OutVoltage = 0; OutFreq = 0; } - if( ( !( SourceFail ) && InversorOn ) ) + if (!SourceFail && InversorOn) OutFreq = InFreq; - if( AppPower <= 0 ) /* charge pf */ + if (AppPower < 0) /* charge pf */ ChargePowerFactor = 0; - else - { - if( AppPower == 0 ) + else { + if( d_equal(AppPower, 0) ) ChargePowerFactor = 100; else - ChargePowerFactor = (( UtilPower / AppPower) * 100 ); - if( ChargePowerFactor > 100 ) + ChargePowerFactor = (( UtilPower / AppPower) * 100); + + if(ChargePowerFactor > 100) ChargePowerFactor = 100; } - if( SourceFail && SourceLast ) /* first time failure */ + if (SourceFail && SourceLast) /* first time failure */ FailureFlag = true; /* source return */ - if( !( SourceFail ) && !( SourceLast ) ) { + if (!SourceFail && !SourceLast) { SourceReturn = true; - ser_flush_in(upsfd,"",0); /* clean port */ + /* clean port: */ + /* ser_flush_in(upsfd,"",0); */ } - - if( !( SourceFail ) == SourceLast ) { + + if((!SourceFail) == SourceLast) { SourceReturn = false; FailureFlag = false; } - SourceLast = !( SourceFail ); + SourceLast = !SourceFail; /* Autonomy */ - if( ( Autonomy < 5 ) ) + if (Autonomy < 5) LowBatt = true; else LowBatt = false; @@ -618,320 +528,298 @@ static void ScanReceivePack( void ) UpsPowerFactor = 700; /* input 110V or 220v */ - if( ( InputValue == 0 ) ) { + if (InputValue == 0) { InDownLim = 75; InUpLim = 150; NomInVolt = 110; - } - else - { + } else { InDownLim = 150; InUpLim = 300; NomInVolt = 220; } /* output volage 220V or 110V */ - if( Out220 ) { + if (Out220) { OutDownLim = 190; OutUpLim = 250; NomOutVolt = 220; - } - else - { + } else { OutDownLim = 100; OutUpLim = 140; NomOutVolt = 110; } - if( SourceFail ) /* source status */ + if (SourceFail) /* source status */ InputStatus = 2; else InputStatus = 1; - if( InversorOn ) /* output status */ + if (InversorOn) /* output status */ OutputStatus = 1; else OutputStatus = 2; - if( OverCharge ) + if (OverCharge) OutputStatus = 3; - if( CriticBatt ) /* battery status */ + if (CriticBatt) /* battery status */ BattStatus = 4; else BattStatus = 1; SourceEvents = 0; - if( FailureFlag ) + if (FailureFlag) SourceEvents = 1; - if( SourceReturn ) + if (SourceReturn) SourceEvents = 2; /* verify Inversor */ - if( Flag_inversor ) { + if (Flag_inversor) { InversorOnLast = InversorOn; Flag_inversor = false; } OutputEvents = 0; - if( InversorOn && !( InversorOnLast ) ) + if (InversorOn && !InversorOnLast) OutputEvents = 26; - if( InversorOnLast && !( InversorOn ) ) + if (InversorOnLast && !InversorOn) OutputEvents = 27; InversorOnLast = InversorOn; - if( SuperHeat && !( SuperHeatLast ) ) + + if (SuperHeat && !SuperHeatLast) OutputEvents = 12; - if( SuperHeatLast && !( SuperHeat ) ) + if (SuperHeatLast && !SuperHeat) OutputEvents = 13; SuperHeatLast = SuperHeat; - if( OverCharge && !( OverChargeLast ) ) + + if (OverCharge && !OverChargeLast) OutputEvents = 10; - if( OverChargeLast && !( OverCharge ) ) + if (OverChargeLast && !OverCharge) OutputEvents = 11; OverChargeLast = OverCharge; BattEvents = 0; CriticBattLast = CriticBatt; - } -static void -CommReceive(const char *bufptr, int size) -{ +static void comm_receive(const unsigned char *bufptr, size_t size) { + if (size == packet_size) { + int CheckSum = 0; + size_t i; - int i, CheckSum, i_end; + memcpy(RecPack, bufptr, packet_size); + + if (nut_debug_level >= 3) + upsdebug_hex(3, "comm_receive: RecPack", RecPack, size); - if( ( size==25 ) ) - Waiting = 0; - - switch( Waiting ) - { - /* normal package */ - case 0: - { - if( size == 25 ) { - i_end = 25; - for( i = 0 ; i < i_end ; ++i ) { - RecPack[i] = *bufptr; - bufptr++; - } - /* CheckSum verify */ - CheckSum = 0; - i_end = 23; - for( i = 0 ; i < i_end ; ++i ) - CheckSum = RecPack[ i ] + CheckSum; + for (i = 0 ; i < packet_size-2 ; ++i ) + CheckSum += RecPack[i]; CheckSum = CheckSum % 256; - - ser_flush_in(upsfd,"",0); /* clean port */ - - /* correct package */ - if( ( (RecPack[0] & 0xF0) == 0xA0 ) - && ( RecPack[ 24 ] == 254 ) - && ( RecPack[ 23 ] == CheckSum ) ) { + upsdebugx(4, "%s: calculated checksum = 0x%02x, RecPack[23] = 0x%02x", __func__, CheckSum, RecPack[23]); - if(!(detected)) { - SolisModel = (int) (RecPack[0] & 0x0F); - if( SolisModel < 13 ) + /* clean port: */ + /* ser_flush_in(upsfd,"",0); */ + + /* RecPack[0] == model number below: + * SOLIS = 1; + * RHINO = 2; + * STAY = 3; + * SOLIS_LI_700 = 169; + * SOLIS_M11 = 171; + * SOLIS_M15 = 175; + * SOLIS_M14 = 174; + * SOLIS_M13 = 173; + * SOLISDC_M14 = 201; + * SOLISDC_M13 = 206; + * SOLISDC_M15 = 207; + * CABECALHO_RHINO = 194; + * PS800 = 185; + * STAY1200_USB = 186; + * PS350_CII = 184; + * PS2200 = 187; + * PS2200_22 = 188; + * STAY700_USB = 189; + * BZ1500 = 190; + */ + + if ((((RecPack[0] & 0xF0) == 0xA0 ) || (RecPack[0] & 0xF0) == 0xB0) && + (RecPack[24] == 254) && + (RecPack[23] == CheckSum)) { + + if (!detected) { + if (RecPack[0] == 186) + SolisModel = 16; + else + SolisModel = (int) (RecPack[0] & 0x0F); + + if (SolisModel < 13) imodel = SolisModel - 10; /* 10 = 0, 11 = 1 */ else imodel = SolisModel - 11; /* 13 = 2, 14 = 3, 15 = 4 */ + detected = true; } - switch( SolisModel ) - { + switch (SolisModel) { case 10: case 11: case 12: case 13: case 14: case 15: - { - ScanReceivePack(); + scan_received_pack(); + break; + case 16: /* STAY1200_USB model */ + scan_received_pack(); break; - } default: - { - printf( M_UNKN ); + printf(M_UNKN); + scan_received_pack(); /* Scan anyway. */ break; } } - } - } - - break; - } - - case 1: - { - /* dumping package nothing to do yet */ - Waiting = 0; - break; - } - - } - - Waiting =0; - } -static void getbaseinfo(void) -{ - - unsigned char temp[256]; +static void get_base_info(void) { #ifdef PORTUGUESE - char diassemana[7][4]={"Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab"}; + const char DaysOfWeek[7][4]={"Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab"}; #else - char DaysOfWeek[7][4]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; + const char DaysOfWeek[7][4]={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; #endif - char mycmd[8]; - char *str1, *str2, *str3, *str4, *strx; - unsigned char Pacote[25]; - int i, i1=0, i2=0, j=0, tam, tpac=25; + unsigned char packet[PACKET_SIZE], syncEOR = '\0', syncEOR_was_read = 0; + int i1=0, i2=0; + size_t i; + ssize_t tam; - time_t *tmt; + time_t tmt; struct tm *now; - tmt = ( time_t * ) malloc( sizeof( time_t ) ); - time( tmt ); - now = localtime( tmt ); + struct tm tmbuf; + + time(&tmt); + now = localtime_r(&tmt, &tmbuf); dian = now->tm_mday; mesn = now->tm_mon+1; anon = now->tm_year+1900; ihour = now->tm_hour; imin = now->tm_min; isec = now->tm_sec; - weekn = now->tm_wday; + weekn = now->tm_wday; -#ifdef PORTUGUESE - strcpy( seman, diassemana[weekn] ); -#else - strcpy( seman, DaysOfWeek[weekn] ); -#endif + strcpy(seman, DaysOfWeek[weekn]); - if( testvar("battext")) + if (testvar("battext")) BattExtension = atoi(getval("battext")); - if( testvar("prgshut")) + if (testvar("prgshut")) prgups = atoi(getval("prgshut")); - if( prgups > 0 && prgups < 3 ) { - if( testvar("daysweek") ) { - strx = getval("daysweek"); - str1 = convdays( strx ); - DaysOnWeek = Binary( str1 ); + if (prgups > 0 && prgups < 3) { + if (testvar("daysweek")) + DaysOnWeek = str2bin(convert_days(getval("daysweek"))); + + if (testvar("daysoff")) { + char *doff = getval("daysoff"); + DaysStd = str2bin(doff); + DaysOffWeek = str2bin( convert_days(doff)); } - if( testvar("daysoff") ) { - strx = getval("daysoff"); - str2 = convdays( strx ); - DaysStd = Binary ( strx ); - DaysOffWeek = Binary( str2 ); - } + if (testvar("houron")) + i1 = is_hour(getval("houron"), 0); - if( testvar("houron") ) { - str3 = getval("houron"); - i1 = IsHour( str3, 0 ); - } + if (testvar("houroff")) + i2 = is_hour(getval("houroff"), 1); - if( testvar("houroff") ) { - str4 = getval("houroff"); - i2 = IsHour( str4, 1 ); - } - - if( i1 == 1 && i2 == 1 && ( DaysOnWeek > 0 ) ) { + if (i1 == 1 && i2 == 1 && (DaysOnWeek > 0)) { isprogram = 1; /* prgups == 1 ou 2 */ - if( prgups == 2 ) - confups(); /* save ups config */ - } - else - { - if( (i2 == 1) && ( DaysOffWeek > 0 ) ) { + if (prgups == 2) + save_ups_config(); /* save ups config */ + } else { + if (i2 == 1 && DaysOffWeek > 0) { isprogram = 1; - if( DaysOnWeek != DaysOffWeek ) DaysOnWeek = DaysOffWeek; + } } - } - } /* end prgups 1 - 2 */ /* dummy read attempt to sync - throw it out */ - snprintf(mycmd, sizeof(mycmd), "%c%c",CMD_UPSCONT, ENDCHAR); - ser_send(upsfd, "%s", mycmd); + upsdebugx(3, "%s: sending CMD_UPSCONT and ENDCHAR to sync", __func__); + ser_send(upsfd, "%c%c", CMD_UPSCONT, ENDCHAR); - /* trying detect solis model */ - while ( ( !detected ) && ( j < 20 ) ) { - temp[0] = 0; /* flush temp buffer */ - tam = ser_get_buf_len(upsfd, temp, tpac, 3, 0); - if( tam == 25 ) { - for( i = 0 ; i < tam ; i++ ) { - Pacote[i] = temp[i]; - } - } - - j++; - if( tam == 25) - CommReceive((char *)Pacote, tam); - else - CommReceive((char *)temp, tam); - } /* while end */ - - if( (!detected) ) { - fatalx(EXIT_FAILURE, NO_SOLIS ); + /* + * - Read until end-of-response character (0xFE): + * read up to 3 packets in size before giving up + * synchronizing with the device. + */ + for (i = 0; i < packet_size*3; i++) { + ser_get_char(upsfd, &syncEOR, 3, 0); + syncEOR_was_read = 1; + if(syncEOR == RESP_END) + break; } - switch( SolisModel ) - { + if (!syncEOR_was_read || syncEOR != RESP_END) { + /* synchronization failed */ + fatalx(EXIT_FAILURE, NO_SOLIS); + } else { + upsdebugx(4, "%s: requesting %zu bytes from ser_get_buf_len()", __func__, packet_size); + tam = ser_get_buf_len(upsfd, packet, packet_size, 3, 0); + if (tam < 0) { + upsdebugx(0, "%s: Error (%zd) reading from ser_get_buf_len()", __func__, tam); + fatalx(EXIT_FAILURE, NO_SOLIS); + } + upsdebugx(2, "%s: received %zd bytes from ser_get_buf_len()", __func__, tam); + if (tam > 0 && nut_debug_level >= 4) { + upsdebug_hex(4, "received from ser_get_buf_len()", packet, (size_t)tam); + } + comm_receive(packet, (size_t)tam); + } + + if (!detected) + fatalx(EXIT_FAILURE, NO_SOLIS ); + + switch (SolisModel) { case 10: case 11: case 12: - { - strcpy(Model, "Solis 1.0"); + Model = "Solis 1.0"; break; - } case 13: - { - strcpy(Model, "Solis 1.5"); + Model = "Solis 1.5"; break; - } case 14: - { - strcpy(Model, "Solis 2.0"); + Model = "Solis 2.0"; break; - } case 15: - { - strcpy(Model, "Solis 3.0"); + Model = "Solis 3.0"; break; - } + case 16: + Model = "Microsol Back-Ups BZ1200-BR"; + break; } /* if( isprogram ) */ - if( prgups == 1 ) { + if (prgups == 1) { hourshut = dhour; minshut = dmin; - } - else - { - if( prgups == 2 || prgups == 3 ) { /* broadcast before firmware shutdown */ - if( dmin < 5 ) { - if( dhour > 1 ) + } else { + if (prgups == 2 || prgups == 3) { /* broadcast before firmware shutdown */ + if (dmin < 5) { + if (dhour > 1) hourshut = dhour - 1; else hourshut = 23; - minshut = 60 - ( 5 - dmin ); - } - else - { - hourshut = dhour; - minshut = dmin - 5; - } - } - } + minshut = 60 - ( 5 - dmin ); + } else { + hourshut = dhour; + minshut = dmin - 5; + } + } + } /* manufacturer */ dstate_setinfo("ups.mfr", "%s", "Microsol"); @@ -945,106 +833,110 @@ static void getbaseinfo(void) printf("Detected %s on %s\n", dstate_getinfo("ups.model"), device_path); - prnInfo(); - - + print_info(); } -static void getupdateinfo(void) -{ - unsigned char temp[256]; - int tam, isday, hourn, minn; +static void get_update_info(void) { + unsigned char temp[256]; + int isday, hourn, minn; + ssize_t tam; /* time update and programable shutdown block */ - time_t *tmt; + time_t tmt; struct tm *now; - tmt = ( time_t * ) malloc( sizeof( time_t ) ); - time( tmt ); - now = localtime( tmt ); + struct tm tmbuf; + + time(&tmt); + now = localtime_r(&tmt, &tmbuf); hourn = now->tm_hour; minn = now->tm_min; - weekn = now->tm_wday; + weekn = now->tm_wday; - if( isprogram || prgups == 3 ) { - if( isprogram ) - isday = IsToday( DaysStd, weekn ); + if (isprogram || prgups == 3) { + if (isprogram) + isday = is_today(DaysStd, weekn); else - isday = IsToday( DaysStd, weekn ); - - if( isday ) - printf( TODAY_DD, hourshut, minshut ); + isday = is_today( DaysStd, weekn); + + if (isday) + printf(TODAY_DD, hourshut, minshut); + + if ( + (hourn == hourshut) && + (minn >= minshut) && + isday) { - if( ( hourn == hourshut ) && ( minn >= minshut ) && isday ) { printf( SHUT_NOW ); progshut = 1; } - } - + } /* programable shutdown end block */ - pacsize = 25; - /* get update package */ temp[0] = 0; /* flush temp buffer */ - tam = ser_get_buf_len(upsfd, temp, pacsize, 3, 0); - CommReceive((char *)temp, tam); + upsdebugx(3, "%s: requesting %zu bytes from ser_get_buf_len()", __func__, packet_size); + tam = ser_get_buf_len(upsfd, temp, packet_size, 3, 0); + if (tam < 0) { + upsdebugx(0, "%s: Error (%zd) reading from ser_get_buf_len()", __func__, tam); + fatalx(EXIT_FAILURE, NO_SOLIS); + } + + upsdebugx(2, "%s: received %zd bytes from ser_get_buf_len()", __func__, tam); + if(tam > 0 && nut_debug_level >= 4) + upsdebug_hex(4, "received from ser_get_buf_len()", temp, (size_t)tam); + + comm_receive(temp, (size_t)tam); } -static int instcmd(const char *cmdname, const char *extra) -{ - - if (!strcasecmp(cmdname, "shutdown.return")) { +static int instcmd(const char *cmdname, const char *extra) { + if (!strcasecmp(cmdname, "shutdown.return")) { /* shutdown and restart */ ser_send_char(upsfd, CMD_SHUTRET); /* 0xDE */ /* ser_send_char(upsfd, ENDCHAR); */ return STAT_INSTCMD_HANDLED; } - if (!strcasecmp(cmdname, "shutdown.stayoff")) - { - /* shutdown now (one way) */ - ser_send_char(upsfd, CMD_SHUT); /* 0xDD */ - /* ser_send_char(upsfd, ENDCHAR); */ - return STAT_INSTCMD_HANDLED; - } + if (!strcasecmp(cmdname, "shutdown.stayoff")) { + /* shutdown now (one way) */ + ser_send_char(upsfd, CMD_SHUT); /* 0xDD */ + /* ser_send_char(upsfd, ENDCHAR); */ + return STAT_INSTCMD_HANDLED; + } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } -void upsdrv_initinfo(void) -{ - getbaseinfo(); +void upsdrv_initinfo(void) { + get_base_info(); upsh.instcmd = instcmd; } -void upsdrv_updateinfo(void) -{ - - getupdateinfo(); /* new package for updates */ +void upsdrv_updateinfo(void) { + get_update_info(); /* new package for updates */ dstate_setinfo("output.voltage", "%03.1f", OutVoltage); dstate_setinfo("input.voltage", "%03.1f", InVoltage); dstate_setinfo("battery.voltage", "%02.1f", BattVoltage); dstate_setinfo("battery.charge", "%03.1f", batcharge); - + dstate_setinfo("output.current", "%03.1f", OutCurrent); status_init(); - if (!SourceFail ) + if (!SourceFail) status_set("OL"); /* on line */ else status_set("OB"); /* on battery */ - if (Autonomy < 5 ) + if (Autonomy < 5) status_set("LB"); /* low battery */ - if( progshut ) { /* software programable shutdown immediately */ + if (progshut) { /* software programable shutdown immediately */ if( prgups == 2 ) - sendshut(); /* Ups shutdown in 4-5 minutes -- redundant Ups shutdown */ + send_shutdown(); /* Ups shutdown in 4-5 minutes -- redundant Ups shutdown */ status_set("LB"); /* no low battery but is a force shutdown */ } @@ -1056,33 +948,24 @@ void upsdrv_updateinfo(void) dstate_setinfo("ups.load", "%03.1f", upscharge); dstate_dataok(); - } -/* power down the attached load immediately */ -void upsdrv_shutdown(void) -{ - - /* basic idea: find out line status and send appropriate command */ - /* on battery: send normal shutdown, ups will return by itself on utility */ - /* on line: send shutdown+return, ups will cycle and return soon */ - +/*! @brief Power down the attached load immediately. + * Basic idea: find out line status and send appropriate command. + * - on battery: send normal shutdown, UPS will return by itself on utility + * - on line: send shutdown+return, UPS will cycle and return soon. + */ +void upsdrv_shutdown(void) { if (!SourceFail) { /* on line */ - - printf("On line, sending shutdown+return command...\n"); + upslogx(LOG_NOTICE, "On line, sending shutdown+return command...\n"); ser_send_char(upsfd, CMD_SHUTRET ); - } - else - { - printf("On battery, sending normal shutdown command...\n"); + } else { + upslogx(LOG_NOTICE, "On battery, sending normal shutdown command...\n"); ser_send_char(upsfd, CMD_SHUT); } - } -void upsdrv_help(void) -{ - +void upsdrv_help(void) { printf("\nSolis options\n"); printf(" Battery Extension in AH\n"); printf(" battext = 80\n"); @@ -1098,25 +981,20 @@ void upsdrv_help(void) printf(" houron = hh:mm hh = hour 0-23 mm = minute 0-59 separated with :\n"); printf(" houroff = hh:mm hh = hour 0-23 mm = minute 0-59 separated with :\n"); printf(" where houron is power-on hour and houroff is shutdown and power-off hour\n"); - printf(" Uses daysweek and houron to programing and save UPS power on/off\n"); + printf(" Uses daysweek and houron to programming and save UPS power on/off\n"); printf(" These are valid only if prgshut = 2 or 3\n"); - } -void upsdrv_makevartable(void) -{ - +void upsdrv_makevartable(void) { addvar(VAR_VALUE, "battext", "Battery Extension (0-80)min"); addvar(VAR_VALUE, "prgshut", "Programable power off (0-3)"); addvar(VAR_VALUE, "daysweek", "Days of week UPS power of/off"); addvar(VAR_VALUE, "daysoff", "Days of week Driver shutdown"); addvar(VAR_VALUE, "houron", "Power on hour (hh:mm)"); addvar(VAR_VALUE, "houroff", "Power off hour (hh:mm)"); - } -void upsdrv_initups(void) -{ +void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B9600); @@ -1124,7 +1002,6 @@ void upsdrv_initups(void) ser_set_rts(upsfd, 0); } -void upsdrv_cleanup(void) -{ +void upsdrv_cleanup(void) { ser_close(upsfd, device_path); } diff --git a/drivers/solis.h b/drivers/solis.h index 521d080..54c9471 100644 --- a/drivers/solis.h +++ b/drivers/solis.h @@ -1,6 +1,8 @@ /* solis.h - Microsol Solis UPS hardware - Copyright (C) 2004 Silvino B. Magalhaes + Copyright (C) 2004 Silvino B. Magalhaes + 2019 Roberto Panerai Velloso + This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -22,23 +24,34 @@ 2004/10/30 - Version 0.40 - add model data structs 2004/11/22 - Version 0.50 - add internal e external shutdown programming 2005/06/16 - Version 0.60 - save external shutdown programming to ups, - support new cables and Solaris compilation + support new cables and Solaris compilation + 2015/09/19 - Version 0.63 - patch for correct reading for Microsol Back-Ups BZ1200-BR */ #ifndef INCLUDED_SOLIS_H #define INCLUDED_SOLIS_H +#include "nut_stdint.h" + +/* General FIXMEs: + * * "static" declarations belong in some one single C source; + * headers should use "extern" to refer linker to look for + * vars in other object files + * * use a common definition of bool_t + */ typedef int bool_t; /* autonomy constants */ -int bext[5] = {14,18,28,18,1}; -int nompow[4] = { 1000,1500,2000,3000 }; -int inds[5] = { 0,0,1,2,3 }; -double InVolt_offset = 30.; +static const int bext[5] = {14,18,28,18,1}; +static const int nompow[5] = { 1000,1500,2000,3000,1200 }; +static const int inds[6] = { 0,0,1,2,3,4 }; +static const double InVolt_offset = 30.; +#define PACKET_SIZE 25 +static const size_t packet_size = PACKET_SIZE; -struct { +static const struct { int maxi; /* power internals */ int minc[21]; /* power minimal index */ int maxc[21]; /* power maximus index */ @@ -201,7 +214,7 @@ struct { * Solis constants for data ajustment * ----------------------------------------------------------- */ -struct { +static const struct { double m_infreq; double m_appp_offset; double m_involt193[2]; @@ -216,7 +229,7 @@ struct { double m_utilp_i[2][2]; double m_appp_s[2][2]; double m_appp_i[2][2]; -} ctab[5] = +} ctab[6] = { { 101620.0, 25.0, { 1.141, 13.0 }, @@ -287,21 +300,56 @@ struct { { { 1.0/4.78, 52.0 }, { 1.0/4.55, 55.0 } }, { { 1.0/5.15, 29.0 }, { 1.0/4.8, 26.0 } }, { { 1.0/4.78, 52.0 }, { 1.0/4.55, 55.0 } } + }, + + /*STAY1200_USB + + double m_infreq; + double m_appp_offset; + double m_involt193[2]; + double m_involt194[2]; + double m_incurr[2]; + double m_battvolt[2]; + double m_outvolt_s[2][2]; + double m_outvolt_i[2][2]; + double m_outcurr_s[2][2]; + double m_outcurr_i[2][2]; + double m_utilp_s[2][2]; + double m_utilp_i[2][2]; + double m_appp_s[2][2]; + double m_appp_i[2][2]; + + + */ + { 101800.0, /* m_infreq */ + 56.0, /* m_appp_offset */ + { 1.64, 9.34 }, /* m_involt193 - ok */ + { 2.5, -250.0 }, /* m_involt194 */ + { 35.0, 1000.0 }, /* m_incurr */ + { 0.1551, 0.2525 }, /* m_battvolt */ + { { 1.41, 13.0 }, { 1.4, 17.0 } }, /* m_outvolt_s */ + { { 2.73, 25.0 }, { 2.73, 30.0 } }, /* m_outvolt_i */ + { { 1.0/8.15, 0.25 }, { 1.0/8.15, 0.25 } }, /* m_outcurr_s */ + { { 1.0/16.0, 0.4 }, { 1.0/15.0, 0.4 } }, /* m_outcurr_i */ + { { 1.0/4.87, 19.0 }, { 1.0/4.55, 17.0 } }, /* m_utilp_s */ + { { 1.0/4.78, 52.0 }, { 1.0/4.55, 55.0 } }, /* m_utilp_i */ + { { 1.0/5.15, 29.0 }, { 1.0/4.8, 26.0 } }, /* m__app_s */ + { { 1.0/4.78, 52.0 }, { 1.0/4.55, 55.0 } } /* m_app_i */ } }; /* Date, time and programming group */ -static int const BASE_YEAR = 1998; -int Day, Month, Year; -int isprogram = 0, progshut = 0, prgups = 0; -int dian=0, mesn=0, anon=0, weekn=0; -int dhour, dmin, lhour, lmin, ihour,imin, isec, hourshut, minshut; -unsigned char DaysOnWeek=0, DaysOffWeek=0, DaysStd = 0; -char seman[4]; +static int const BASE_YEAR = 1998; /* Note: code below uses relative "unsigned char" years */ +static int Day, Month, Year; +static int isprogram = 0, progshut = 0, prgups = 0; +static int dian=0, mesn=0, anon=0, weekn=0; +static int dhour, dmin, lhour, lmin, ihour,imin, isec, hourshut, minshut; +static uint8_t DaysOnWeek=0, DaysOffWeek=0, DaysStd = 0; +static char seman[4]; /* buffers */ -unsigned char RecPack[25]; -unsigned char ConfigPack[12]; +static unsigned char RecPack[PACKET_SIZE]; +static unsigned char ConfigPack[12]; /* unsigned char MibData[161]; @@ -309,48 +357,39 @@ unsigned char DumpPack[242]; */ /* Identification */ -char Model[12]; -int SolisModel, imodel; -int InputValue, Out220; - -/* protocol */ -int Waiting, NumByteRec = 0; -int pacsize; +static const char *Model; +static int SolisModel, imodel; +static int InputValue, Out220; /* Status group */ -unsigned char InputStatus,OutputStatus, BattStatus, StatusGeral; +static unsigned char InputStatus,OutputStatus, BattStatus; /* Events group */ -unsigned char SourceEvents, OutputEvents, BattEvents, GeneralEvents; +static unsigned char SourceEvents, OutputEvents, BattEvents; /* logical */ -bool_t detected = 0; -bool_t SourceFail, SourceLast, FailureFlag, SourceReturn, SuperHeat; -bool_t SuperHeatLast, OverCharge, OverChargeLast, LowBatt; -bool_t CriticBatt, CriticBattLast, Flag_inversor, InversorOn, InversorOnLast; +static bool_t detected = 0; +static bool_t SourceFail, SourceLast, FailureFlag, SourceReturn, SuperHeat; +static bool_t SuperHeatLast, OverCharge, OverChargeLast, LowBatt; +static bool_t CriticBatt, CriticBattLast, Flag_inversor, InversorOn, InversorOnLast; /* Input group */ -double InVoltage, InCurrent, InFreq; -double InDownLim, InUpLim, NomInVolt; +static double InVoltage, InCurrent, InFreq; +static double InDownLim, InUpLim, NomInVolt; /* Output group */ -double OutVoltage, OutCurrent, OutFreq, OutDownLim, OutUpLim, NomOutVolt; +static double OutVoltage, OutCurrent, OutFreq, OutDownLim, OutUpLim, NomOutVolt; /* Battery group */ -int Autonomy, BattExtension, maxauto; -double BattVoltage, BattCurrent, Temperature, batcharge; -double Bat_LimInfRede, Bat_LimSupRede, Bat_LimInfInv, Bat_LimSupInv, Bat_VoltNom; +static int Autonomy, BattExtension, maxauto; +static double BattVoltage, Temperature, batcharge; /* Power group */ -double AppPower, UtilPower, upscharge; -int ChargePowerFactor, NominalPower, UpsPowerFactor; +static double AppPower, UtilPower, upscharge; +static int ChargePowerFactor, NominalPower, UpsPowerFactor; -static void prnInfo(void); -static int IsToday( unsigned char, int ); -static void AutonomyCalc( int ); -static void ScanReceivePack(void); -static void CommReceive(const char*, int ); -static void getbaseinfo(void); -static void getupdateinfo(void); +static void print_info(void); +static int is_today( unsigned char, int ); +static void autonomy_calc( int ); +static void scan_received_pack(void); +static void comm_receive(const unsigned char*, size_t); +static void get_base_info(void); +static void get_update_info(void); #endif /* INCLUDED_SOLIS_H */ - - - - diff --git a/drivers/tripplite-hid.c b/drivers/tripplite-hid.c index 5fc88cd..20d5152 100644 --- a/drivers/tripplite-hid.c +++ b/drivers/tripplite-hid.c @@ -1,7 +1,7 @@ /* tripplite-hid.c - data to monitor Tripp Lite USB/HID devices with NUT * * Copyright (C) - * 2003 - 2005 Arnaud Quette + * 2003 - 2012 Arnaud Quette * 2005 - 2006 Peter Selinger * 2008 - 2009 Arjen de Korte * @@ -29,7 +29,7 @@ #include "tripplite-hid.h" #include "usb-common.h" -#define TRIPPLITE_HID_VERSION "TrippLite HID 0.6" +#define TRIPPLITE_HID_VERSION "TrippLite HID 0.84" /* FIXME: experimental flag to be put in upsdrv_info */ @@ -40,18 +40,33 @@ */ static double battery_scale = 1.0; +static double io_voltage_scale = 1.0; +static double io_frequency_scale = 1.0; +static double io_current_scale = 1.0; + /* Specific handlers for USB device matching */ -static void *battery_scale_1dot0(void) +static void *battery_scale_1dot0(USBDevice_t *device) { + NUT_UNUSED_VARIABLE(device); /* FIXME: we could remove this one since it's the default! */ battery_scale = 1.0; return NULL; } -static void *battery_scale_0dot1(void) +static void *battery_scale_0dot1(USBDevice_t *device) { + NUT_UNUSED_VARIABLE(device); battery_scale = 0.1; return NULL; } +static void *smart1500lcdt_scale(USBDevice_t *device) +{ + NUT_UNUSED_VARIABLE(device); + battery_scale = 100000.0; + io_voltage_scale = 100000.0; + io_frequency_scale = 0.01; + io_current_scale = 0.01; + return NULL; +} /* TrippLite */ #define TRIPPLITE_VENDORID 0x09ae @@ -59,6 +74,9 @@ static void *battery_scale_0dot1(void) /* Hewlett Packard */ #define HP_VENDORID 0x03f0 +/* Delta/Minuteman */ +#define DELTA_VENDORID 0x05dd + /* USB IDs device table */ static usb_device_id_t tripplite_usb_device_table[] = { /* e.g. TrippLite AVR550U */ @@ -69,6 +87,8 @@ static usb_device_id_t tripplite_usb_device_table[] = { { USB_DEVICE(TRIPPLITE_VENDORID, 0x1008), battery_scale_0dot1 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x1009), battery_scale_0dot1 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x1010), battery_scale_0dot1 }, + /* e.g. TrippLite SU3000LCD2UHV */ + { USB_DEVICE(TRIPPLITE_VENDORID, 0x1330), battery_scale_1dot0 }, /* e.g. TrippLite OMNI1000LCD */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x2005), battery_scale_0dot1 }, /* e.g. TrippLite OMNI900LCD */ @@ -95,6 +115,10 @@ static usb_device_id_t tripplite_usb_device_table[] = { { USB_DEVICE(TRIPPLITE_VENDORID, 0x3014), battery_scale_1dot0 }, /* e.g. ? */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x3015), battery_scale_1dot0 }, + /* e.g. TrippLite Smart1500LCD (newer unit) */ + { USB_DEVICE(TRIPPLITE_VENDORID, 0x3016), smart1500lcdt_scale }, + /* e.g. TrippLite AVR750U (newer unit) */ + { USB_DEVICE(TRIPPLITE_VENDORID, 0x3024), smart1500lcdt_scale }, /* e.g. TrippLite SmartOnline SU1500RTXL2UA (older unit?) */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x4001), battery_scale_1dot0 }, /* e.g. TrippLite SmartOnline SU6000RT4U? */ @@ -109,6 +133,16 @@ static usb_device_id_t tripplite_usb_device_table[] = { { USB_DEVICE(TRIPPLITE_VENDORID, 0x4007), battery_scale_1dot0 }, { USB_DEVICE(TRIPPLITE_VENDORID, 0x4008), battery_scale_1dot0 }, + /* e.g. ? */ + { USB_DEVICE(HP_VENDORID, 0x0001), battery_scale_1dot0 }, + /* HP R1500 G2 and G3 INTL */ + { USB_DEVICE(HP_VENDORID, 0x1fe0), battery_scale_1dot0 }, + /* HP T750 G2 */ + { USB_DEVICE(HP_VENDORID, 0x1fe1), battery_scale_1dot0 }, + /* e.g. ? */ + { USB_DEVICE(HP_VENDORID, 0x1fe2), battery_scale_1dot0 }, + /* HP T1500 G3 */ + { USB_DEVICE(HP_VENDORID, 0x1fe3), battery_scale_1dot0 }, /* HP T750 INTL */ { USB_DEVICE(HP_VENDORID, 0x1f06), battery_scale_1dot0 }, /* HP T1000 INTL */ @@ -117,13 +151,12 @@ static usb_device_id_t tripplite_usb_device_table[] = { { USB_DEVICE(HP_VENDORID, 0x1f09), battery_scale_1dot0 }, /* HP R/T 2200 INTL (like SMART2200RMXL2U) */ { USB_DEVICE(HP_VENDORID, 0x1f0a), battery_scale_1dot0 }, - /* HP R1500 G2 INTL */ - { USB_DEVICE(HP_VENDORID, 0x1fe0), battery_scale_1dot0 }, - /* HP T750 G2 */ - { USB_DEVICE(HP_VENDORID, 0x1fe1), battery_scale_1dot0 }, + + /* Delta/Minuteman Enterprise Plus E1500RM2U */ + { USB_DEVICE(DELTA_VENDORID, 0xa011), battery_scale_1dot0 }, /* Terminating entry */ - { -1, -1, NULL } + { 0, 0, NULL } }; /* returns statically allocated string - must not use it again before @@ -149,7 +182,7 @@ static const char *tripplite_chemistry_fun(double value) } static info_lkp_t tripplite_chemistry[] = { - { 0, NULL, tripplite_chemistry_fun } + { 0, NULL, tripplite_chemistry_fun, NULL } }; /* returns statically allocated string - must not use it again before @@ -164,7 +197,46 @@ static const char *tripplite_battvolt_fun(double value) } static info_lkp_t tripplite_battvolt[] = { - { 0, NULL, tripplite_battvolt_fun } + { 0, NULL, tripplite_battvolt_fun, NULL } +}; + +static const char *tripplite_iovolt_fun(double value) +{ + static char buf[8]; + + snprintf(buf, sizeof(buf), "%.1f", io_voltage_scale * value); + + return buf; +} + +static info_lkp_t tripplite_iovolt[] = { + { 0, NULL, tripplite_iovolt_fun, NULL } +}; + +static const char *tripplite_iofreq_fun(double value) +{ + static char buf[8]; + + snprintf(buf, sizeof(buf), "%.1f", io_frequency_scale * value); + + return buf; +} + +static info_lkp_t tripplite_iofreq[] = { + { 0, NULL, tripplite_iofreq_fun, NULL } +}; + +static const char *tripplite_ioamp_fun(double value) +{ + static char buf[8]; + + snprintf(buf, sizeof(buf), "%.1f", io_current_scale * value); + + return buf; +} + +static info_lkp_t tripplite_ioamp[] = { + { 0, NULL, tripplite_ioamp_fun, NULL } }; /* --------------------------------------------------------------- */ @@ -174,19 +246,41 @@ static info_lkp_t tripplite_battvolt[] = { /* TRIPPLITE usage table */ static usage_lkp_t tripplite_usage_lkp[] = { /* currently unknown: - ffff0010, 00ff0001, ffff007d, ffff00c0, ffff00c1, ffff00c2, + 00ff0001, ffff007d, ffff00c0, ffff00c1, ffff00c2, ffff00c3, ffff00c4, ffff00c5, ffff00d2, ffff0091, ffff00c7 */ + { "TLCustom", 0xffff0010 }, + { "TLDelayBeforeStartup", 0xffff0056 }, /* in minutes */ { "TLLowVoltageTransferMax", 0xffff0057 }, { "TLLowVoltageTransferMin", 0xffff0058 }, { "TLHighVoltageTransferMax", 0xffff0059 }, { "TLHighVoltageTransferMin", 0xffff005a }, + /* Outlet state: + * 1- On/Closed + * 2- Off/Open + * 3- On with pending off + * 4- Off with pending on + * 5- Unknown + * 6- Resolved/Unknown + * 7- Failed and Closed + * 8- Failed and Open */ + { "OutletState", 0xffff007a }, + { "OutletCount", 0xffff007b }, /* Number of load segments */ + { "UPSFirmwareVersion", 0xffff007c }, + { "CommunicationProtocolVersion", 0xffff007d }, /* HID protocol version */ + { "CommunicationVersion", 0xffff007e }, /* USB firmware version */ + { "iUPSPartNumber", 0xffff007f }, /* String index to Part Number */ + { "AutoOnDelay", 0xffff0080 }, /* '0' (no delay) to 'n' (delay in seconds) */ { "TLWatchdog", 0xffff0092 }, + { "TLOutletsAvailableMask", 0xffff0095 }, + { "TLOutletsStatusMask", 0xffff0096 }, /* it looks like Tripp Lite confused pages 0x84 and 0x85 for the following 4 items, on some OMNI1000LCD devices. */ { "TLCharging", 0x00840044 }, /* conflicts with HID spec! */ - { "TLDischarging", 0x00840045 }, /* conflicts with HID spec! */ + /* conflicts with HID spec (and HP implementation) for TrippLite! + * Refer to tripplite_discharging_info */ + { "TLDischarging", 0x00840045 }, { "TLNeedReplacement", 0x0084004b }, { "TLACPresent", 0x008400d0 }, { NULL, 0 } @@ -209,19 +303,19 @@ static hid_info_t tripplite_hid2nut[] = { /* unmapped variables - meaning unknown */ { "UPS.Flow.0xffff0097", 0, 0, "UPS.Flow.0xffff0097", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff0075", 0, 0, "UPS.0xffff0010.[1].0xffff0075", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff0076", 0, 0, "UPS.0xffff0010.[1].0xffff0076", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff007c", 0, 0, "UPS.0xffff0010.[1].0xffff007c", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff007d", 0, 0, "UPS.0xffff0010.[1].0xffff007d", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff00e0", 0, 0, "UPS.0xffff0010.[1].0xffff00e0", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff00e1", 0, 0, "UPS.0xffff0010.[1].0xffff00e1", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff00e2", 0, 0, "UPS.0xffff0010.[1].0xffff00e2", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff00e3", 0, 0, "UPS.0xffff0010.[1].0xffff00e3", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff00e4", 0, 0, "UPS.0xffff0010.[1].0xffff00e4", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff00e5", 0, 0, "UPS.0xffff0010.[1].0xffff00e5", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff00e6", 0, 0, "UPS.0xffff0010.[1].0xffff00e6", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff00e7", 0, 0, "UPS.0xffff0010.[1].0xffff00e7", NULL, "%.0f", 0, NULL }, - { "UPS.0xffff0010.[1].0xffff00e8", 0, 0, "UPS.0xffff0010.[1].0xffff00e8", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff0075", 0, 0, "UPS.TLCustom.[1].0xffff0075", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff0076", 0, 0, "UPS.TLCustom.[1].0xffff0076", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff007c", 0, 0, "UPS.TLCustom.[1].0xffff007c", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff007d", 0, 0, "UPS.TLCustom.[1].0xffff007d", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff00e0", 0, 0, "UPS.TLCustom.[1].0xffff00e0", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff00e1", 0, 0, "UPS.TLCustom.[1].0xffff00e1", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff00e2", 0, 0, "UPS.TLCustom.[1].0xffff00e2", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff00e3", 0, 0, "UPS.TLCustom.[1].0xffff00e3", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff00e4", 0, 0, "UPS.TLCustom.[1].0xffff00e4", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff00e5", 0, 0, "UPS.TLCustom.[1].0xffff00e5", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff00e6", 0, 0, "UPS.TLCustom.[1].0xffff00e6", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff00e7", 0, 0, "UPS.TLCustom.[1].0xffff00e7", NULL, "%.0f", 0, NULL }, + { "UPS.TLCustom.[1].0xffff00e8", 0, 0, "UPS.TLCustom.[1].0xffff00e8", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00c0", 0, 0, "UPS.0xffff0015.[1].0xffff00c0", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00c1", 0, 0, "UPS.0xffff0015.[1].0xffff00c1", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00c2", 0, 0, "UPS.0xffff0015.[1].0xffff00c2", NULL, "%.0f", 0, NULL }, @@ -231,7 +325,6 @@ static hid_info_t tripplite_hid2nut[] = { { "UPS.0xffff0015.[1].0xffff00d2", 0, 0, "UPS.0xffff0015.[1].0xffff00d2", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00d3", 0, 0, "UPS.0xffff0015.[1].0xffff00d3", NULL, "%.0f", 0, NULL }, { "UPS.0xffff0015.[1].0xffff00d6", 0, 0, "UPS.0xffff0015.[1].0xffff00d6", NULL, "%.0f", 0, NULL }, - { "UPS.OutletSystem.Outlet.0xffff0056", 0, 0, "UPS.OutletSystem.Outlet.0xffff0056", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff0081", 0, 0, "UPS.OutletSystem.Outlet.0xffff0081", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff0091", 0, 0, "UPS.OutletSystem.Outlet.0xffff0091", NULL, "%.0f", 0, NULL }, { "UPS.OutletSystem.Outlet.0xffff0093", 0, 0, "UPS.OutletSystem.Outlet.0xffff0093", NULL, "%.0f", 0, NULL }, @@ -249,6 +342,9 @@ static hid_info_t tripplite_hid2nut[] = { #endif /* USBHID_UPS_TRIPPLITE_DEBUG */ + /* Device page */ + { "device.part", 0, 0, "UPS.TLCustom.[1].iUPSPartNumber", NULL, "%s", HU_FLAG_STATIC, stringid_conversion }, + /* Battery page */ { "battery.charge", 0, 0, "UPS.PowerSummary.RemainingCapacity", NULL, "%.0f", 0, NULL }, { "battery.charge", 0, 0, "UPS.BatterySystem.Battery.RemainingCapacity", NULL, "%.0f", 0, NULL }, @@ -262,8 +358,16 @@ static hid_info_t tripplite_hid2nut[] = { /* UPS page */ { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.OutletSystem.Outlet.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, + { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.OutletSystem.Outlet.TLDelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, + /* FIXME + { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 6, "UPS.TLCustom.[1].TLDelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_FLAG_ABSENT, NULL}, + { "ups.timer.start", 0, 0, "UPS.TLCustom.[1].DelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, + - what's the right notion behind this one? + { "ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING, 6, "UPS.TLCustom.[1].AutoOnDelay", NULL, DEFAULT_ONDELAY, 0, NULL}, + */ { "ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING, 10, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_FLAG_ABSENT, NULL}, { "ups.timer.start", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, + { "ups.timer.start", 0, 0, "UPS.OutletSystem.Outlet.TLDelayBeforeStartup", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.shutdown", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL}, { "ups.timer.reboot", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeReboot", NULL, "%.0f", HU_FLAG_QUICK_POLL, NULL }, { "ups.test.result", 0, 0, "UPS.BatterySystem.Test", NULL, "%s", 0, test_read_info }, @@ -272,6 +376,10 @@ static hid_info_t tripplite_hid2nut[] = { { "ups.power", 0, 0, "UPS.OutletSystem.Outlet.ActivePower", NULL, "%.1f", 0, NULL }, { "ups.power", 0, 0, "UPS.PowerConverter.Output.ActivePower", NULL, "%.1f", 0, NULL }, { "ups.load", 0, 0, "UPS.OutletSystem.Outlet.PercentLoad", NULL, "%.0f", 0, NULL }, + /* FIXME: what is the conversion format for this one? + * Example on HP T1500 G3 + * UPS.TLCustom.[1].UPSFirmwareVersion, Type: Feature, ReportID: 0x0f, Offset: 0, Size: 16, Value: 262 */ + { "ups.firmware", 0, 0, "UPS.TLCustom.[1].UPSFirmwareVersion", NULL, "%.0f", HU_FLAG_STATIC, NULL }, /* Number of seconds left before the watchdog reboots the UPS (0 = disabled) */ { "ups.watchdog.status", 0, 0, "UPS.OutletSystem.Outlet.TLWatchdog", NULL, "%.0f", 0, NULL }, @@ -288,10 +396,16 @@ static hid_info_t tripplite_hid2nut[] = { { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.NeedReplacement", NULL, NULL, 0, replacebatt_info }, /* repeat some of the above for faulty usage codes (seen on OMNI1000LCD, untested) */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.TLACPresent", NULL, NULL, HU_FLAG_QUICK_POLL, online_info }, + /* "Redundant" definition to deal with the conflict between + * TrippLite units, wrongly defining 0x00840045 as "TLDischarging" + * and HP which uses the standard 0x00840045 (as ConfigPercentLoad). + * Note that this path should not exist on HP devices. */ { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.TLDischarging", NULL, NULL, HU_FLAG_QUICK_POLL, discharging_info }, + /* Otherwise, define the version for HP devices */ + { "ups.load.nominal", 0, 0, "UPS.Flow.ConfigPercentLoad", NULL, "%.0f", 0, NULL }, + { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.TLCharging", NULL, NULL, HU_FLAG_QUICK_POLL, charging_info }, { "BOOL", 0, 0, "UPS.PowerSummary.PresentStatus.TLNeedReplacement", NULL, NULL, 0, replacebatt_info }, - { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.VoltageOutOfRange", NULL, NULL, 0, vrange_info }, { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.Buck", NULL, NULL, 0, trim_info }, { "BOOL", 0, 0, "UPS.PowerConverter.PresentStatus.Boost", NULL, NULL, 0, boost_info }, @@ -313,9 +427,9 @@ static hid_info_t tripplite_hid2nut[] = { /* Input page */ { "input.voltage.nominal", 0, 0, "UPS.PowerSummary.Input.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, - { "input.voltage", 0, 0, "UPS.PowerSummary.Input.Voltage", NULL, "%.1f", 0, NULL }, - { "input.voltage", 0, 0, "UPS.PowerConverter.Input.Voltage", NULL, "%.1f", 0, NULL }, - { "input.frequency", 0, 0, "UPS.PowerConverter.Input.Frequency", NULL, "%.1f", 0, NULL }, + { "input.voltage", 0, 0, "UPS.PowerSummary.Input.Voltage", NULL, "%s", 0, tripplite_iovolt }, + { "input.voltage", 0, 0, "UPS.PowerConverter.Input.Voltage", NULL, "%s", 0, tripplite_iovolt }, + { "input.frequency", 0, 0, "UPS.PowerConverter.Input.Frequency", NULL, "%s", 0, tripplite_iofreq }, { "input.transfer.low", ST_FLAG_RW | ST_FLAG_STRING, 5, "UPS.PowerConverter.Output.LowVoltageTransfer", NULL, "%.1f", HU_FLAG_SEMI_STATIC, NULL }, { "input.transfer.low.max", 0, 0, "UPS.PowerConverter.Output.TLLowVoltageTransferMax", NULL, "%.0f", HU_FLAG_STATIC, NULL }, { "input.transfer.low.min", 0, 0, "UPS.PowerConverter.Output.TLLowVoltageTransferMin", NULL, "%.0f", HU_FLAG_STATIC, NULL }, @@ -325,11 +439,11 @@ static hid_info_t tripplite_hid2nut[] = { /* Output page */ { "output.voltage.nominal", 0, 0, "UPS.Flow.ConfigVoltage", NULL, "%.0f", HU_FLAG_STATIC, NULL }, - { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%.1f", 0, NULL }, - { "output.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%.1f", 0, NULL }, - { "output.current", 0, 0, "UPS.PowerConverter.Output.Current", NULL, "%.2f", 0, NULL }, + { "output.voltage", 0, 0, "UPS.PowerConverter.Output.Voltage", NULL, "%s", 0, tripplite_iovolt }, + { "output.voltage", 0, 0, "UPS.PowerSummary.Voltage", NULL, "%s", 0, tripplite_iovolt }, + { "output.current", 0, 0, "UPS.PowerConverter.Output.Current", NULL, "%s", 0, tripplite_ioamp }, { "output.frequency.nominal", 0, 0, "UPS.Flow.ConfigFrequency", NULL, "%.0f", HU_FLAG_STATIC, NULL }, - { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", NULL, "%.1f", 0, NULL }, + { "output.frequency", 0, 0, "UPS.PowerConverter.Output.Frequency", NULL, "%s", 0, tripplite_iofreq }, /* instant commands. */ { "test.battery.start.quick", 0, 0, "UPS.BatterySystem.Test", NULL, "1", HU_TYPE_CMD, NULL }, /* reported to work on OMNI1000 */ @@ -338,6 +452,7 @@ static hid_info_t tripplite_hid2nut[] = { { "load.off.delay", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, DEFAULT_OFFDELAY, HU_TYPE_CMD, NULL }, { "load.on.delay", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, + { "load.on.delay", 0, 0, "UPS.OutletSystem.Outlet.TLDelayBeforeStartup", NULL, DEFAULT_ONDELAY, HU_TYPE_CMD, NULL }, { "shutdown.stop", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeShutdown", NULL, "-1", HU_TYPE_CMD, NULL }, { "shutdown.reboot", 0, 0, "UPS.OutletSystem.Outlet.DelayBeforeReboot", NULL, "10", HU_TYPE_CMD, NULL }, @@ -354,6 +469,18 @@ static hid_info_t tripplite_hid2nut[] = { { "beeper.enable", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "2", HU_TYPE_CMD, NULL }, { "beeper.mute", 0, 0, "UPS.PowerSummary.AudibleAlarmControl", NULL, "3", HU_TYPE_CMD, NULL }, + /* FIXME (to be tested): HP specific (may conflict or differ from TL implementation!) + * { "outlet.count", 0, 0, "UPS.TLCustom.[1].OutletCount", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + * { "outlet.status", 0, 0, "UPS.TLCustom.[1].OutletState", NULL, "%.0f", HU_FLAG_STATIC, NULL }, + 0.284486 Path: UPS.TLCustom.[1].ffff00ff, Type: Feature, ReportID: 0xff, Offset: 0, Size: 8, Value: 255 + 0.285276 Path: UPS.TLCustom.[1].OutletCount, Type: Feature, ReportID: 0x6d, Offset: 0, Size: 8, Value: 1 + 0.286260 Path: UPS.TLCustom.[1].OutletState, Type: Feature, ReportID: 0x70, Offset: 0, Size: 8, Value: 1 + 0.287248 Path: UPS.TLCustom.[1].CommunicationVersion, Type: Feature, ReportID: 0x0e, Offset: 0, Size: 16, Value: 262 + 0.288901 Path: UPS.TLCustom.[1].CommunicationProtocolVersion, Type: Feature, ReportID: 0x6c, Offset: 0, Size: 16, Value: 2560 + 0.289903 Path: UPS.TLCustom.[1].TLDelayBeforeStartup, Type: Feature, ReportID: 0x71, Offset: 0, Size: 16, Value: 65535 + 0.290854 Path: UPS.TLCustom.[1].AutoOnDelay, Type: Feature, ReportID: 0x72, Offset: 0, Size: 16, Value: 65535 + */ + /* end of structure. */ { NULL, 0, 0, NULL, NULL, NULL, 0, NULL } }; @@ -374,8 +501,7 @@ static const char *tripplite_format_serial(HIDDevice_t *hd) { * the device is supported by this subdriver, else 0. */ static int tripplite_claim(HIDDevice_t *hd) { - int status = is_usb_device_supported(tripplite_usb_device_table, hd->VendorID, - hd->ProductID); + int status = is_usb_device_supported(tripplite_usb_device_table, hd); switch (status) { @@ -436,4 +562,5 @@ subdriver_t tripplite_subdriver = { tripplite_format_model, tripplite_format_mfr, tripplite_format_serial, + fix_report_desc, }; diff --git a/drivers/tripplite-hid.h b/drivers/tripplite-hid.h index b444c4d..5a09c08 100644 --- a/drivers/tripplite-hid.h +++ b/drivers/tripplite-hid.h @@ -1,6 +1,6 @@ /* tripplite-hid.h - data to monitor Tripp Lite USB/HID devices with NUT * - * Copyright (C) + * Copyright (C) * 2003 - 2005 Arnaud Quette * 2005 Peter Selinger * diff --git a/drivers/tripplite.c b/drivers/tripplite.c index 78ef253..672c734 100644 --- a/drivers/tripplite.c +++ b/drivers/tripplite.c @@ -65,13 +65,17 @@ * :C -- fetches result of a self-test * :K1 -- turns on power receptacles * :K0 -- turns off power receptacles + * :K3 -- turns on bank 1 receptacle(s) + * :K2 -- turns off bank 1 receptacle(s) + * :K5 -- turns on bank 2 receptacle(s) + * :K4 -- turns off bank 2 receptacle(s) * :G -- unconfirmed: shuts down UPS until power returns * :Q1 -- enable "Remote Reboot" * :Q0 -- disable "Remote Reboot" * :W -- returns 'W' data * :L -- returns 'L' data * :V -- returns 'V' data (firmware revision) - * :X -- returns 'X' data (firmware revision) + * :X -- returns 'X' data (firmware checksum) * :D -- returns general status data * :B -- returns battery voltage (hexadecimal decivolts) * :I -- returns minimum input voltage (hexadecimal hertz) @@ -108,11 +112,12 @@ #include "main.h" #include "serial.h" #include "tripplite.h" +#include "nut_stdint.h" #include #include #define DRIVER_NAME "Tripp-Lite SmartUPS driver" -#define DRIVER_VERSION "0.91" +#define DRIVER_VERSION "0.93" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -130,7 +135,7 @@ static unsigned int offdelay = DEFAULT_OFFDELAY; static unsigned int startdelay = DEFAULT_STARTDELAY; static unsigned int bootdelay = DEFAULT_BOOTDELAY; -static int hex2d(char *start, unsigned int len) +static long hex2d(char *start, unsigned int len) { char buf[32]; @@ -146,16 +151,16 @@ static int hex2d(char *start, unsigned int len) * very clean. * * return: # of chars in buf, excluding terminating \0 */ -static int send_cmd(const char *str, char *buf, size_t len) +static ssize_t send_cmd(const char *str, char *buf, size_t len) { - unsigned char c; - int ret = 0; - size_t i = 0; + char c; + ssize_t ret = 0; + ssize_t i = 0; ser_flush_io(upsfd); ser_send(upsfd, "%s", str); - if (!len || !buf) + if (!len || !buf || len > SSIZE_MAX) return -1; for (;;) { @@ -173,14 +178,15 @@ static int send_cmd(const char *str, char *buf, size_t len) if (c == IGNCHAR || c == ENDCHAR) continue; buf[i++] = c; - } while (c != ENDCHAR && i < len); + } while (c != ENDCHAR && i < (int)len); buf[i] = '\0'; return i; } static void get_letter_cmd(const char *str, char *buf, size_t len) { - int tries, ret; + int tries; + ssize_t ret; for (tries = 0; tries < MAXTRIES; ++tries) { ret = send_cmd(str, buf, len); @@ -190,7 +196,7 @@ static void get_letter_cmd(const char *str, char *buf, size_t len) fatalx(EXIT_FAILURE, "\nFailed to find UPS - giving up..."); } -static int do_reboot_now(void) +static ssize_t do_reboot_now(void) { char buf[256], cmd[16]; @@ -207,7 +213,7 @@ static void do_reboot(void) do_reboot_now(); } -static int soft_shutdown(void) +static ssize_t soft_shutdown(void) { char buf[256], cmd[16]; @@ -216,7 +222,7 @@ static int soft_shutdown(void) return send_cmd(":G\r", buf, sizeof buf); } -static int hard_shutdown(void) +static ssize_t hard_shutdown(void) { char buf[256], cmd[16]; @@ -241,6 +247,22 @@ static int instcmd(const char *cmdname, const char *extra) send_cmd(":K1\r", buf, sizeof buf); return STAT_INSTCMD_HANDLED; } + if (!strcasecmp(cmdname, "outlet.1.load.off")) { + send_cmd(":K2\r", buf, sizeof buf); + return STAT_INSTCMD_HANDLED; + } + if (!strcasecmp(cmdname, "outlet.1.load.on")) { + send_cmd(":K3\r", buf, sizeof buf); + return STAT_INSTCMD_HANDLED; + } + if (!strcasecmp(cmdname, "outlet.2.load.off")) { + send_cmd(":K4\r", buf, sizeof buf); + return STAT_INSTCMD_HANDLED; + } + if (!strcasecmp(cmdname, "outlet.2.load.on")) { + send_cmd(":K5\r", buf, sizeof buf); + return STAT_INSTCMD_HANDLED; + } if (!strcasecmp(cmdname, "shutdown.reboot")) { do_reboot_now(); return STAT_INSTCMD_HANDLED; @@ -258,25 +280,31 @@ static int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } static int setvar(const char *varname, const char *val) { if (!strcasecmp(varname, "ups.delay.shutdown")) { - offdelay = atoi(val); - dstate_setinfo("ups.delay.shutdown", "%d", offdelay); + int ipv = atoi(val); + if (ipv >= 0) + offdelay = (unsigned int)ipv; + dstate_setinfo("ups.delay.shutdown", "%u", offdelay); return STAT_SET_HANDLED; } if (!strcasecmp(varname, "ups.delay.start")) { - startdelay = atoi(val); - dstate_setinfo("ups.delay.start", "%d", startdelay); + int ipv = atoi(val); + if (ipv >= 0) + startdelay = (unsigned int)ipv; + dstate_setinfo("ups.delay.start", "%u", startdelay); return STAT_SET_HANDLED; } if (!strcasecmp(varname, "ups.delay.reboot")) { - bootdelay = atoi(val); - dstate_setinfo("ups.delay.reboot", "%d", bootdelay); + int ipv = atoi(val); + if (ipv >= 0) + bootdelay = (unsigned int)ipv; + dstate_setinfo("ups.delay.reboot", "%u", bootdelay); return STAT_SET_HANDLED; } return STAT_SET_UNKNOWN; @@ -286,8 +314,8 @@ void upsdrv_initinfo(void) { const char *model; char w_value[16], l_value[16], v_value[16], x_value[16]; - int va; - long w, l; + int gen, plugs; + long w, l, va; get_letter_cmd(":W\r", w_value, sizeof w_value); get_letter_cmd(":L\r", l_value, sizeof l_value); @@ -299,17 +327,20 @@ void upsdrv_initinfo(void) w = hex2d(w_value, 2); l = hex2d(l_value, 2); - model = "Smart %d"; + model = "Smart"; if (w & 0x40) - model = "Unison %d"; + model = "Unison"; va = ((w & 0x3f) * 32 + (l >> 3)) * 5; /* New formula */ if (!(w & 0x80)) va = l / 2; /* Old formula */ - dstate_setinfo("ups.model", model, va); - dstate_setinfo("ups.firmware", "%c%c", - 'A'+v_value[0]-'0', 'A'+v_value[1]-'0'); + gen = 1 + (!(x_value[0] & 0x07) * !(x_value[1] & 0x07)); + plugs = x_value[0] - !!(x_value[1] >> 3) * 8; + + dstate_setinfo("ups.model", "%s %ld", model, va); + dstate_setinfo("ups.firmware", "%c%c (Gen %d)", + 'A'+v_value[0]-'0', 'A'+v_value[1]-'0', gen); dstate_setinfo("ups.delay.shutdown", "%d", offdelay); dstate_setflags("ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING); @@ -329,6 +360,15 @@ void upsdrv_initinfo(void) dstate_addcmd("shutdown.return"); dstate_addcmd("shutdown.stayoff"); + if (plugs > 1) { + dstate_addcmd("outlet.1.load.off"); + dstate_addcmd("outlet.1.load.on"); + if (plugs > 2) { + dstate_addcmd("outlet.2.load.off"); + dstate_addcmd("outlet.2.load.on"); + } + } + upsh.instcmd = instcmd; upsh.setvar = setvar; @@ -345,7 +385,9 @@ void upsdrv_updateinfo(void) { static int numfails; char buf[256]; - int bp, volt, temp, load, vmax, vmin, stest, len; + int bp, temp; + ssize_t len; + long volt, load, vmax, vmin, stest; int bcond, lstate, tstate, mode; float bv, freq; @@ -353,7 +395,7 @@ void upsdrv_updateinfo(void) if (len != 21) { ++numfails; if (numfails > MAXTRIES) { - ser_comm_fail("Data command failed: [%d] bytes != 21 bytes.", len); + ser_comm_fail("Data command failed: [%zd] bytes != 21 bytes.", len); dstate_datastale(); } return; @@ -373,7 +415,7 @@ void upsdrv_updateinfo(void) freq > FREQ_MAX || freq < FREQ_MIN) { ++numfails; if (numfails > MAXTRIES) { - ser_comm_fail("Data out of bounds: [%0d,%3d,%3d,%02.2f]", + ser_comm_fail("Data out of bounds: [%0ld,%3d,%3ld,%02.2f]", volt, temp, load, freq); dstate_datastale(); } @@ -381,7 +423,7 @@ void upsdrv_updateinfo(void) } send_cmd(":B\r", buf, sizeof buf); - bv = (float)hex2d(buf, 2) / 10.0; + bv = (float)(hex2d(buf, 2)) / 10.0; if (bv > 50.0 || bv < 0.0) { ++numfails; if (numfails > MAXTRIES) { @@ -396,7 +438,7 @@ void upsdrv_updateinfo(void) if (vmax > INVOLT_MAX || vmax < INVOLT_MIN) { ++numfails; if (numfails > MAXTRIES) { - ser_comm_fail("InVoltMax out of bounds: [%d]", vmax); + ser_comm_fail("InVoltMax out of bounds: [%ld]", vmax); dstate_datastale(); } return; @@ -407,7 +449,7 @@ void upsdrv_updateinfo(void) if (vmin > INVOLT_MAX || vmin < INVOLT_MIN) { ++numfails; if (numfails > MAXTRIES) { - ser_comm_fail("InVoltMin out of bounds: [%d]", vmin); + ser_comm_fail("InVoltMin out of bounds: [%ld]", vmin); dstate_datastale(); } return; @@ -419,7 +461,7 @@ void upsdrv_updateinfo(void) if (errno == ERANGE) { ++numfails; if (numfails > MAXTRIES) { - ser_comm_fail("Self test is out of range: [%d]", stest); + ser_comm_fail("Self test is out of range: [%ld]", stest); dstate_datastale(); } return; @@ -435,7 +477,7 @@ void upsdrv_updateinfo(void) if (stest > 3 || stest < 0) { ++numfails; if (numfails > MAXTRIES) { - ser_comm_fail("Self test out of bounds: [%d]", stest); + ser_comm_fail("Self test out of bounds: [%ld]", stest); dstate_datastale(); } return; @@ -444,9 +486,9 @@ void upsdrv_updateinfo(void) /* We've successfully gathered all the data for an update. */ numfails = 0; - dstate_setinfo("input.voltage", "%0d", volt); + dstate_setinfo("input.voltage", "%0ld", volt); dstate_setinfo("ups.temperature", "%3d", temp); - dstate_setinfo("ups.load", "%3d", load); + dstate_setinfo("ups.load", "%3ld", load); dstate_setinfo("input.frequency", "%02.2f", freq); status_init(); @@ -524,8 +566,8 @@ void upsdrv_updateinfo(void) dstate_setinfo("battery.voltage", "%.1f", bv); dstate_setinfo("battery.charge", "%3d", bp); - dstate_setinfo("input.voltage.maximum", "%d", vmax); - dstate_setinfo("input.voltage.minimum", "%d", vmin); + dstate_setinfo("input.voltage.maximum", "%ld", vmax); + dstate_setinfo("input.voltage.minimum", "%ld", vmin); switch (stest) { case 0: @@ -572,13 +614,23 @@ void upsdrv_initups(void) { upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B2400); + char *val; - if (getval("offdelay")) - offdelay = atoi(getval("offdelay")); - if (getval("startdelay")) - startdelay = atoi(getval("startdelay")); - if (getval("rebootdelay")) - bootdelay = atoi(getval("rebootdelay")); + if ((val = getval("offdelay"))) { + int ipv = atoi(val); + if (ipv >= 0) + offdelay = (unsigned int)ipv; + } + if ((val = getval("startdelay"))) { + int ipv = atoi(val); + if (ipv >= 0) + startdelay = (unsigned int)ipv; + } + if ((val = getval("rebootdelay"))) { + int ipv = atoi(val); + if (ipv >= 0) + bootdelay = (unsigned int)ipv; + } } void upsdrv_cleanup(void) diff --git a/drivers/tripplite.h b/drivers/tripplite.h index 931d62d..498f69c 100644 --- a/drivers/tripplite.h +++ b/drivers/tripplite.h @@ -23,6 +23,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef NUT_TRIPPLITE_H_SEEN +#define NUT_TRIPPLITE_H_SEEN 1 + #define ENDCHAR '\n' /* replies end with CR LF -- use LF to end */ #define IGNCHAR '\r' /* ignore CR */ #define MAXTRIES 3 /* max number of times we try to detect ups */ @@ -56,3 +59,4 @@ #define MAX_VOLT 13.4 /* Max battery voltage (100%) */ #define MIN_VOLT 11.0 /* Min battery voltage (10%) */ +#endif /* NUT_TRIPPLITE_H_SEEN */ diff --git a/drivers/tripplite_usb.c b/drivers/tripplite_usb.c index 56061a7..cbaf73b 100644 --- a/drivers/tripplite_usb.c +++ b/drivers/tripplite_usb.c @@ -1,4 +1,4 @@ -/*!@file tripplite_usb.c +/*!@file tripplite_usb.c * @brief Driver for Tripp Lite non-PDC/HID USB models. */ /* @@ -8,7 +8,8 @@ Copyright (C) 1999 Russell Kroll Copyright (C) 2001 Rickard E. (Rik) Faith Copyright (C) 2004 Nicholas J. Kain - Copyright (C) 2005-2008 Charles Lepple + Copyright (C) 2005-2008, 2014 Charles Lepple + Copyright (C) 2016 Eaton This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,7 +27,7 @@ */ -/* % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % +/* % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % * * Protocol 1001 * @@ -37,12 +38,12 @@ * :P -> P01000X (1000VA unit) * :S -> Sbb_XXX (bb = 10: on-line, 11: on battery) * :V -> V102XXX (firmware/protocol version?) - * :Wt -> Wt (watchdog; t = time in seconds (binary, not hex), + * :Wt -> Wt (watchdog; t = time in seconds (binary, not hex), * 0 = disable; if UPS is not pinged in this interval, it * will power off the load, and then power it back on after * a delay.) * - * % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % + * % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % * * The outgoing commands are sent with HID Set_Report commands over EP0 * (control message), and incoming commands are received on EP1IN (interrupt @@ -53,14 +54,14 @@ * The descriptors say that bInterval is 10 ms. You generally need to wait at * least 80-90 ms to get some characters back from the device. If it takes * more than 250 ms, you probably need to resend the command. - * + * * All outgoing commands are followed by a checksum, which is 255 - (sum of * characters after ':'), and then by '\r'. All responses should start with * the command letter that was sent (no colon), and should be followed by * '\r'. If the command is not supported (or apparently if there is a serial * timeout internally), the previous response will be echoed back. * - * % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % + * % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % * * SMARTPRO commands (3003): * @@ -85,14 +86,14 @@ * individually switchable.) * :W_ -> W_ (watchdog) * :Z -> Z (reset for max/min; takes a moment to complete) - * - * % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % + * + * % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % * * The SMARTPRO unit seems to be slightly saner with regard to message * polling. It specifies an interrupt in interval of 100 ms, but I just - * started at a 2 second timeout to obtain the above table. + * started at a 2 second timeout to obtain the above table. * - * % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % + * % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % % * * Commands from serial tripplite.c: * @@ -125,202 +126,17 @@ * :S -- enables remote reboot/remote power on */ -/* - -POD ("Plain Old Documentation") - run through pod2html or perldoc. See -perlpod(1) for more information. - -pod2man --name='TRIPPLITE_USB' --section=8 --release='$Rev: 2598 $' --center='Network UPS Tools (NUT)' tripplite_usb.c - -=head1 NAME - -tripplite_usb - Driver for older Tripp Lite USB UPSes (not PDC HID) - -=head1 NOTE - -This man page only documents the hardware-specific features of the -tripplite_usb driver. For information about the core driver, see -nutupsdrv(8). - -=head1 SUPPORTED HARDWARE - -This driver should work with older Tripp Lite UPSes which are detected as USB -HID-class devices, but are not true HID Power-Device Class devices. So far, -the devices supported by tripplite_usb have product ID 0001, and the newer -units (such as those with "LCD" in the model name) with product ID 2001 require -the usbhid-ups driver instead. Please report success or failure to -the nut-upsuser mailing list. A key piece of information is the protocol -number, returned in ups.debug.0. Also, be sure to turn on debugging (C<-DDD>) -for more informative log messages. If your Tripp Lite UPS uses a serial port, -you may wish to investigate the tripplite(8) or tripplite_su(8) driver. - -This driver has been tested with the following models: - -=over - -=item * INTERNETOFFICE700 - -=item * OMNISV1000 - -=item * OMNISV1500XL (some warnings) - -=item * SMART700USB - -=item * SMART1500RM2U - -=item * SMART2200RMXL2U - -=item * SMART3000RM2U - -=back - -If you have used Tripp Lite's PowerAlert software to connect to your UPS, there -is a good chance that tripplite_usb(8) will work if it uses one of the -following protocols: - -=over - -=item * Protocol 0004 - -=item * Protocol 1001 - -=item * Protocol 2001 - -=item * Protocol 3003 - -=back - -=head1 EXTRA ARGUMENTS - -This driver supports the following optional setting in the ups.conf(5) file -(or with C<-x> on the command line): - -=over - -=item offdelay - -This setting controls the delay between receiving the "kill" command (C<-k>) -and actually cutting power to the computer. - -=item bus - -This regular expression is used to match the USB bus (as seen in -C or lsusb(8); including leading zeroes). - -=item product - -A regular expression to match the product string for the UPS. This would be -useful if you have two different Tripp Lite UPS models connected to the -system, and you want to be sure that you shut them down in the correct order. - -Note that this regex is matched against the full USB product string as seen in -lsusb(8). The C in the C output only lists the name after -"TRIPP LITE", so to match a SMART2200RMXL2U, you could use the regex -".*SMART2200.*". - -=item productid - -The productid is a regular expression which matches the UPS PID as four -hexadecimal digits. So far, the only devices that work with this driver have -PID C<0001>. - -=item serial - -It does not appear that these particular Tripp Lite UPSes use the iSerial -descriptor field to return a serial number. However, in case your unit does, -you may specify it here. - -=back - -For more information on regular expressions, see regex(7) - -=head1 RUNTIME VARIABLES - -=over - -=item ups.delay.shutdown - -This variable is the same as the C setting, but it can be changed at -runtime by upsrw(8). - -=item ups.id - -Some SMARTPRO models feature an ID that can be set and retrieved. If your UPS -supports this feature, this variable will be listed in the output of upsrw(8). - -=item outlet.1.switch - -Some Tripp Lite units have a switchable outlet (usually outlet #1) which can be -turned on and off by writing C<1> or C<0>, respectively, to C. -If your unit has multiple switchable outlets, substitute the outlet number for -"1" in the variable name. Be sure to test this first - there is no other way to -be certain that the number used by the driver matches the label on the unit. - -=back - -=head1 KNOWN ISSUES AND BUGS - -The driver was not developed with any official documentation from Tripp Lite, -so certain events may confuse the driver. If you observe any strange behavior, -please re-run the driver with C<-DDD> to increase the verbosity. - -So far, the Tripp Lite UPSes do not seem to have any serial number or other -unique identifier accessible through USB. Thus, when monitoring several Tripp -Lite USB UPSes, you should use either the C or C configuration -options to uniquely specify which UPS a given driver instance should control. - -For instance, you can easily monitor an OMNIVS1000 and a SMART1500RM2U at the -same time, since they have different USB Product ID strings. If you have two -SMART1500RM2U units, you would have to find which USB bus number each unit is -on (via C), which may result in ambiguities if the available USB ports -are on the same bus. - -Some of the SMART*2U models have an ID number, but because this ID is not -exposed as a USB string descriptor, there is no easy way to use this ID to -distinguish between multiple UPS units on a single machine. - -=head1 AUTHORS - -Charles Lepple Eclepple+nut@gmail.com, based on the tripplite driver by -Rickard E. (Rik) Faith Efaith@alephnull.comE and Nicholas Kain -Enicholas@kain.usE. Please do not email the authors directly - use the -nut-upsdev mailing list. - -A Tripp Lite OMNIVS1000 was graciously donated to the NUT project by: - -=over - -Relevant Evidence, LLC. - -http://www.relevantevidence.com - -Email: info@relevantevidence.com - -=back - -=head1 SEE ALSO - -=head2 The core driver: - -nutupsdrv(8), regex(7), usbhid-ups(8) - -=head2 Internet resources: - -The NUT (Network UPS Tools) home page: http://www.networkupstools.org/ - -=cut - -*/ +/* Watchdog for 3005 is 15 - 255 seconds. + */ #include "main.h" -#include "libusb.h" +#include "nut_libusb.h" #include #include -#include #include "usb-common.h" #define DRIVER_NAME "Tripp Lite OMNIVS / SMARTPRO driver" -#define DRIVER_VERSION "0.20" +#define DRIVER_VERSION "0.33" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -335,20 +151,27 @@ upsdrv_info_t upsdrv_info = { }; /* TrippLite */ -#define TRIPPLITE_VENDORID 0x09ae +#define TRIPPLITE_VENDORID 0x09ae /* USB IDs device table */ static usb_device_id_t tripplite_usb_device_table[] = { /* e.g. OMNIVS1000, SMART550USB, ... */ { USB_DEVICE(TRIPPLITE_VENDORID, 0x0001), NULL }, - + /* Terminating entry */ - { -1, -1, NULL } + { 0, 0, NULL } }; -static int subdriver_match_func(USBDevice_t *hd, void *privdata) +static int subdriver_match_func(USBDevice_t *arghd, void *privdata) { - switch (is_usb_device_supported(tripplite_usb_device_table, hd->VendorID, hd->ProductID)) + NUT_UNUSED_VARIABLE(privdata); + + /* FIXME? Should we save "arghd" into global "hd" variable? + * This was previously shadowed by function argument named "hd"... + */ + /* hd = arghd; */ + + switch (is_usb_device_supported(tripplite_usb_device_table, arghd)) { case SUPPORTED: return 1; @@ -358,6 +181,8 @@ static int subdriver_match_func(USBDevice_t *hd, void *privdata) if (getval("productid")) { return 1; } + return 0; + case NOT_SUPPORTED: default: return 0; @@ -375,11 +200,70 @@ static enum tl_model_t { TRIPP_LITE_OMNIVS, TRIPP_LITE_OMNIVS_2001, TRIPP_LITE_SMARTPRO, - TRIPP_LITE_SMART_0004 + TRIPP_LITE_SMART_0004, + TRIPP_LITE_SMART_3005 } tl_model = TRIPP_LITE_UNKNOWN; +/*! Are the values encoded in ASCII or binary? + * TODO: Add 3004? + */ +static int is_binary_protocol() +{ + switch(tl_model) { + case TRIPP_LITE_SMART_3005: + return 1; + case TRIPP_LITE_SMARTPRO: + case TRIPP_LITE_SMART_0004: + case TRIPP_LITE_OMNIVS: + case TRIPP_LITE_OMNIVS_2001: + case TRIPP_LITE_UNKNOWN: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + return 0; + } +} + +/*! Is this the "SMART" family of protocols? + * TODO: Add 3004? + */ +static int is_smart_protocol() +{ + switch(tl_model) { + case TRIPP_LITE_SMARTPRO: + case TRIPP_LITE_SMART_0004: + case TRIPP_LITE_SMART_3005: + return 1; + case TRIPP_LITE_OMNIVS: + case TRIPP_LITE_OMNIVS_2001: + case TRIPP_LITE_UNKNOWN: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ + default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + return 0; + } +} + /*!@brief If a character is not printable, return a dot. */ -#define toprint(x) (isalnum((unsigned)x) ? (x) : '.') +#define toprint(x) (isalnum((int)x) ? (char)(x) : (char)'.') #define ENDCHAR 13 @@ -388,7 +272,7 @@ static enum tl_model_t { #define SEND_WAIT_NSEC (1000*1000*100) #define MAX_RECV_TRIES 10 -#define RECV_WAIT_MSEC 1000 /*! was 100 for OMNIVS; SMARTPRO units need longer */ +#define RECV_WAIT_MSEC 1000 /*!< was 100 for OMNIVS; SMARTPRO units need longer */ #define MAX_RECONNECT_TRIES 10 @@ -418,13 +302,13 @@ static usb_communication_subdriver_t *comm_driver = &usb_subdriver; /* Interval notation for Q% = 10% <= [minV, maxV] <= 100% */ static double V_interval[2] = {MIN_VOLT, MAX_VOLT}; -static int battery_voltage_nominal = 12, +static long battery_voltage_nominal = 12, input_voltage_nominal = 120, input_voltage_scaled = 120, /* input_voltage_maximum = -1, input_voltage_minimum = -1, */ switchable_load_banks = 0, - unit_id = -1; /*!< range: 1-65535, most likely */ + unit_id = -1; /*!< range: 1-65535, most likely */ /*! Time in seconds to delay before shutting down. */ static unsigned int offdelay = DEFAULT_OFFDELAY; @@ -461,16 +345,17 @@ static int reconnect_ups(void) /*!@brief Convert a string to printable characters (in-place) * * @param[in,out] str String to convert - * @param[in] len Maximum number of characters to convert, or <= 0 to + * @param[in] len Maximum number of characters to convert, or == 0 to * convert all. * * Uses toprint() macro defined above. */ -void toprint_str(char *str, int len) +static void toprint_str(char *str, size_t len) { - int i; - if(len <= 0) len = strlen(str); - for(i=0; i < len; i++) + size_t i; + if (len == 0) len = strlen(str); + /* FIXME? Should we check for '\0' along the way? */ + for (i = 0; i < len; i++) str[i] = toprint(str[i]); } @@ -484,7 +369,7 @@ void toprint_str(char *str, int len) * * @return See strtol(3) */ -static int hex2d(const unsigned char *start, unsigned int len) +static long hex2d(const unsigned char *start, unsigned int len) { unsigned char buf[32]; buf[31] = '\0'; @@ -494,6 +379,36 @@ static int hex2d(const unsigned char *start, unsigned int len) return strtol((char *)buf, NULL, 16); } +/*!@brief Convert N characters from big-endian binary to decimal + * + * @param start Beginning of string to convert + * @param len Maximum number of characters to consider (max 32) + * + * @a len characters of @a start are shifted into an accumulator. + * + * We assume len < sizeof(int), and that value > 0. + * + * @return the value + */ +static unsigned int bin2d(const unsigned char *start, unsigned int len) +{ + unsigned int value = 0, index = 0; + for(index = 0; index < len; index++) { + value <<= 8; + value |= start[index]; + } + + return value; +} + +static long hex_or_bin2d(const unsigned char *start, unsigned int len) +{ + if(is_binary_protocol()) { + return bin2d(start, len); + } + return hex2d(start, len); +} + /*!@brief Dump message in both hex and ASCII * * @param[in] msg Buffer to dump @@ -504,33 +419,39 @@ static int hex2d(const unsigned char *start, unsigned int len) static const char *hexascdump(unsigned char *msg, size_t len) { size_t i; - static unsigned char buf[256], *bufp; + static unsigned char buf[256]; + unsigned char *bufp, *end; bufp = buf; + end = bufp + sizeof(buf); buf[0] = 0; /* Dump each byte in hex: */ - for(i=0; i=3; i++) { bufp += sprintf((char *)bufp, "%02x ", msg[i]); } /* Dump single-quoted string with printable version of each byte: */ - *bufp++ = '\''; - for(i=0; i 0) *bufp++ = '\''; - *bufp++ = '\0'; + for(i=0; i0; i++) { + *bufp++ = (unsigned char)toprint(msg[i]); + } + if (end-bufp > 0) *bufp++ = '\''; + + if (end-bufp > 0) + *bufp = '\0'; + else + *--end='\0'; return (char *)buf; } -enum tl_model_t decode_protocol(unsigned int proto) +static enum tl_model_t decode_protocol(unsigned int proto) { switch(proto) { case 0x0004: - upslogx(3, "Using older SMART protocol (%x)", proto); + upslogx(3, "Using older SMART protocol (%04x)", proto); return TRIPP_LITE_SMART_0004; case 0x1001: upslogx(3, "Using OMNIVS protocol (%x)", proto); @@ -541,32 +462,44 @@ enum tl_model_t decode_protocol(unsigned int proto) case 0x3003: upslogx(3, "Using SMARTPRO protocol (%x)", proto); return TRIPP_LITE_SMARTPRO; + case 0x3005: + upslogx(3, "Using binary SMART protocol (%x)", proto); + return TRIPP_LITE_SMART_3005; default: - printf("Unknown protocol (%x)", proto); + printf("Unknown protocol (%04x)", proto); break; } return TRIPP_LITE_UNKNOWN; } -void decode_v(const unsigned char *value) +static void decode_v(const unsigned char *value) { unsigned char ivn, lb; - int bv = hex2d(value+2, 2); + long bv; + + if(is_binary_protocol()) { + /* 0x00 0x0c -> 12V ? */ + battery_voltage_nominal = (value[2] << 8) | value[3]; + } else { + bv = hex2d(value+2, 2); + battery_voltage_nominal = bv * 6; + } ivn = value[1]; lb = value[4]; switch(ivn) { - case '0': input_voltage_nominal = + case '0': input_voltage_nominal = input_voltage_scaled = 100; break; - case '1': input_voltage_nominal = + case 2: /* protocol 3005 */ + case '1': input_voltage_nominal = input_voltage_scaled = 120; break; - case '2': input_voltage_nominal = + case '2': input_voltage_nominal = input_voltage_scaled = 230; break; @@ -574,42 +507,54 @@ void decode_v(const unsigned char *value) input_voltage_scaled = 230; break; + case 6: input_voltage_nominal = + input_voltage_scaled = 230; + break; + default: upslogx(2, "Unknown input voltage range: 0x%02x", (unsigned int)ivn); break; } - battery_voltage_nominal = bv * 6; - if( (lb >= '0') && (lb <= '9') ) { switchable_load_banks = lb - '0'; } else { - if( lb != 'X' ) { - upslogx(2, "Unknown number of switchable load banks: 0x%02x", + if(is_binary_protocol()) { + switchable_load_banks = lb; + } else { + if( lb != 'X' ) { + upslogx(2, "Unknown number of switchable load banks: 0x%02x", (unsigned int)lb); + } } } + upsdebugx(2, "Switchable load banks: %ld", switchable_load_banks); } void upsdrv_initinfo(void); /*!@brief Report a USB comm failure, and reconnect if necessary - * + * * @param[in] res Result code from libusb/libhid call * @param[in] msg Error message to display */ -void usb_comm_fail(int res, const char *msg) +static void usb_comm_fail(int res, const char *msg) { static int try = 0; switch(res) { - case -EBUSY: - upslogx(LOG_WARNING, "%s: Device claimed by another process", msg); + case ERROR_BUSY: + upslogx(LOG_WARNING, + "%s: Device claimed by another process", msg); fatalx(EXIT_FAILURE, "Terminating: EBUSY"); +#ifndef HAVE___ATTRIBUTE__NORETURN break; +#endif default: - upslogx(LOG_WARNING, "%s: Device detached? (error %d: %s)", msg, res, usb_strerror()); + upslogx(LOG_WARNING, + "%s: Device detached? (error %d: %s)", + msg, res, nut_usb_strerror(res)); upslogx(LOG_NOTICE, "Reconnect attempt #%d", ++try); hd = NULL; @@ -644,6 +589,7 @@ void usb_comm_fail(int res, const char *msg) */ static int send_cmd(const unsigned char *msg, size_t msg_len, unsigned char *reply, size_t reply_len) { + NUT_UNUSED_VARIABLE(reply_len); unsigned char buffer_out[8]; unsigned char csum = 0; int ret = 0, send_try, recv_try=0, done = 0; @@ -671,21 +617,26 @@ static int send_cmd(const unsigned char *msg, size_t msg_len, unsigned char *rep for(send_try=0; !done && send_try < MAX_SEND_TRIES; send_try++) { upsdebugx(6, "send_cmd send_try %d", send_try+1); - ret = comm_driver->set_report(udev, 0, buffer_out, sizeof(buffer_out)); + ret = comm_driver->set_report(udev, 0, + (usb_ctrl_charbuf)buffer_out, + (usb_ctrl_charbufsize)sizeof(buffer_out)); if(ret != sizeof(buffer_out)) { - upslogx(1, "libusb_set_report() returned %d instead of %u", - ret, (unsigned)(sizeof(buffer_out))); + upslogx(1, "libusb_set_report() returned %d instead of %zu", + ret, sizeof(buffer_out)); return ret; } #if ! defined(__FreeBSD__) - if(!done) { usleep(1000*100); /* TODO: nanosleep */ } + usleep(1000*100); /* TODO: nanosleep */ #endif for(recv_try=0; !done && recv_try < MAX_RECV_TRIES; recv_try++) { upsdebugx(7, "send_cmd recv_try %d", recv_try+1); - ret = comm_driver->get_interrupt(udev, reply, sizeof(buffer_out), RECV_WAIT_MSEC); + ret = comm_driver->get_interrupt(udev, + (usb_ctrl_charbuf)reply, + (usb_ctrl_charbufsize)sizeof(buffer_out), + RECV_WAIT_MSEC); if(ret != sizeof(buffer_out)) { upslogx(1, "libusb_get_interrupt() returned %d instead of %u while sending %s", ret, (unsigned)(sizeof(buffer_out)), @@ -699,8 +650,8 @@ static int send_cmd(const unsigned char *msg, size_t msg_len, unsigned char *rep upsdebugx(5, "send_cmd: received %s (%s)", hexascdump(reply, sizeof(buffer_out)), done ? "OK" : "bad"); } - - upsdebugx(((send_try > 2) || (recv_try > 2)) ? 3 : 6, + + upsdebugx(((send_try > 2) || (recv_try > 2)) ? 3 : 6, "send_cmd: send_try = %d, recv_try = %d\n", send_try, recv_try); return done ? sizeof(buffer_out) : 0; @@ -714,7 +665,7 @@ static int send_cmd(const unsigned char *msg, size_t msg_len, unsigned char *rep * The variables are of the form "ups.debug.X" where "X" is the command * character. */ -void debug_message(const char *msg, int len) +static void debug_message(const char *msg, size_t len) { int ret; unsigned char tmp_value[9]; @@ -736,7 +687,7 @@ void debug_message(const char *msg, int len) static void do_reboot_wait(unsigned dly) { int ret; - char buf[256], cmd_W[]="Wx"; + char buf[256], cmd_W[]="Wx"; cmd_W[1] = dly; upsdebugx(3, "do_reboot_wait(wait=%d): N", dly); @@ -762,8 +713,10 @@ static int soft_shutdown(void) int ret; unsigned char buf[256], cmd_N[]="N\0x", cmd_G[] = "G"; - cmd_N[2] = offdelay; - cmd_N[1] = offdelay >> 8; + /* Already binary: */ + /* FIXME: Assumes memory layout / endianness? */ + cmd_N[2] = (unsigned char)(offdelay & 0x00FF); + cmd_N[1] = (unsigned char)(offdelay >> 8); upsdebugx(3, "soft_shutdown(offdelay=%d): N", offdelay); ret = send_cmd(cmd_N, sizeof(cmd_N), buf, sizeof(buf)); @@ -774,8 +727,8 @@ static int soft_shutdown(void) } sleep(2); - - /*! The unit must be on battery for this to work. + + /*! The unit must be on battery for this to work. * * @todo check for on-battery condition, and print error if not. * @todo Find an equivalent command for non-OMNIVS models. @@ -794,17 +747,18 @@ static int soft_shutdown(void) static int hard_shutdown(void) { int ret; - char buf[256], cmd_N[]="N\0x", cmd_K[] = "K\0"; + unsigned char buf[256], cmd_N[]="N\0x", cmd_K[] = "K\0"; - cmd_N[2] = offdelay; - cmd_N[1] = offdelay >> 8; + /* FIXME: Assumes memory layout / endianness? */ + cmd_N[2] = (unsigned char)(offdelay & 0x00FF); + cmd_N[1] = (unsigned char)(offdelay >> 8); upsdebugx(3, "hard_shutdown(offdelay=%d): N", offdelay); ret = send_cmd(cmd_N, sizeof(cmd_N), buf, sizeof(buf)); if(ret != 8) return ret; sleep(2); - + ret = send_cmd(cmd_K, sizeof(cmd_K), buf, sizeof(buf)); return (ret == 8); } @@ -833,8 +787,62 @@ static int control_outlet(int outlet_id, int state) } else { return 1; } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_BREAK +#pragma GCC diagnostic ignored "-Wunreachable-code-break" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif + break; +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic pop +#endif + + case TRIPP_LITE_SMART_3005: + snprintf(k_cmd, sizeof(k_cmd)-1, "N%c", 5); + ret = send_cmd((unsigned char *)k_cmd, strlen(k_cmd) + 1, (unsigned char *)buf, sizeof buf); + snprintf(k_cmd, sizeof(k_cmd)-1, "K%c%c", outlet_id, state & 1); + ret = send_cmd((unsigned char *)k_cmd, strlen(k_cmd) + 1, (unsigned char *)buf, sizeof buf); + + if(ret != 8) { + upslogx(LOG_ERR, "Could not set outlet %d to state %d, ret = %d", outlet_id, state, ret); + return 0; + } else { + return 1; + } +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_BREAK +#pragma GCC diagnostic ignored "-Wunreachable-code-break" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif + break; +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic pop +#endif + + case TRIPP_LITE_OMNIVS: + case TRIPP_LITE_OMNIVS_2001: + case TRIPP_LITE_UNKNOWN: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: - upslogx(LOG_ERR, "control_outlet unimplemented for this UPS model"); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif + upslogx(LOG_ERR, "control_outlet unimplemented for protocol %04x", tl_model); } return 0; } @@ -845,7 +853,7 @@ static int instcmd(const char *cmdname, const char *extra) { unsigned char buf[10]; - if(tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_SMART_0004) { + if(is_smart_protocol()) { if (!strcasecmp(cmdname, "test.battery.start")) { send_cmd((const unsigned char *)"A", 2, buf, sizeof buf); return STAT_INSTCMD_HANDLED; @@ -882,26 +890,33 @@ static int instcmd(const char *cmdname, const char *extra) return STAT_INSTCMD_HANDLED; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } static int setvar(const char *varname, const char *val) { if (!strcasecmp(varname, "ups.delay.shutdown")) { - offdelay = atoi(val); - dstate_setinfo("ups.delay.shutdown", "%d", offdelay); - return STAT_SET_HANDLED; + int ival = atoi(val); + if (ival >= 0) { + offdelay = (unsigned int)ival; + dstate_setinfo("ups.delay.shutdown", "%d", offdelay); + return STAT_SET_HANDLED; + } else { + upslogx(LOG_NOTICE, "FAILED to set '%s' to %d", varname, ival); + return STAT_SET_UNKNOWN; + } } if (unit_id >= 0 && !strcasecmp(varname, "ups.id")) { - int new_unit_id, ret; + int new_unit_id, ret; unsigned char J_msg[] = "J__", buf[9]; new_unit_id = atoi(val); - J_msg[1] = new_unit_id >> 8; - J_msg[2] = new_unit_id & 0xff; - ret = send_cmd(J_msg, sizeof(J_msg), buf, sizeof(buf)); + /* FIXME: Assumes memory layout / endianness? */ + J_msg[1] = (unsigned char)(new_unit_id >> 8); + J_msg[2] = (unsigned char)(new_unit_id & 0xff); + ret = send_cmd(J_msg, sizeof(J_msg), buf, sizeof(buf)); if(ret <= 0) { upslogx(LOG_NOTICE, "Could not set Unit ID (return code: %d).", ret); @@ -915,16 +930,21 @@ static int setvar(const char *varname, const char *val) if(!strncmp(varname, "outlet.", strlen("outlet."))) { char outlet_name[80]; char index_str[10], *first_dot, *next_dot; - int index_chars, index, state, ret; + long index_chars; + int index, state, ret; first_dot = strstr(varname, "."); next_dot = strstr(first_dot + 1, "."); + if (!next_dot) { + upslogx(LOG_NOTICE, "FAILED to get outlet index from '%s' (no second dot)", varname); + return STAT_SET_UNKNOWN; + } index_chars = next_dot - (first_dot + 1); - if(index_chars > 9) return STAT_SET_UNKNOWN; + if(index_chars > 9 || index_chars < 0) return STAT_SET_UNKNOWN; if(strcmp(next_dot, ".switch")) return STAT_SET_UNKNOWN; - strncpy(index_str, first_dot + 1, index_chars); + strncpy(index_str, first_dot + 1, (size_t)index_chars); index_str[index_chars] = 0; index = atoi(index_str); @@ -971,7 +991,8 @@ void upsdrv_initinfo(void) char *model, *model_end; unsigned char proto_value[9], f_value[9], p_value[9], s_value[9], u_value[9], v_value[9], w_value[9]; - int va, ret; + long va; + ssize_t ret; unsigned int proto_number = 0; /* Read protocol: */ @@ -980,8 +1001,8 @@ void upsdrv_initinfo(void) fatalx(EXIT_FAILURE, "Error reading protocol"); } - proto_number = ((unsigned)(proto_value[1]) << 8) - | (unsigned)(proto_value[2]); + proto_number = ((unsigned)(proto_value[1]) << 8) + | (unsigned)(proto_value[2]); tl_model = decode_protocol(proto_number); if(tl_model == TRIPP_LITE_UNKNOWN) @@ -996,7 +1017,7 @@ void upsdrv_initinfo(void) if(tl_model != TRIPP_LITE_SMARTPRO ) { ret = send_cmd(w_msg, sizeof(w_msg), w_value, sizeof(w_value)-1); if(ret <= 0) { - if(ret == -EPIPE) { + if(ret == ERROR_PIPE) { fatalx(EXIT_FAILURE, "Could not reset watchdog. Please check and" "see if usbhid-ups(8) works with this UPS."); } else { @@ -1046,11 +1067,11 @@ void upsdrv_initinfo(void) dstate_setinfo("ups.model", "%s", model); - dstate_setinfo("ups.power.nominal", "%d", va); + dstate_setinfo("ups.power.nominal", "%ld", va); /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - /* Fetch firmware version: */ + /* Fetch firmware version: */ ret = send_cmd(f_msg, sizeof(f_msg), f_value, sizeof(f_value)-1); toprint_str((char *)(f_value+1), 6); @@ -1109,8 +1130,8 @@ void upsdrv_initinfo(void) if(ret <= 0) { upslogx(LOG_INFO, "Unit ID not retrieved (not available on all models)"); } else { - unit_id = (int)((unsigned)(u_value[1]) << 8) - | (unsigned)(u_value[2]); + unit_id = (long)((unsigned)(u_value[1]) << 8) + | (unsigned)(u_value[2]); } if(tl_model == TRIPP_LITE_SMART_0004) { @@ -1119,33 +1140,33 @@ void upsdrv_initinfo(void) } if(unit_id >= 0) { - dstate_setinfo("ups.id", "%d", unit_id); + dstate_setinfo("ups.id", "%ld", unit_id); dstate_setflags("ups.id", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.id", 5); - upslogx(LOG_DEBUG,"Unit ID: %d", unit_id); + upslogx(LOG_DEBUG,"Unit ID: %ld", unit_id); } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - dstate_setinfo("input.voltage.nominal", "%d", input_voltage_nominal); - dstate_setinfo("battery.voltage.nominal", "%d", battery_voltage_nominal); - dstate_setinfo("ups.debug.load_banks", "%d", switchable_load_banks); + dstate_setinfo("input.voltage.nominal", "%ld", input_voltage_nominal); + dstate_setinfo("battery.voltage.nominal", "%ld", battery_voltage_nominal); + dstate_setinfo("ups.debug.load_banks", "%ld", switchable_load_banks); - dstate_setinfo("ups.delay.shutdown", "%d", offdelay); + dstate_setinfo("ups.delay.shutdown", "%u", offdelay); dstate_setflags("ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.delay.shutdown", 3); #if 0 - dstate_setinfo("ups.delay.start", "%d", startdelay); + dstate_setinfo("ups.delay.start", "%u", startdelay); dstate_setflags("ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.delay.start", 8); - dstate_setinfo("ups.delay.reboot", "%d", bootdelay); + dstate_setinfo("ups.delay.reboot", "%u", bootdelay); dstate_setflags("ups.delay.reboot", ST_FLAG_RW | ST_FLAG_STRING); dstate_setaux("ups.delay.reboot", 3); #endif - if(tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_SMART_0004) { + if(is_smart_protocol()) { dstate_addcmd("test.battery.start"); dstate_addcmd("reset.input.minmax"); } @@ -1182,11 +1203,14 @@ void upsdrv_updateinfo(void) s_msg[] = "S", m_msg[] = "M", t_msg[] = "T"; unsigned char b_value[9], d_value[9], l_value[9], s_value[9], m_value[9], t_value[9]; - int bp, freq; - double bv; + int bp; + long freq; + double bv_12V = 0.0; /*!< battery voltage, relative to a 12V battery */ + double battery_voltage; /*!< the total battery voltage */ + + unsigned int s_value_1; int ret; - unsigned battery_charge; status_init(); @@ -1236,8 +1260,14 @@ void upsdrv_updateinfo(void) /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - if(tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_OMNIVS_2001 || tl_model == TRIPP_LITE_SMART_0004) { - switch(s_value[2]) { + if(is_smart_protocol() || tl_model == TRIPP_LITE_OMNIVS_2001) { + + unsigned int s_value_2 = s_value[2]; + + if(is_binary_protocol()) { + s_value_2 += '0'; + } + switch(s_value_2) { case '0': dstate_setinfo("battery.test.status", "Battery OK"); break; @@ -1277,16 +1307,27 @@ void upsdrv_updateinfo(void) } } - /* This may not be right... */ +#if 0 + /* Apparently, this value changes more frequently when the + * battery is discharged, but it does not track the actual + * state-of-charge. See battery.charge calculation below. + */ if(tl_model == TRIPP_LITE_SMARTPRO) { + unsigned battery_charge; battery_charge = (unsigned)(s_value[5]); dstate_setinfo("battery.charge", "%u", battery_charge); } +#endif } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - switch(s_value[1]) { + s_value_1 = s_value[1]; + if(is_binary_protocol()) { + s_value_1 += '0'; + } + + switch(s_value_1) { case '0': status_set("LB"); break; @@ -1297,7 +1338,9 @@ void upsdrv_updateinfo(void) status_set("RB"); break; } /* else fall through: */ + goto fallthrough_case_default; default: + fallthrough_case_default: upslogx(LOG_ERR, "Unknown value for s[1]: 0x%02x", s_value[1]); dstate_datastale(); break; @@ -1315,28 +1358,18 @@ void upsdrv_updateinfo(void) return; } - dstate_setinfo("input.voltage", "%.2f", hex2d(b_value+1, 4)/30.0); + dstate_setinfo("input.voltage", "%.2f", hex2d(b_value+1, 4)/3600.0*input_voltage_scaled); - bv = hex2d(b_value+5, 2)/16.0; + bv_12V = hex2d(b_value+5, 2)/16.0; - /* dq ~= sqrt(dV) is a reasonable approximation - * Results fit well against the discrete function used in the Tripp Lite - * source, but give a continuous result. */ - if (bv >= V_interval[1]) - bp = 100; - else if (bv <= V_interval[0]) - bp = 10; - else - bp = (int)(100*sqrt((bv - V_interval[0]) - / (V_interval[1] - V_interval[0]))); - - dstate_setinfo("battery.voltage", "%.2f", bv); - dstate_setinfo("battery.charge", "%3d", bp); + /* TODO: use battery_voltage_nominal, even though it is most likely 12V */ + dstate_setinfo("battery.voltage", "%.2f", bv_12V); } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ - if( tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_SMART_0004 ) { + if( is_smart_protocol() ) { + ret = send_cmd(d_msg, sizeof(d_msg), d_value, sizeof(d_value)); if(ret <= 0) { dstate_datastale(); @@ -1344,18 +1377,20 @@ void upsdrv_updateinfo(void) return; } - dstate_setinfo("input.voltage", "%d", - hex2d(d_value+1, 2) * input_voltage_scaled / 120); + dstate_setinfo("input.voltage", "%ld", + hex_or_bin2d(d_value+1, 2) * input_voltage_scaled / 120); - bv = hex2d(d_value+3, 2) * battery_voltage_nominal / 120.0 ; + /* TODO: factor out the two constants */ + bv_12V = hex_or_bin2d(d_value+3, 2) / 10.0 ; + battery_voltage = bv_12V * battery_voltage_nominal / 12.0; - dstate_setinfo("battery.voltage", "%.2f", bv); + dstate_setinfo("battery.voltage", "%.2f", battery_voltage); /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ ret = send_cmd(m_msg, sizeof(m_msg), m_value, sizeof(m_value)); - if(m_value[5] != 0x0d) { /* we only expect 4 hex digits */ + if(m_value[5] != 0x0d) { /* we only expect 4 hex/binary digits */ dstate_setinfo("ups.debug.M", "%s", hexascdump(m_value+1, 7)); } @@ -1365,8 +1400,10 @@ void upsdrv_updateinfo(void) return; } - dstate_setinfo("input.voltage.minimum", "%3d", hex2d(m_value+1, 2)); - dstate_setinfo("input.voltage.maximum", "%3d", hex2d(m_value+3, 2)); + dstate_setinfo("input.voltage.minimum", "%3ld", + hex_or_bin2d(m_value+1, 2) * input_voltage_scaled / 120); + dstate_setinfo("input.voltage.maximum", "%3ld", + hex_or_bin2d(m_value+3, 2) * input_voltage_scaled / 120); /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ @@ -1390,15 +1427,39 @@ void upsdrv_updateinfo(void) dstate_setinfo("input.frequency.nominal", "%d", 50); break; } - } + } if( tl_model == TRIPP_LITE_SMART_0004 ) { freq = hex2d(t_value + 3, 4); dstate_setinfo("input.frequency", "%.1f", freq / 10.0); } - /* I'm guessing this is a calibration constant of some sort. */ - dstate_setinfo("ups.temperature", "%.1f", (unsigned)(hex2d(t_value+1, 2)) * 0.3636 - 21); + if( tl_model == TRIPP_LITE_SMART_3005 ) { + dstate_setinfo("ups.temperature", "%d", + (unsigned)(hex2d(t_value+1, 1))); + } else { + /* I'm guessing this is a calibration constant of some sort. */ + dstate_setinfo("ups.temperature", "%.1f", + (unsigned)(hex2d(t_value+1, 2)) * 0.3636 - 21); + } + } + + /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ + + if( tl_model == TRIPP_LITE_OMNIVS || tl_model == TRIPP_LITE_OMNIVS_2001 || + tl_model == TRIPP_LITE_SMARTPRO || tl_model == TRIPP_LITE_SMART_0004 || tl_model == TRIPP_LITE_SMART_3005) { + /* dq ~= sqrt(dV) is a reasonable approximation + * Results fit well against the discrete function used in the Tripp Lite + * source, but give a continuous result. */ + if (bv_12V >= V_interval[1]) + bp = 100; + else if (bv_12V <= V_interval[0]) + bp = 10; + else + bp = (int)(100*sqrt((bv_12V - V_interval[0]) + / (V_interval[1] - V_interval[0]))); + + dstate_setinfo("battery.charge", "%3d", bp); } /* - * - * - * - * - * - * - * - * - * - * - * - * - * - * - */ @@ -1413,16 +1474,32 @@ void upsdrv_updateinfo(void) switch(tl_model) { case TRIPP_LITE_OMNIVS: case TRIPP_LITE_OMNIVS_2001: - dstate_setinfo("output.voltage", "%.1f", hex2d(l_value+1, 4)/2.0); + dstate_setinfo("output.voltage", "%.1f", + hex2d(l_value+1, 4)/240.0*input_voltage_scaled); break; case TRIPP_LITE_SMARTPRO: - dstate_setinfo("ups.load", "%d", hex2d(l_value+1, 2)); + dstate_setinfo("ups.load", "%ld", hex2d(l_value+1, 2)); + break; + case TRIPP_LITE_SMART_3005: + dstate_setinfo("ups.load", "%ld", hex_or_bin2d(l_value+1, 1)); break; case TRIPP_LITE_SMART_0004: - dstate_setinfo("ups.load", "%d", hex2d(l_value+1, 2)); + dstate_setinfo("ups.load", "%ld", hex2d(l_value+1, 2)); dstate_setinfo("ups.debug.L","%s", hexascdump(l_value+1, 7)); break; + case TRIPP_LITE_UNKNOWN: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) +# pragma GCC diagnostic pop +#endif dstate_setinfo("ups.debug.L","%s", hexascdump(l_value+1, 7)); break; } @@ -1453,16 +1530,20 @@ void upsdrv_makevartable(void) { char msg[256]; - snprintf(msg, sizeof msg, "Set shutdown delay, in seconds (default=%d).", + snprintf(msg, sizeof msg, "Set shutdown delay, in seconds (default=%d)", DEFAULT_OFFDELAY); addvar(VAR_VALUE, "offdelay", msg); - /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ - addvar(VAR_VALUE, "vendor", "Regular expression to match UPS Manufacturer string"); - addvar(VAR_VALUE, "product", "Regular expression to match UPS Product string"); - addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial number"); - addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)"); - addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name"); + /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ + nut_usb_addvars(); + + snprintf(msg, sizeof msg, "Minimum battery voltage, corresponding to 10%% charge (default=%.1f)", + MIN_VOLT); + addvar(VAR_VALUE, "battery_min", msg); + + snprintf(msg, sizeof msg, "Maximum battery voltage, corresponding to 100%% charge (default=%.1f)", + MAX_VOLT); + addvar(VAR_VALUE, "battery_max", msg); #if 0 snprintf(msg, sizeof msg, "Set start delay, in seconds (default=%d).", @@ -1481,9 +1562,12 @@ void upsdrv_makevartable(void) */ void upsdrv_initups(void) { - char *regex_array[6]; + char *regex_array[7]; + char *value; int r; + warn_if_bad_usb_port_filename(device_path); + /* process the UPS selection options */ regex_array[0] = NULL; /* handled by USB IDs device table */ regex_array[1] = getval("productid"); @@ -1491,6 +1575,7 @@ void upsdrv_initups(void) regex_array[3] = getval("product"); /* product string */ regex_array[4] = getval("serial"); /* probably won't see this */ regex_array[5] = getval("bus"); + regex_array[6] = getval("device"); r = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); if (r==-1) { @@ -1503,14 +1588,14 @@ void upsdrv_initups(void) regex_matcher->next = &subdriver_matcher; /* Search for the first supported UPS matching the regular - * expression */ + * expression */ r = comm_driver->open(&udev, &curDevice, regex_matcher, NULL); if (r < 1) { fatalx(EXIT_FAILURE, "No matching USB/HID UPS found"); } hd = &curDevice; - + upslogx(1, "Detected a UPS: %s/%s", hd->Vendor ? hd->Vendor : "unknown", hd->Product ? hd->Product : "unknown"); dstate_setinfo("ups.vendorid", "%04x", hd->VendorID); @@ -1524,8 +1609,29 @@ void upsdrv_initups(void) /* link the two matchers */ reopen_matcher->next = regex_matcher; - if (getval("offdelay")) - offdelay = atoi(getval("offdelay")); + value = getval("offdelay"); + if (value) { + int ival = atoi(value); + if (ival >= 0) { + offdelay = (unsigned int)ival; + upsdebugx(2, "Setting 'offdelay' to %u", offdelay); + } else { + upsdebugx(2, "FAILED to set 'offdelay' to %d", ival); + } + } + + value = getval("battery_min"); + if (value) { + V_interval[0] = atof(value); + upsdebugx(2, "Setting 'battery_min' to %.g", V_interval[0]); + } + + value = getval("battery_max"); + if (value) { + V_interval[1] = atof(value); + upsdebugx(2, "Setting 'battery_max' to %.g", V_interval[1]); + } + #if 0 if (getval("startdelay")) startdelay = atoi(getval("startdelay")); @@ -1539,4 +1645,9 @@ void upsdrv_cleanup(void) comm_driver->close(udev); USBFreeExactMatcher(reopen_matcher); USBFreeRegexMatcher(regex_matcher); + free(curDevice.Vendor); + free(curDevice.Product); + free(curDevice.Serial); + free(curDevice.Bus); + free(curDevice.Device); } diff --git a/drivers/tripplitesu.c b/drivers/tripplitesu.c index 9c9049b..05e8972 100644 --- a/drivers/tripplitesu.c +++ b/drivers/tripplitesu.c @@ -75,7 +75,6 @@ battery.temperature battery.voltage battery.voltage.nominal - driver.version.internal input.frequency input.sensitivity (RW) (1) input.transfer.high (RW) @@ -126,7 +125,7 @@ #include "serial.h" #define DRIVER_NAME "Tripp Lite SmartOnline driver" -#define DRIVER_VERSION "0.03" +#define DRIVER_VERSION "0.06" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -216,10 +215,11 @@ static struct { #define WATCHDOG "WDG" /* poll/set */ -static int do_command(char type, const char *command, const char *parameters, char *response) +static ssize_t do_command(char type, const char *command, const char *parameters, char *response) { char buffer[SMALLBUF]; - int count, ret; + size_t count; + ssize_t ret; ser_flush_io(upsfd); @@ -235,7 +235,7 @@ static int do_command(char type, const char *command, const char *parameters, ch return -1; } - upsdebugx(3, "do_command: %d bytes sent [%s] -> OK", ret, buffer); + upsdebugx(3, "do_command: %zd bytes sent [%s] -> OK", ret, buffer); ret = ser_get_buf_len(upsfd, (unsigned char *)buffer, 4, 3, 0); if (ret < 0) { @@ -248,7 +248,7 @@ static int do_command(char type, const char *command, const char *parameters, ch } buffer[ret] = '\0'; - upsdebugx(3, "do_command: %d byted read [%s]", ret, buffer); + upsdebugx(3, "do_command: %zd byted read [%s]", ret, buffer); if (!strcmp(buffer, "~00D")) { @@ -263,9 +263,15 @@ static int do_command(char type, const char *command, const char *parameters, ch } buffer[ret] = '\0'; - upsdebugx(3, "do_command: %d bytes read [%s]", ret, buffer); + upsdebugx(3, "do_command: %zd bytes read [%s]", ret, buffer); - count = atoi(buffer); + int c = atoi(buffer); + if (c < 0) { + upsdebugx(3, "do_command: response not expected to be a negative count!"); + return -1; + } + + count = (size_t)c; if (count >= MAX_RESPONSE_LENGTH) { upsdebugx(3, "do_command: response exceeds expected size!"); return -1; @@ -291,13 +297,13 @@ static int do_command(char type, const char *command, const char *parameters, ch } response[ret] = '\0'; - upsdebugx(3, "do_command: %d bytes read [%s]", ret, response); + upsdebugx(3, "do_command: %zd bytes read [%s]", ret, response); /* Tripp Lite pads their string responses with spaces. I don't like that, so I remove them. This is safe to do with all responses for this protocol, so I just do that here. */ - rtrim(response, ' '); + str_rtrim(response, ' '); return ret; } @@ -433,7 +439,7 @@ static void set_sensitivity(const char *val) { for (i = 0; i < sizeof(sensitivity) / sizeof(sensitivity[0]); i++) { if (!strcasecmp(val, sensitivity[i].name)) { - snprintf(parm, sizeof(parm), "%d", i); + snprintf(parm, sizeof(parm), "%u", i); do_command(SET, VOLTAGE_SENSITIVITY, parm, NULL); break; } @@ -515,7 +521,7 @@ static int instcmd(const char *cmdname, const char *extra) do_command(SET, TEST, "0", NULL); return STAT_INSTCMD_HANDLED; } - upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname); + upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra); return STAT_INSTCMD_UNKNOWN; } @@ -549,10 +555,15 @@ static int setvar(const char *varname, const char *val) static int init_comm(void) { - int i, bit; + size_t i, bit; char response[MAX_RESPONSE_LENGTH]; ups.commands_available = 0; + /* Repeat enumerate command 2x, firmware bug on some units garbles 1st response */ + if (do_command(POLL, AVAILABLE, "", response) <= 0){ + upslogx(LOG_NOTICE, "init_comm: Initial response malformed, retrying in 300ms"); + usleep(3E5); + } if (do_command(POLL, AVAILABLE, "", response) <= 0) return 0; i = strlen(response); @@ -604,17 +615,29 @@ void upsdrv_initinfo(void) dstate_setinfo("battery.voltage.nominal", "%d", atoi(ptr)); ptr = field(response, 10); - if (ptr) - min_low_transfer = atoi(ptr); + if (ptr) { + int ipv = atoi(ptr); + if (ipv >= 0) + min_low_transfer = (unsigned int)ipv; + } ptr = field(response, 9); - if (ptr) - max_low_transfer = atoi(ptr); + if (ptr) { + int ipv = atoi(ptr); + if (ipv >= 0) + max_low_transfer = (unsigned int)ipv; + } ptr = field(response, 12); - if (ptr) - min_high_transfer = atoi(ptr); + if (ptr) { + int ipv = atoi(ptr); + if (ipv >= 0) + min_high_transfer = (unsigned int)ipv; + } ptr = field(response, 11); - if (ptr) - max_high_transfer = atoi(ptr); + if (ptr) { + int ipv = atoi(ptr); + if (ipv >= 0) + max_high_transfer = (unsigned int)ipv; + } } if (do_command(POLL, OUTLET_RELAYS, "", response) > 0) ups.outlet_banks = atoi(response); @@ -719,15 +742,15 @@ void upsdrv_updateinfo(void) ptr = field(response, 3); if (ptr) dstate_setinfo("output.voltage", "%03.1f", - (double) atoi(ptr) / 10.0); + (double) (atoi(ptr)) / 10.0); ptr = field(response, 1); if (ptr) dstate_setinfo("output.frequency", "%03.1f", - (double) atoi(ptr) / 10.0); + (double) (atoi(ptr)) / 10.0); ptr = field(response, 4); if (ptr) dstate_setinfo("output.current", "%03.1f", - (double) atoi(ptr) / 10.0); + (double) (atoi(ptr)) / 10.0); low_battery = 0; if (do_command(POLL, STATUS_BATTERY, "", response) <= 0) { @@ -754,11 +777,11 @@ void upsdrv_updateinfo(void) ptr = field(response, 6); if (ptr) dstate_setinfo("battery.voltage", "%03.1f", - (double) atoi(ptr) / 10.0); + (double) (atoi(ptr)) / 10.0); ptr = field(response, 7); if (ptr) dstate_setinfo("battery.current", "%03.1f", - (double) atoi(ptr) / 10.0); + (double) (atoi(ptr)) / 10.0); if (low_battery) status_set("LB"); @@ -774,11 +797,11 @@ void upsdrv_updateinfo(void) ptr = field(response, 2); if (ptr) dstate_setinfo("input.voltage", "%03.1f", - (double) atoi(ptr) / 10.0); + (double) (atoi(ptr)) / 10.0); ptr = field(response, 1); if (ptr) - dstate_setinfo("input.frequency", - "%03.1f", (double) atoi(ptr) / 10.0); + dstate_setinfo("input.frequency", "%03.1f", + (double) (atoi(ptr)) / 10.0); } if (do_command(POLL, TEST_RESULT, "", response) > 0) { @@ -786,7 +809,7 @@ void upsdrv_updateinfo(void) size_t trsize; r = atoi(response); - trsize = sizeof(test_result_names) / + trsize = sizeof(test_result_names) / sizeof(test_result_names[0]); if ((r < 0) || (r >= (int) trsize)) diff --git a/drivers/upscode2.c b/drivers/upscode2.c index 5f2e0b4..50a4764 100644 --- a/drivers/upscode2.c +++ b/drivers/upscode2.c @@ -20,7 +20,6 @@ along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - $Id: upscode2.c 2391 2010-03-04 15:35:09Z adkorte-guest $ */ /* @@ -41,11 +40,10 @@ #include "serial.h" #include "timehead.h" #include "nut_stdint.h" - -#include +#include "nut_float.h" #define DRIVER_NAME "UPScode II UPS driver" -#define DRIVER_VERSION "0.87" +#define DRIVER_VERSION "0.90" /* driver description structure */ upsdrv_info_t upsdrv_info = { @@ -84,7 +82,7 @@ upsdrv_info_t upsdrv_info = { typedef enum { t_ignore, /* Ignore this response */ t_value, /* Sets a NUT variable */ - t_final, /* Marks the end of UPS data for this command */ + t_final, /* Marks the end of UPS data for this command */ t_string, /* Set a NUT string variable */ t_finstr, /* Set a NUT string variable, and marks end of data */ t_setval, /* Sets a variable in the driver and possibly in NUT */ @@ -101,7 +99,7 @@ typedef struct simple_s { const char *code; type_t type; const char *desc; - int status; + uint32_t status; float *aux; struct simple_s *stats; } simple_t; @@ -128,12 +126,14 @@ static char has_uppm_p[100]; static int input_timeout_sec = INP_TIMO_SEC, - output_pace_usec = OUT_PACE_USEC, full_update_timer = FULL_UPDATE_TIMER, use_crlf = 0, use_pre_lf = 0, buffer_empty = 0; +static useconds_t + output_pace_usec = OUT_PACE_USEC; + static uint32_t status = UPSC_STAT_NOTINIT; @@ -169,287 +169,287 @@ static int /* Status codes for the STAT and STMF status responses */ static simple_t att[] = { - { "00", t_ignore }, - { "AC", t_alarm, "Aux contact failure", 0 }, - { "BA", t_alarm, "Batteries disconnected", 0 }, - { "BC", t_alarm, "Backfeed contact failure", 0 }, - { "BD", t_alarm, "Abnormal battery discharge", 0 }, - { "BF", t_alarm, "Battery fuse failure", 0 }, - { "BL", t_status, "Battery low limit", UPSC_STAT_LOBATT }, - { "BO", t_alarm, "Battery over voltage", 0 }, - { "BP", t_alarm, "Bypass fuse failure", 0 }, - { "BR", t_alarm, "Abnormal battery recharge", 0 }, - { "BT", t_alarm, "Battery over temperature", 0 }, - { "BX", t_alarm, "Bypass unavailable", 0 }, - { "BY", t_alarm, "Battery failure", 0 }, - { "CE", t_alarm, "Configuration error", 0 }, - { "CM", t_alarm, "Battery converter failure", 0 }, - { "CT", t_alarm, "Cabinet over temperature", 0 }, - { "DO", t_alarm, "DC over voltage", 0 }, - { "DU", t_alarm, "DC under voltage", 0 }, - { "EP", t_alarm, "Emergency power off", 0 }, - { "FF", t_alarm, "Fan failure", 0 }, - { "FH", t_alarm, "Line frequency high", 0 }, - { "FL", t_alarm, "Line frequency low", 0 }, - { "FT", t_alarm, "Filter over temperature", 0 }, - { "GF", t_alarm, "Ground failure", 0 }, - { "HT", t_alarm, "Charger over temperature", 0 }, - { "IB", t_alarm, "Internal data bus failure", 0 }, - { "IF", t_alarm, "Inverter fuse failure", 0 }, - { "IM", t_alarm, "Inverter failure", 0 }, - { "IO", t_alarm, "Inverter over voltage", 0 }, - { "IP", t_alarm, "Internal power supply failure", 0 }, - { "IT", t_alarm, "Inverter over temperature", 0 }, - { "IU", t_alarm, "Inverter under voltage", 0 }, - { "IV", t_alarm, "Inverter off", 0 }, - { "LR", t_alarm, "Loss of redundancy", 0 }, - { "NF", t_alarm, "Neutral fault", 0 }, - { "OD", t_status, "UPS not supplying load", UPSC_STAT_OFF }, - { "OF", t_alarm, "Oscillator failure", 0 }, - { "OL", t_status, "Overload", UPSC_STAT_OVERLOAD }, - { "OR", t_alarm, "Redundancy overload", 0 }, - { "OV", t_alarm, "Abnormal output voltage", 0 }, - { "OW", t_alarm, "Output failure", 0 }, - { "PB", t_alarm, "Parallel bus failure", 0 }, - { "PE", t_alarm, "Phase rotation error", 0 }, - { "RE", t_alarm, "Rectifier off", 0 }, - { "RF", t_alarm, "Rectifier fuse failure", 0 }, - { "RM", t_alarm, "Rectifier failure", 0 }, - { "RT", t_alarm, "Rectifier over temperature", 0 }, - { "SM", t_alarm, "Static switch failure", 0 }, - { "ST", t_alarm, "Static switch over temperature", 0 }, - { "TT", t_alarm, "Trafo over temperature", 0 }, - { "UD", t_alarm, "UPS disabled", 0 }, - { "UO", t_alarm, "Utility over voltage", 0 }, - { "US", t_alarm, "Unsynchronized", 0 }, - { "UU", t_alarm, "Utility under voltage", 0 }, - { "VE", t_alarm, "internal voltage error", 0 }, - { NULL } + { "00", t_ignore, NULL, 0, NULL, NULL }, + { "AC", t_alarm, "Aux contact failure", 0, NULL, NULL }, + { "BA", t_alarm, "Batteries disconnected", 0, NULL, NULL }, + { "BC", t_alarm, "Backfeed contact failure", 0, NULL, NULL }, + { "BD", t_alarm, "Abnormal battery discharge", 0, NULL, NULL }, + { "BF", t_alarm, "Battery fuse failure", 0, NULL, NULL }, + { "BL", t_status, "Battery low limit", UPSC_STAT_LOBATT, NULL, NULL }, + { "BO", t_alarm, "Battery over voltage", 0, NULL, NULL }, + { "BP", t_alarm, "Bypass fuse failure", 0, NULL, NULL }, + { "BR", t_alarm, "Abnormal battery recharge", 0, NULL, NULL }, + { "BT", t_alarm, "Battery over temperature", 0, NULL, NULL }, + { "BX", t_alarm, "Bypass unavailable", 0, NULL, NULL }, + { "BY", t_alarm, "Battery failure", 0, NULL, NULL }, + { "CE", t_alarm, "Configuration error", 0, NULL, NULL }, + { "CM", t_alarm, "Battery converter failure", 0, NULL, NULL }, + { "CT", t_alarm, "Cabinet over temperature", 0, NULL, NULL }, + { "DO", t_alarm, "DC over voltage", 0, NULL, NULL }, + { "DU", t_alarm, "DC under voltage", 0, NULL, NULL }, + { "EP", t_alarm, "Emergency power off", 0, NULL, NULL }, + { "FF", t_alarm, "Fan failure", 0, NULL, NULL }, + { "FH", t_alarm, "Line frequency high", 0, NULL, NULL }, + { "FL", t_alarm, "Line frequency low", 0, NULL, NULL }, + { "FT", t_alarm, "Filter over temperature", 0, NULL, NULL }, + { "GF", t_alarm, "Ground failure", 0, NULL, NULL }, + { "HT", t_alarm, "Charger over temperature", 0, NULL, NULL }, + { "IB", t_alarm, "Internal data bus failure", 0, NULL, NULL }, + { "IF", t_alarm, "Inverter fuse failure", 0, NULL, NULL }, + { "IM", t_alarm, "Inverter failure", 0, NULL, NULL }, + { "IO", t_alarm, "Inverter over voltage", 0, NULL, NULL }, + { "IP", t_alarm, "Internal power supply failure", 0, NULL, NULL }, + { "IT", t_alarm, "Inverter over temperature", 0, NULL, NULL }, + { "IU", t_alarm, "Inverter under voltage", 0, NULL, NULL }, + { "IV", t_alarm, "Inverter off", 0, NULL, NULL }, + { "LR", t_alarm, "Loss of redundancy", 0, NULL, NULL }, + { "NF", t_alarm, "Neutral fault", 0, NULL, NULL }, + { "OD", t_status, "UPS not supplying load", UPSC_STAT_OFF, NULL, NULL }, + { "OF", t_alarm, "Oscillator failure", 0, NULL, NULL }, + { "OL", t_status, "Overload", UPSC_STAT_OVERLOAD, NULL, NULL }, + { "OR", t_alarm, "Redundancy overload", 0, NULL, NULL }, + { "OV", t_alarm, "Abnormal output voltage", 0, NULL, NULL }, + { "OW", t_alarm, "Output failure", 0, NULL, NULL }, + { "PB", t_alarm, "Parallel bus failure", 0, NULL, NULL }, + { "PE", t_alarm, "Phase rotation error", 0, NULL, NULL }, + { "RE", t_alarm, "Rectifier off", 0, NULL, NULL }, + { "RF", t_alarm, "Rectifier fuse failure", 0, NULL, NULL }, + { "RM", t_alarm, "Rectifier failure", 0, NULL, NULL }, + { "RT", t_alarm, "Rectifier over temperature", 0, NULL, NULL }, + { "SM", t_alarm, "Static switch failure", 0, NULL, NULL }, + { "ST", t_alarm, "Static switch over temperature", 0, NULL, NULL }, + { "TT", t_alarm, "Trafo over temperature", 0, NULL, NULL }, + { "UD", t_alarm, "UPS disabled", 0, NULL, NULL }, + { "UO", t_alarm, "Utility over voltage", 0, NULL, NULL }, + { "US", t_alarm, "Unsynchronized", 0, NULL, NULL }, + { "UU", t_alarm, "Utility under voltage", 0, NULL, NULL }, + { "VE", t_alarm, "internal voltage error", 0, NULL, NULL }, + { NULL, t_ignore, NULL, 0, NULL, NULL } }; /* Status code for the STLR response */ static simple_t stlr[] = { - { "NO", t_ignore }, - { "SD", t_status, NULL, UPSC_STAT_TRIM }, - { "SU", t_status, NULL, UPSC_STAT_BOOST }, - { "DU", t_status, NULL, UPSC_STAT_BOOST }, - { NULL } + { "NO", t_ignore, NULL, 0, NULL, NULL }, + { "SD", t_status, NULL, UPSC_STAT_TRIM, NULL, NULL }, + { "SU", t_status, NULL, UPSC_STAT_BOOST, NULL, NULL }, + { "DU", t_status, NULL, UPSC_STAT_BOOST, NULL, NULL }, + { NULL, t_ignore, NULL, 0, NULL, NULL } }; /* Status code for the STEA and STEM responses */ static simple_t env[] = { - { "HH", t_ignore, "Humidity high", 0 }, - { "HL", t_ignore, "Humidity low", 0 }, - { "TH", t_ignore, "Temperature high", 0 }, - { "TL", t_ignore, "Temperature low", 0 }, - { "01", t_ignore, "Environment alarm 1", 0 }, - { "02", t_ignore, "Environment alarm 2", 0 }, - { "03", t_ignore, "Environment alarm 3", 0 }, - { "04", t_ignore, "Environment alarm 4", 0 }, - { "05", t_ignore, "Environment alarm 5", 0 }, - { "06", t_ignore, "Environment alarm 6", 0 }, - { "07", t_ignore, "Environment alarm 7", 0 }, - { "08", t_ignore, "Environment alarm 8", 0 }, - { "09", t_ignore, "Environment alarm 9", 0 }, - { NULL } + { "HH", t_ignore, "Humidity high", 0, NULL, NULL }, + { "HL", t_ignore, "Humidity low", 0, NULL, NULL }, + { "TH", t_ignore, "Temperature high", 0, NULL, NULL }, + { "TL", t_ignore, "Temperature low", 0, NULL, NULL }, + { "01", t_ignore, "Environment alarm 1", 0, NULL, NULL }, + { "02", t_ignore, "Environment alarm 2", 0, NULL, NULL }, + { "03", t_ignore, "Environment alarm 3", 0, NULL, NULL }, + { "04", t_ignore, "Environment alarm 4", 0, NULL, NULL }, + { "05", t_ignore, "Environment alarm 5", 0, NULL, NULL }, + { "06", t_ignore, "Environment alarm 6", 0, NULL, NULL }, + { "07", t_ignore, "Environment alarm 7", 0, NULL, NULL }, + { "08", t_ignore, "Environment alarm 8", 0, NULL, NULL }, + { "09", t_ignore, "Environment alarm 9", 0, NULL, NULL }, + { NULL, t_ignore, NULL, 0, NULL, NULL } }; /* Responses for UPSS and UPDS */ static simple_t simple[] = { - { "STAT", t_list, NULL, 0, 0, att }, - { "STBO", t_status, NULL, UPSC_STAT_ONBATT }, - { "STBL", t_status, NULL, UPSC_STAT_LOBATT }, - { "STBM", t_ignore }, - { "STBP", t_status, NULL, UPSC_STAT_BYPASS }, - { "STEA", t_list, NULL, 0, 0, env }, - { "STEM", t_list, NULL, 0, 0, env }, - { "STLR", t_list, NULL, 0, 0, stlr }, - { "STMF", t_list, NULL, 0, 0, att }, - { "STOK", t_ignore }, - { "STUF", t_status, NULL, UPSC_STAT_ONBATT }, + { "STAT", t_list, NULL, 0, NULL, att }, + { "STBO", t_status, NULL, UPSC_STAT_ONBATT, NULL, NULL }, + { "STBL", t_status, NULL, UPSC_STAT_LOBATT, NULL, NULL }, + { "STBM", t_ignore, NULL, 0, NULL, NULL }, + { "STBP", t_status, NULL, UPSC_STAT_BYPASS, NULL, NULL }, + { "STEA", t_list, NULL, 0, NULL, env }, + { "STEM", t_list, NULL, 0, NULL, env }, + { "STLR", t_list, NULL, 0, NULL, stlr }, + { "STMF", t_list, NULL, 0, NULL, att }, + { "STOK", t_ignore, NULL, 0, NULL, NULL }, + { "STUF", t_status, NULL, UPSC_STAT_ONBATT, NULL, NULL }, - { "BTIME", t_setval, NULL, 0, &batt_runtime }, + { "BTIME", t_setval, NULL, 0, &batt_runtime, NULL }, - { "METE1", t_value, "ambient.temperature" }, - { "MERH1", t_value, "ambient.humidity" }, + { "METE1", t_value, "ambient.temperature", 0, NULL, NULL }, + { "MERH1", t_value, "ambient.humidity", 0, NULL, NULL }, - { "MIFFF", t_value, "input.frequency" }, - { "MIIL1", t_value, "input.current" }, - { "MIIL2", t_value, "input.L2.current" }, - { "MIIL3", t_value, "input.L3.current" }, - { "MIPL1", t_value, "input.realpower" }, - { "MIPL2", t_value, "input.L2.realpower" }, - { "MIPL3", t_value, "input.L3.realpower" }, - { "MISL1", t_value, "input.power" }, - { "MISL2", t_value, "input.L2.power" }, - { "MISL3", t_value, "input.L3.power" }, - { "MIUL1", t_value, "input.voltage" }, - { "MIUL2", t_value, "input.L2-N.voltage" }, - { "MIUL3", t_value, "input.L3-N.voltage" }, - { "MIU12", t_value, "input.L1-L2.voltage" }, - { "MIU23", t_value, "input.L2-L3.voltage" }, - { "MIU31", t_value, "input.L3-L1.voltage" }, + { "MIFFF", t_value, "input.frequency", 0, NULL, NULL }, + { "MIIL1", t_value, "input.current", 0, NULL, NULL }, + { "MIIL2", t_value, "input.L2.current", 0, NULL, NULL }, + { "MIIL3", t_value, "input.L3.current", 0, NULL, NULL }, + { "MIPL1", t_value, "input.realpower", 0, NULL, NULL }, + { "MIPL2", t_value, "input.L2.realpower", 0, NULL, NULL }, + { "MIPL3", t_value, "input.L3.realpower", 0, NULL, NULL }, + { "MISL1", t_value, "input.power", 0, NULL, NULL }, + { "MISL2", t_value, "input.L2.power", 0, NULL, NULL }, + { "MISL3", t_value, "input.L3.power", 0, NULL, NULL }, + { "MIUL1", t_value, "input.voltage", 0, NULL, NULL }, + { "MIUL2", t_value, "input.L2-N.voltage", 0, NULL, NULL }, + { "MIUL3", t_value, "input.L3-N.voltage", 0, NULL, NULL }, + { "MIU12", t_value, "input.L1-L2.voltage", 0, NULL, NULL }, + { "MIU23", t_value, "input.L2-L3.voltage", 0, NULL, NULL }, + { "MIU31", t_value, "input.L3-L1.voltage", 0, NULL, NULL }, - { "MBCH1", t_setval, NULL, 0, &batt_charge }, /* battery.charge */ - { "MBIII", t_setval, "battery.current", 0, &batt_current }, - { "MBINE", t_ignore, /* "battery.current.negative" */ }, - { "MBIPO", t_ignore, /* "battery.current.positive" */ }, - { "MBUNE", t_ignore, /* "battery.voltage.negative" */ }, - { "MBUPO", t_ignore, /* "battery.voltage.positive" */}, - { "MBUUU", t_setval, "battery.voltage", 0, &batt_volt }, + { "MBCH1", t_setval, NULL, 0, &batt_charge, NULL }, /* battery.charge */ + { "MBIII", t_setval, "battery.current", 0, &batt_current, NULL }, + { "MBINE", t_ignore, /* "battery.current.negative" */ NULL, 0, NULL, NULL }, + { "MBIPO", t_ignore, /* "battery.current.positive" */ NULL, 0, NULL, NULL }, + { "MBUNE", t_ignore, /* "battery.voltage.negative" */ NULL, 0, NULL, NULL }, + { "MBUPO", t_ignore, /* "battery.voltage.positive" */ NULL, 0, NULL, NULL }, + { "MBUUU", t_setval, "battery.voltage", 0, &batt_volt, NULL }, - { "MLUNE", t_ignore, /* "dc.voltage.negative" */ }, - { "MLUPO", t_ignore, /* "dc.voltage.positive" */ }, - { "MLUUU", t_ignore, /* "dc.voltage" */ }, + { "MLUNE", t_ignore, /* "dc.voltage.negative" */ NULL, 0, NULL, NULL }, + { "MLUPO", t_ignore, /* "dc.voltage.positive" */ NULL, 0, NULL, NULL }, + { "MLUUU", t_ignore, /* "dc.voltage" */ NULL, 0, NULL, NULL }, - { "MOFFF", t_final, "output.frequency" }, - { "MOIL1", t_value, "output.current" }, - { "MOIL2", t_value, "output.L2.current" }, - { "MOIL3", t_value, "output.L3.current" }, - { "MOIP1", t_value, "output.peakcurrent" }, - { "MOIP2", t_value, "output.L2.peakcurrent" }, - { "MOIP3", t_value, "output.L3.peakcurrent" }, - { "MOPL1", t_value, "output.realpower", 0, &kilo_to_unity }, - { "MOPL2", t_value, "output.L2.realpower", 0, &kilo_to_unity }, - { "MOPL3", t_value, "output.L3.realpower", 0, &kilo_to_unity }, - { "MOSL1", t_value, "output.power" }, - { "MOSL2", t_value, "output.L2.power" }, - { "MOSL3", t_value, "output.L3.power" }, - { "MOUL1", t_value, "output.voltage" }, - { "MOUL2", t_value, "output.L2-N.voltage" }, - { "MOUL3", t_value, "output.L3-N.voltage" }, - { "MOU12", t_value, "output.L1-L2.voltage" }, - { "MOU23", t_value, "output.L2-L3.voltage" }, - { "MOU31", t_value, "output.L3-L1.voltage" }, + { "MOFFF", t_final, "output.frequency", 0, NULL, NULL }, + { "MOIL1", t_value, "output.current", 0, NULL, NULL }, + { "MOIL2", t_value, "output.L2.current", 0, NULL, NULL }, + { "MOIL3", t_value, "output.L3.current", 0, NULL, NULL }, + { "MOIP1", t_value, "output.current.peak", 0, NULL, NULL }, + { "MOIP2", t_value, "output.L2.current.peak", 0, NULL, NULL }, + { "MOIP3", t_value, "output.L3.current.peak", 0, NULL, NULL }, + { "MOPL1", t_value, "output.realpower", 0, &kilo_to_unity, NULL }, + { "MOPL2", t_value, "output.L2.realpower", 0, &kilo_to_unity, NULL }, + { "MOPL3", t_value, "output.L3.realpower", 0, &kilo_to_unity, NULL }, + { "MOSL1", t_value, "output.power", 0, NULL, NULL }, + { "MOSL2", t_value, "output.L2.power", 0, NULL, NULL }, + { "MOSL3", t_value, "output.L3.power", 0, NULL, NULL }, + { "MOUL1", t_value, "output.voltage", 0, NULL, NULL }, + { "MOUL2", t_value, "output.L2-N.voltage", 0, NULL, NULL }, + { "MOUL3", t_value, "output.L3-N.voltage", 0, NULL, NULL }, + { "MOU12", t_value, "output.L1-L2.voltage", 0, NULL, NULL }, + { "MOU23", t_value, "output.L2-L3.voltage", 0, NULL, NULL }, + { "MOU31", t_value, "output.L3-L1.voltage", 0, NULL, NULL }, - { "MPUL1", t_value, "input.bypass.L1-N.voltage" }, - { "MPUL2", t_value, "input.bypass.L2-N.voltage" }, - { "MPUL3", t_value, "input.bypass.L3-N.voltage" }, + { "MPUL1", t_value, "input.bypass.L1-N.voltage", 0, NULL, NULL }, + { "MPUL2", t_value, "input.bypass.L2-N.voltage", 0, NULL, NULL }, + { "MPUL3", t_value, "input.bypass.L3-N.voltage", 0, NULL, NULL }, - { "MUTE1", t_value, "ups.temperature" }, - { NULL } + { "MUTE1", t_value, "ups.temperature", 0, NULL, NULL }, + { NULL, t_ignore, NULL, 0, NULL, NULL } }; /* Responses for UPDV */ static simple_t nominal[] = { - { "NIUHH", t_value, "input.voltage.maximum" }, - { "NIULL", t_value, "input.voltage.minimum" }, - { "NIUNN", t_value, "input.voltage.nominal" }, + { "NIUHH", t_value, "input.voltage.maximum", 0, NULL, NULL }, + { "NIULL", t_value, "input.voltage.minimum", 0, NULL, NULL }, + { "NIUNN", t_value, "input.voltage.nominal", 0, NULL, NULL }, - { "NIIHH", t_value, "input.current.maximum" }, - { "NIILL", t_value, "input.current.minimum" }, - { "NIINN", t_value, "input.current.nominal" }, + { "NIIHH", t_value, "input.current.maximum", 0, NULL, NULL }, + { "NIILL", t_value, "input.current.minimum", 0, NULL, NULL }, + { "NIINN", t_value, "input.current.nominal", 0, NULL, NULL }, - { "NIPHH", t_value, "input.realpower.maximum" }, - { "NIPNN", t_value, "input.realpower.nominal" }, + { "NIPHH", t_value, "input.realpower.maximum", 0, NULL, NULL }, + { "NIPNN", t_value, "input.realpower.nominal", 0, NULL, NULL }, - { "NISHH", t_value, "input.power.maximum" }, - { "NISNN", t_value, "input.power.nominal" }, + { "NISHH", t_value, "input.power.maximum", 0, NULL, NULL }, + { "NISNN", t_value, "input.power.nominal", 0, NULL, NULL }, - { "NBAHN", t_setval, "battery.capacity.nominal", 0, &batt_cap_nom }, - { "NBIHH", t_ignore, "battery charge current maximum" }, - { "NBILL", t_setval, NULL, 0, &batt_disch_curr_max}, - { "NBINN", t_value, "battery.current.nominal" }, - { "NBTHH", t_setval, NULL, 0, &batt_runtime_max}, - { "NBUHH", t_setval, "battery.voltage.maximum", 0, &batt_volt_high }, - { "NBULL", t_setval, "battery.voltage.minimum", 0, &batt_volt_low }, - { "NBUNN", t_setval, "battery.voltage.nominal", 0, &batt_volt_nom }, + { "NBAHN", t_setval, "battery.capacity.nominal", 0, &batt_cap_nom, NULL }, + { "NBIHH", t_ignore, "battery charge current maximum", 0, NULL, NULL }, + { "NBILL", t_setval, NULL, 0, &batt_disch_curr_max, NULL }, + { "NBINN", t_value, "battery.current.nominal", 0, NULL, NULL }, + { "NBTHH", t_setval, NULL, 0, &batt_runtime_max, NULL }, + { "NBUHH", t_setval, "battery.voltage.maximum", 0, &batt_volt_high, NULL }, + { "NBULL", t_setval, "battery.voltage.minimum", 0, &batt_volt_low, NULL }, + { "NBUNN", t_setval, "battery.voltage.nominal", 0, &batt_volt_nom, NULL }, - { "NOFHH", t_value, "output.frequency.maximum" }, - { "NOFLL", t_final, "output.frequency.minimum" }, - { "NOIHH", t_setval, "output.current.maximum", 0, &max_out_current }, - { "NOINN", t_setval, "output.current.nominal", 0, &nom_out_current }, - { "NOPNN", t_value, "output.realpower.nominal", 0, &outpwr_factor }, - { "NOSNN", t_value, "ups.power.nominal", 0, &outpwr_factor }, - { "NOUHH", t_value, "output.voltage.maximum" }, - { "NOULL", t_value, "output.voltage.minimum" }, - { "NOUNN", t_value, "output.voltage.nominal" }, + { "NOFHH", t_value, "output.frequency.maximum", 0, NULL, NULL }, + { "NOFLL", t_final, "output.frequency.minimum", 0, NULL, NULL }, + { "NOIHH", t_setval, "output.current.maximum", 0, &max_out_current, NULL }, + { "NOINN", t_setval, "output.current.nominal", 0, &nom_out_current, NULL }, + { "NOPNN", t_value, "output.realpower.nominal", 0, &outpwr_factor, NULL }, + { "NOSNN", t_value, "ups.power.nominal", 0, &outpwr_factor, NULL }, + { "NOUHH", t_value, "output.voltage.maximum", 0, NULL, NULL }, + { "NOULL", t_value, "output.voltage.minimum", 0, NULL, NULL }, + { "NOUNN", t_value, "output.voltage.nominal", 0, NULL, NULL }, - { "NUTEH", t_value, "ups.temperature.maximum" }, - { NULL } + { "NUTEH", t_value, "ups.temperature.maximum", 0, NULL, NULL }, + { NULL, t_ignore, NULL, 0, NULL, NULL } }; /* Status responses for UPBS command */ static simple_t battery[] = { - { "MBTE1", t_value, "battery.1.temperature" }, - { "MBIN1", t_ignore, NULL /* aging index */ }, - { "BDAT1", t_string, "battery.1.date" }, - { "MBTE2", t_value, "battery.2.temperature.2" }, - { "MBIN2", t_ignore, NULL }, - { "BDAT2", t_string, "battery.2.date" }, - { "MBTE3", t_value, "battery.3.temperature" }, - { "MBIN3", t_ignore, NULL }, - { "BDAT3", t_string, "battery.3.date" }, - { "MBTE4", t_value, "battery.4.temperature" }, - { "MBIN4", t_ignore, NULL }, - { "BDAT4", t_string, "battery.4.date" }, - { "MBTE5", t_value, "battery.5.temperature" }, - { "MBIN5", t_ignore, NULL }, - { "BDAT5", t_string, "battery.5.date" }, - { "MBTE6", t_value, "battery.6.temperature" }, - { "MBIN6", t_ignore, NULL }, - { "BDAT6", t_string, "battery.6.date" }, - { "MBTE7", t_value, "battery.7.temperature" }, - { "MBIN7", t_ignore, NULL }, - { "BDAT7", t_string, "battery.7.date" }, - { "MBTE8", t_value, "battery.8.temperature" }, - { "MBIN8", t_ignore, NULL }, - { "BDAT8", t_finstr, "battery.8.date" }, - { NULL } + { "MBTE1", t_value, "battery.1.temperature", 0, NULL, NULL }, + { "MBIN1", t_ignore, NULL /* aging index */, 0, NULL, NULL }, + { "BDAT1", t_string, "battery.1.date", 0, NULL, NULL }, + { "MBTE2", t_value, "battery.2.temperature.2", 0, NULL, NULL }, + { "MBIN2", t_ignore, NULL, 0, NULL, NULL }, + { "BDAT2", t_string, "battery.2.date", 0, NULL, NULL }, + { "MBTE3", t_value, "battery.3.temperature", 0, NULL, NULL }, + { "MBIN3", t_ignore, NULL, 0, NULL, NULL }, + { "BDAT3", t_string, "battery.3.date", 0, NULL, NULL }, + { "MBTE4", t_value, "battery.4.temperature", 0, NULL, NULL }, + { "MBIN4", t_ignore, NULL, 0, NULL, NULL }, + { "BDAT4", t_string, "battery.4.date", 0, NULL, NULL }, + { "MBTE5", t_value, "battery.5.temperature", 0, NULL, NULL }, + { "MBIN5", t_ignore, NULL, 0, NULL, NULL }, + { "BDAT5", t_string, "battery.5.date", 0, NULL, NULL }, + { "MBTE6", t_value, "battery.6.temperature", 0, NULL, NULL }, + { "MBIN6", t_ignore, NULL, 0, NULL, NULL }, + { "BDAT6", t_string, "battery.6.date", 0, NULL, NULL }, + { "MBTE7", t_value, "battery.7.temperature", 0, NULL, NULL }, + { "MBIN7", t_ignore, NULL, 0, NULL, NULL }, + { "BDAT7", t_string, "battery.7.date", 0, NULL, NULL }, + { "MBTE8", t_value, "battery.8.temperature", 0, NULL, NULL }, + { "MBIN8", t_ignore, NULL, 0, NULL, NULL }, + { "BDAT8", t_finstr, "battery.8.date", 0, NULL, NULL }, + { NULL, t_ignore, NULL, 0, NULL, NULL } }; static cmd_t commands[] = { - { "load.off", NULL, NULL }, - { "load.on", NULL, NULL }, - { "shutdown.return", "UPPF", "IJHLDMGCIU" }, - { "shutdown.stayoff", "UPPD", "LGGNLMDPGV" }, - { "shutdown.stop", "UPPU", NULL }, - { "shutdown.reboot", "UPPC", "IJHLDMGCIU" }, - { "shutdown.reboot.graceful", NULL, NULL }, - { "test.panel.start", "UPIS", NULL }, - { "test.panel.stop", NULL, NULL }, - { "test.failure.start", NULL, NULL }, - { "test.failure.stop", NULL, NULL }, - { "test.battery.start", "UPBT", "1" }, - { "test.battery.stop", NULL, NULL }, - { "calibrate.start", NULL, NULL }, - { "calibrate.stop", NULL, NULL }, - { "bypass.start", NULL, NULL }, - { "bypass.stop", NULL, NULL }, - { "reset.input.minmax", NULL, NULL }, - { "reset.watchdog", NULL, NULL }, - { "beeper.enable", NULL, NULL }, - { "beeper.disable", NULL, NULL }, - { "beeper.on", NULL, NULL }, - { "beeper.off", NULL, NULL }, - { NULL } + { "load.off", NULL, NULL, 0 }, + { "load.on", NULL, NULL, 0 }, + { "shutdown.return", "UPPF", "IJHLDMGCIU", 0 }, + { "shutdown.stayoff", "UPPD", "LGGNLMDPGV", 0 }, + { "shutdown.stop", "UPPU", NULL, 0 }, + { "shutdown.reboot", "UPPC", "IJHLDMGCIU", 0 }, + { "shutdown.reboot.graceful", NULL, NULL, 0 }, + { "test.panel.start", "UPIS", NULL, 0 }, + { "test.panel.stop", NULL, NULL, 0 }, + { "test.failure.start", NULL, NULL, 0 }, + { "test.failure.stop", NULL, NULL, 0 }, + { "test.battery.start", "UPBT", "1", 0 }, + { "test.battery.stop", NULL, NULL, 0 }, + { "calibrate.start", NULL, NULL, 0 }, + { "calibrate.stop", NULL, NULL, 0 }, + { "bypass.start", NULL, NULL, 0 }, + { "bypass.stop", NULL, NULL, 0 }, + { "reset.input.minmax", NULL, NULL, 0 }, + { "reset.watchdog", NULL, NULL, 0 }, + { "beeper.enable", NULL, NULL, 0 }, + { "beeper.disable", NULL, NULL, 0 }, + { "beeper.on", NULL, NULL, 0 }, + { "beeper.off", NULL, NULL, 0 }, + { NULL, NULL, NULL, 0 } }; static cmd_t variables[] = { - { "ups.delay.reboot", "UPCD", "ACCD" }, - { "ups.delay.shutdown", "UPSD", "ACSD" }, - { NULL } + { "ups.delay.reboot", "UPCD", "ACCD", 0 }, + { "ups.delay.shutdown", "UPSD", "ACSD", 0 }, + { NULL, NULL, NULL, 0 } }; static int instcmd (const char *auxcmd, const char *data); static int setvar (const char *var, const char *data); -static void upsc_setstatus(unsigned int status); +static void upsc_setstatus(unsigned int upsc_status); static void upsc_flush_input(void); static void upsc_getbaseinfo(void); static int upsc_commandlist(void); static int upsc_getparams(const char *cmd, const simple_t *table); static int upsc_getvalue(const char *cmd, const char *param, const char *resp, const char *var, char *ret); -static int upscsend(const char *cmd); -static int upscrecv(char *buf); +static ssize_t upscsend(const char *cmd); +static ssize_t upscrecv(char *buf); static int upsc_simple(const simple_t *sp, const char *var, const char *val); static void check_uppm(void); static float batt_charge_pct(void); @@ -464,7 +464,7 @@ void upsdrv_help(void) void upsdrv_initups(void) { struct termios tio; - int baud = B1200; + speed_t baud = B1200; char *str; if ((str = getval("baudrate")) != NULL) { @@ -511,12 +511,13 @@ void upsdrv_initups(void) upsdebugx(1, "input_timeout = %d Sec", input_timeout_sec); if ((str = getval("output_pace")) != NULL) { - int temp = atoi(str); + /* Range should be at least up to 1000000; a C99+ long guarantees that */ + long temp = atol(str); if (temp <= 0) fatalx(EXIT_FAILURE, "Bad output_pace parameter: %s", str); - output_pace_usec = temp; + output_pace_usec = (useconds_t)temp; } - upsdebugx(1, "output_pace = %d uSec", output_pace_usec); + upsdebugx(1, "output_pace = %ju uSec", (uintmax_t)output_pace_usec); if ((str = getval("full_update_timer")) != NULL) { int temp = atoi(str); @@ -780,7 +781,7 @@ void upsdrv_updateinfo(void) change_name(simple, "output.current", "output.L1.current"); change_name(simple, - "output.peakcurrent", "output.L1.peakcurrent"); + "output.current.peak", "output.L1.current.peak"); change_name(simple, "output.realpower", "output.L1.realpower"); change_name(simple, @@ -838,8 +839,8 @@ void upsdrv_updateinfo(void) if (batt_runtime >= 0 && batt_runtime < 9999) { dstate_setinfo("battery.runtime", "%.0f", batt_runtime*60); } - else if (load > 0 && batt_disch_curr_max != 0) { - float est_battcurr = load * abs(batt_disch_curr_max); + else if (load > 0 && ! f_equal(batt_disch_curr_max, 0)) { + float est_battcurr = load * fabs(batt_disch_curr_max); /* Peukert equation */ float runtime = (batt_cap_nom*3600)/pow(est_battcurr, 1.35); @@ -964,8 +965,10 @@ void upsdrv_cleanup(void) /* * Generate status string from bitfield */ -static void upsc_setstatus(unsigned int status) +static void upsc_setstatus(unsigned int upsc_status) { + /* Save into global state variable for the driver instance */ + status = upsc_status; /* * I'll look for all available statuses, even though they might not be @@ -1001,9 +1004,9 @@ static void upsc_setstatus(unsigned int status) /* Add \r to end of command and send to UPS */ /* returns < 0 on errors, 0 on timeout and > 0 on success. */ -static int upscsend(const char *cmd) +static ssize_t upscsend(const char *cmd) { - int res; + ssize_t res; res = ser_send_pace(upsfd, output_pace_usec, "%s%s%s", use_pre_lf ? "\n" : "", @@ -1024,9 +1027,9 @@ static int upscsend(const char *cmd) /* Return a string read from UPS */ /* returns < 0 on errors, 0 on timeout and > 0 on success. */ -static int upscrecv(char *buf) +static ssize_t upscrecv(char *buf) { - int res; + ssize_t res; /* NOTE: the serial port is set to use Canonical Mode Input Processing, which means ser_get_buf() either returns one line terminated with @@ -1047,7 +1050,7 @@ static int upscrecv(char *buf) } else if (res == 0) { upsdebugx(3, "upscrecv: Timeout"); } else { - upsdebugx(3, "upscrecv: %u bytes:\t'%s'", res-1, rtrim(buf, ENDCHAR)); + upsdebugx(3, "upscrecv: %zd bytes:\t'%s'", res-1, str_rtrim(buf, ENDCHAR)); } return res; @@ -1186,19 +1189,19 @@ static void check_uppm(void) if (strcmp(var, "ACPM")) upslogx(LOG_ERR, "Bad response to UPPM: %s", var); while (1) { - int val, stat; + int intval, stat; upscrecv(var); if (strlen(var) == 0) break; upsdebugx(2, "UPPM available: %s", var); - stat = sscanf(var, "P%2d", &val); + stat = sscanf(var, "P%2d", &intval); if (stat != 1) { upslogx(LOG_ERR, "Bad response to UPPM: %s", var); return; } - has_uppm_p[val] = 1; - if (val > last) - last = val; + has_uppm_p[intval] = 1; + if (intval > last) + last = intval; } for (i = 0; i <= last; i++) { @@ -1301,7 +1304,10 @@ static int upsc_simple(const simple_t *sp, const char *var, const char *val) break; case t_final: buffer_empty = 1; + /* FIXME? Should this really fall through, or should break? */ + goto fallthrough_bufempty_processing_1; case t_value: + fallthrough_bufempty_processing_1: if (!sp->desc) { break; } @@ -1318,7 +1324,10 @@ static int upsc_simple(const simple_t *sp, const char *var, const char *val) break; case t_finstr: buffer_empty = 1; + /* FIXME? Should this really fall through, or should break? */ + goto fallthrough_bufempty_processing_2; case t_string: + fallthrough_bufempty_processing_2: if (!sp->desc) { break; } @@ -1348,9 +1357,36 @@ static int upsc_simple(const simple_t *sp, const char *var, const char *val) upslogx(LOG_ERR, "Unknown value: %s %s", var, val); break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: upslogx(LOG_ERR, "Unknown type for %s", var); break; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif + } return 1; } diff --git a/drivers/upsdrvctl.c b/drivers/upsdrvctl.c index 623f753..a33fcd1 100644 --- a/drivers/upsdrvctl.c +++ b/drivers/upsdrvctl.c @@ -17,6 +17,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" /* must be the first header */ + #include #include #include @@ -24,10 +26,11 @@ #include #include -#include "config.h" #include "proto.h" #include "common.h" #include "upsconf.h" +#include "attribute.h" +#include "nut_stdint.h" typedef struct { char *upsname; @@ -42,15 +45,27 @@ static ups_t *upstable = NULL; static int maxsdorder = 0, testmode = 0, exec_error = 0; + /* Should we wait for driver (1) or "parallelize" drivers start (0) */ +static int waitfordrivers = 1; + /* timer - keeps us from getting stuck if a driver hangs */ static int maxstartdelay = 45; + /* counter - retry that many time(s) to start the driver if it fails to */ +static int maxretry = 1; + + /* timer - delay between each restart attempt of the driver(s) */ +static int retrydelay = 5; + /* Directory where driver executables live */ static char *driverpath = NULL; /* passthrough to the drivers: chroot path and new user name */ static char *pt_root = NULL, *pt_user = NULL; + /* flag to pass nut_debug_level to launched drivers (as their -D... args) */ +static int nut_debug_level_passthrough = 0; + void do_upsconf_args(char *upsname, char *var, char *val) { ups_t *tmp, *last; @@ -65,6 +80,15 @@ void do_upsconf_args(char *upsname, char *var, char *val) driverpath = xstrdup(val); } + if (!strcmp(var, "maxretry")) + maxretry = atoi(val); + + if (!strcmp(var, "retrydelay")) + retrydelay = atoi(val); + + if (!strcmp(var, "nowait")) + waitfordrivers = 0; + /* ignore anything else - it's probably for main */ return; @@ -132,13 +156,14 @@ static void stop_driver(const ups_t *ups) ret = stat(pidfn, &fs); if ((ret != 0) && (ups->port != NULL)) { + upslog_with_errno(LOG_ERR, "Can't open %s", pidfn); snprintf(pidfn, sizeof(pidfn), "%s/%s-%s.pid", altpidpath(), ups->driver, xbasename(ups->port)); ret = stat(pidfn, &fs); } if (ret != 0) { - upslog_with_errno(LOG_ERR, "Can't open %s", pidfn); + upslog_with_errno(LOG_ERR, "Can't open %s either", pidfn); exec_error++; return; } @@ -159,6 +184,8 @@ static void stop_driver(const ups_t *ups) static void waitpid_timeout(const int sig) { + NUT_UNUSED_VARIABLE(sig); + /* do nothing */ return; } @@ -191,15 +218,25 @@ static void forkexec(char *const argv[], const ups_t *ups) int wstat; struct sigaction sa; + /* Handle "parallel" drivers startup */ + if (waitfordrivers == 0) { + upsdebugx(2, "'nowait' set, continuing..."); + return; + } + sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sa.sa_handler = waitpid_timeout; sigaction(SIGALRM, &sa, NULL); - if (ups->maxstartdelay != -1) - alarm(ups->maxstartdelay); - else - alarm(maxstartdelay); + /* Use the local maxstartdelay, if available */ + if (ups->maxstartdelay != -1) { + if (ups->maxstartdelay >= 0) + alarm((unsigned int)ups->maxstartdelay); + } else { /* Otherwise, use the global (or default) value */ + if (maxstartdelay >= 0) + alarm((unsigned int)maxstartdelay); + } ret = waitpid(pid, &wstat, 0); @@ -245,9 +282,10 @@ static void forkexec(char *const argv[], const ups_t *ups) static void start_driver(const ups_t *ups) { - char *argv[8]; - char dfn[SMALLBUF]; + char *argv[9]; + char dfn[SMALLBUF], dbg[SMALLBUF]; int ret, arg = 0; + int initial_exec_error = exec_error, drv_maxretry = maxretry; struct stat fs; upsdebugx(1, "Starting UPS: %s", ups->upsname); @@ -259,6 +297,63 @@ static void start_driver(const ups_t *ups) fatal_with_errno(EXIT_FAILURE, "Can't start %s", dfn); argv[arg++] = dfn; + + if (nut_debug_level_passthrough > 0 + && nut_debug_level > 0 + && sizeof(dbg) > 3 + ) { + size_t d, m; + + /* cut-off point: buffer size or requested debug level */ + m = sizeof(dbg) - 1; /* leave a place for '\0' */ + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunreachable-code" +# pragma clang diagnostic ignored "-Wtautological-compare" +# pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + /* Different platforms, different sizes, none fits all... */ + /* can we fit this many 'D's? */ + if ((uintmax_t)SIZE_MAX > (uintmax_t)nut_debug_level /* else can't assign, requested debug level is huge */ + && (size_t)nut_debug_level + 1 < m + ) { +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif + /* need even fewer (leave a place for '-'): */ + m = (size_t)nut_debug_level + 1; + } else { + upsdebugx(1, "Requested debugging level %d is too " + "high for pass-through args, truncated to %zu", + nut_debug_level, + (m - 1) /* count off '-' (and '\0' already) chars */ + ); + } + + dbg[0] = '-'; + for (d = 1; d < m ; d++) { + dbg[d] = 'D'; + } + dbg[d] = '\0'; + argv[arg++] = dbg; + } + argv[arg++] = (char *)"-a"; /* FIXME: cast away const */ argv[arg++] = ups->upsname; @@ -276,13 +371,35 @@ static void start_driver(const ups_t *ups) /* tie it off */ argv[arg++] = NULL; - debugcmdline(2, "exec: ", argv); - if (!testmode) { - forkexec(argv, ups); + while (drv_maxretry > 0) { + int cur_exec_error = exec_error; + + upsdebugx(2, "%i remaining attempts", drv_maxretry); + debugcmdline(2, "exec: ", argv); + drv_maxretry--; + + if (!testmode) { + forkexec(argv, ups); + } + + /* driver command succeeded */ + if (cur_exec_error == exec_error) { + drv_maxretry = 0; + exec_error = initial_exec_error; + } + else { + /* otherwise, retry if still needed */ + if (drv_maxretry > 0) + if (retrydelay >= 0) + sleep ((unsigned int)retrydelay); + } } } +static void help(const char *progname) + __attribute__((noreturn)); + static void help(const char *progname) { printf("Starts and stops UPS drivers via ups.conf.\n\n"); @@ -293,6 +410,7 @@ static void help(const char *progname) printf(" -t testing mode - prints actions without doing them\n"); printf(" -u drivers started will switch from root to \n"); printf(" -D raise debugging level\n"); + printf(" -d pass debugging level from upsdrvctl to driver\n"); printf(" start start all UPS drivers in ups.conf\n"); printf(" start only start driver for UPS \n"); printf(" stop stop all UPS drivers in ups.conf\n"); @@ -384,7 +502,7 @@ static void send_all_drivers(void (*command)(const ups_t *)) while (ups) { if (ups->sdorder == i) command(ups); - + ups = ups->next; } } @@ -420,7 +538,7 @@ int main(int argc, char **argv) UPS_VERSION); prog = argv[0]; - while ((i = getopt(argc, argv, "+htu:r:DV")) != -1) { + while ((i = getopt(argc, argv, "+htu:r:DdV")) != -1) { switch(i) { case 'r': pt_root = optarg; @@ -441,10 +559,13 @@ int main(int argc, char **argv) nut_debug_level++; break; + case 'd': + nut_debug_level_passthrough = 1; + break; + case 'h': default: help(prog); - break; } } @@ -461,14 +582,18 @@ int main(int argc, char **argv) nut_debug_level = 2; } - upsdebugx(2, "\n" - "If you're not a NUT core developer, chances are that you're told to enable debugging\n" - "to see why a driver isn't working for you. We're sorry for the confusion, but this is\n" - "the 'upsdrvctl' wrapper, not the driver you're interested in.\n\n" - "Below you'll find one or more lines starting with 'exec:' followed by an absolute\n" - "path to the driver binary and some command line option. This is what the driver\n" - "starts and you need to copy and paste that line and append the debug flags to that\n" - "line (less the 'exec:' prefix).\n"); + if (nut_debug_level_passthrough == 0) { + upsdebugx(2, "\n" + "If you're not a NUT core developer, chances are that you're told to enable debugging\n" + "to see why a driver isn't working for you. We're sorry for the confusion, but this is\n" + "the 'upsdrvctl' wrapper, not the driver you're interested in.\n\n" + "Below you'll find one or more lines starting with 'exec:' followed by an absolute\n" + "path to the driver binary and some command line option. This is what the driver\n" + "starts and you need to copy and paste that line and append the debug flags to that\n" + "line (less the 'exec:' prefix).\n\n" + "Alternately, provide an additional '-d' (lower-case) parameter to 'upsdrvctl' to\n" + "pass its current debug level to the launched driver.\n"); + } if (!strcmp(argv[0], "start")) command = &start_driver; diff --git a/drivers/upshandler.h b/drivers/upshandler.h index b7539a0..fea10bc 100644 --- a/drivers/upshandler.h +++ b/drivers/upshandler.h @@ -30,10 +30,10 @@ enum { /* return values for setvar */ enum { - STAT_SET_HANDLED = 0, /* completed successfully */ - STAT_SET_UNKNOWN, /* unspecified error */ - STAT_SET_INVALID, /* not writeable */ - STAT_SET_FAILED /* writing failed */ + STAT_SET_HANDLED = 0, /* completed successfully */ + STAT_SET_UNKNOWN, /* unspecified error */ + STAT_SET_INVALID, /* not writeable */ + STAT_SET_FAILED /* writing failed */ }; /* structure for funcs that get called by msg parse routine */ diff --git a/drivers/usb-common.c b/drivers/usb-common.c index e459872..5f2dc16 100644 --- a/drivers/usb-common.c +++ b/drivers/usb-common.c @@ -20,27 +20,29 @@ #include "common.h" #include "usb-common.h" -int is_usb_device_supported(usb_device_id_t *usb_device_id_list, int dev_VendorID, int dev_ProductID) +int is_usb_device_supported(usb_device_id_t *usb_device_id_list, USBDevice_t *device) { int retval = NOT_SUPPORTED; usb_device_id_t *usbdev; - for (usbdev = usb_device_id_list; usbdev->vendorID != -1; usbdev++) { - - if (usbdev->vendorID != dev_VendorID) { + for (usbdev = usb_device_id_list; + (usbdev->vendorID != 0 || usbdev->productID != 0 || usbdev->fun != NULL); + usbdev++ + ) { + if (usbdev->vendorID != device->VendorID) { continue; } /* flag as possibly supported if we see a known vendor */ retval = POSSIBLY_SUPPORTED; - if (usbdev->productID != dev_ProductID) { + if (usbdev->productID != device->ProductID) { continue; } /* call the specific handler, if it exists */ if (usbdev->fun != NULL) { - (*usbdev->fun)(); + (*usbdev->fun)(device); } return SUPPORTED; @@ -78,28 +80,49 @@ static int strcmp_null(char *s1, char *s2) static int match_function_exact(USBDevice_t *hd, void *privdata) { USBDevice_t *data = (USBDevice_t *)privdata; - + + upsdebugx(3, "%s: matching a device...", __func__); + if (hd->VendorID != data->VendorID) { + upsdebugx(2, "%s: failed match of %s: %4x != %4x", + __func__, "VendorID", hd->VendorID, data->VendorID); return 0; } if (hd->ProductID != data->ProductID) { + upsdebugx(2, "%s: failed match of %s: %4x != %4x", + __func__, "ProductID", hd->ProductID, data->ProductID); return 0; } if (strcmp_null(hd->Vendor, data->Vendor) != 0) { + upsdebugx(2, "%s: failed match of %s: %s != %s", + __func__, "Vendor", hd->Vendor, data->Vendor); return 0; } if (strcmp_null(hd->Product, data->Product) != 0) { + upsdebugx(2, "%s: failed match of %s: %s != %s", + __func__, "Product", hd->Product, data->Product); return 0; } if (strcmp_null(hd->Serial, data->Serial) != 0) { + upsdebugx(2, "%s: failed match of %s: %s != %s", + __func__, "Serial", hd->Serial, data->Serial); return 0; } #ifdef DEBUG_EXACT_MATCH_BUS if (strcmp_null(hd->Bus, data->Bus) != 0) { + upsdebugx(2, "%s: failed match of %s: %s != %s", + __func__, "Bus", hd->Bus, data->Bus); + return 0; + } +#endif +#ifdef DEBUG_EXACT_MATCH_DEVICE + if (strcmp_null(hd->Device, data->Device) != 0) { + upsdebugx(2, "%s: failed match of %s: %s != %s", + __func__, "Device", hd->Device, data->Device); return 0; } #endif @@ -137,6 +160,9 @@ int USBNewExactMatcher(USBDeviceMatcher_t **matcher, USBDevice_t *hd) data->Serial = hd->Serial ? strdup(hd->Serial) : NULL; #ifdef DEBUG_EXACT_MATCH_BUS data->Bus = hd->Bus ? strdup(hd->Bus) : NULL; +#endif +#ifdef DEBUG_EXACT_MATCH_DEVICE + data->Device = hd->Device ? strdup(hd->Device) : NULL; #endif *matcher = m; @@ -159,6 +185,9 @@ void USBFreeExactMatcher(USBDeviceMatcher_t *matcher) free(data->Serial); #ifdef DEBUG_EXACT_MATCH_BUS free(data->Bus); +#endif +#ifdef DEBUG_EXACT_MATCH_DEVICE + free(data->Device); #endif free(data); free(matcher); @@ -271,7 +300,7 @@ static int match_regex_hex(regex_t *preg, int n) /* private data type: hold a set of compiled regular expressions. */ typedef struct regex_matcher_data_s { - regex_t *regex[6]; + regex_t *regex[7]; } regex_matcher_data_t; /* private callback function for regex matches */ @@ -279,48 +308,97 @@ static int match_function_regex(USBDevice_t *hd, void *privdata) { regex_matcher_data_t *data = (regex_matcher_data_t *)privdata; int r; - + + upsdebugx(3, "%s: matching a device...", __func__); + r = match_regex_hex(data->regex[0], hd->VendorID); if (r != 1) { +/* + upsdebugx(2, "%s: failed match of %s: %4x !~ %s", + __func__, "VendorID", hd->VendorID, data->regex[0]); +*/ + upsdebugx(2, "%s: failed match of %s: %4x", + __func__, "VendorID", hd->VendorID); return r; } r = match_regex_hex(data->regex[1], hd->ProductID); if (r != 1) { +/* + upsdebugx(2, "%s: failed match of %s: %4x !~ %s", + __func__, "ProductID", hd->ProductID, data->regex[1]); +*/ + upsdebugx(2, "%s: failed match of %s: %4x", + __func__, "ProductID", hd->ProductID); return r; } r = match_regex(data->regex[2], hd->Vendor); if (r != 1) { +/* + upsdebugx(2, "%s: failed match of %s: %s !~ %s", + __func__, "Vendor", hd->Vendor, data->regex[2]); +*/ + upsdebugx(2, "%s: failed match of %s: %s", + __func__, "Vendor", hd->Vendor); return r; } r = match_regex(data->regex[3], hd->Product); if (r != 1) { +/* + upsdebugx(2, "%s: failed match of %s: %s !~ %s", + __func__, "Product", hd->Product, data->regex[3]); +*/ + upsdebugx(2, "%s: failed match of %s: %s", + __func__, "Product", hd->Product); return r; } r = match_regex(data->regex[4], hd->Serial); if (r != 1) { +/* + upsdebugx(2, "%s: failed match of %s: %s !~ %s", + __func__, "Serial", hd->Serial, data->regex[4]); +*/ + upsdebugx(2, "%s: failed match of %s: %s", + __func__, "Serial", hd->Serial); return r; } r = match_regex(data->regex[5], hd->Bus); if (r != 1) { +/* + upsdebugx(2, "%s: failed match of %s: %s !~ %s", + __func__, "Bus", hd->Bus, data->regex[5]); +*/ + upsdebugx(2, "%s: failed match of %s: %s", + __func__, "Bus", hd->Bus); + return r; + } + + r = match_regex(data->regex[6], hd->Device); + if (r != 1) { +/* + upsdebugx(2, "%s: failed match of %s: %s !~ %s", + __func__, "Device", hd->Device, data->regex[6]); +*/ + upsdebugx(2, "%s: failed match of %s: %s", + __func__, "Device", hd->Device); return r; } return 1; } /* constructor: create a regular expression matcher. This matcher is - * based on six regular expression strings in regex_array[0..5], + * based on seven regular expression strings in regex_array[0..6], * corresponding to: vendorid, productid, vendor, product, serial, - * bus. Any of these strings can be NULL, which matches + * bus, device. Any of these strings can be NULL, which matches * everything. Cflags are as in regcomp(3). Typical values for cflags * are REG_ICASE (case insensitive matching) and REG_EXTENDED (use * extended regular expressions). On success, return 0 and store the * matcher in *matcher. On error, return -1 with errno set, or return - * i=1--6 to indicate that the regular expression regex_array[i-1] was + * i=1--7 to indicate that the regular expression regex_array[i-1] was * ill-formed (an error message can then be retrieved with * regerror(3)). */ @@ -345,7 +423,7 @@ int USBNewRegexMatcher(USBDeviceMatcher_t **matcher, char **regex, int cflags) m->privdata = (void *)data; m->next = NULL; - for (i=0; i<6; i++) { + for (i=0; i<7; i++) { r = compile_regex(&data->regex[i], regex[i], cflags); if (r == -2) { r = i+1; @@ -365,14 +443,14 @@ void USBFreeRegexMatcher(USBDeviceMatcher_t *matcher) { int i; regex_matcher_data_t *data; - + if (!matcher) { return; } data = (regex_matcher_data_t *)matcher->privdata; - for (i = 0; i < 6; i++) { + for (i = 0; i < 7; i++) { if (!data->regex[i]) { continue; } @@ -384,3 +462,38 @@ void USBFreeRegexMatcher(USBDeviceMatcher_t *matcher) free(data); free(matcher); } + +void warn_if_bad_usb_port_filename(const char *fn) { + /* USB drivers ignore the 'port' setting - log a notice + * if it is not "auto". Note: per se, ignoring the port + * (or internally the device_path variable from main.c) + * is currently not a bug and is actually documented at + * docs/config-notes.txt; however it is not something + * evident to users during troubleshooting a device. + * Documentation and common practice recommend port=auto + * so here we warn during driver start if it has some + * other value and users might think it is honoured. + */ + + if (!fn) { + upslogx(LOG_WARNING, + "WARNING: %s(): port argument was not " + "specified to the driver", + __func__); + return; + } + + if (!strcmp(fn, "auto")) + return; + + upslogx(LOG_WARNING, + "WARNING: %s(): port argument specified to\n" + " the driver is \"%s\" but USB drivers do " + "not use it and rely on\n" + " libusb walking all devices and matching " + "their identification metadata.\n" + " NUT documentation recommends port=\"auto\" " + "for USB devices to avoid confusion.", + __func__, fn); + return; +} diff --git a/drivers/usb-common.h b/drivers/usb-common.h index 8df0b5a..cc9e8ce 100644 --- a/drivers/usb-common.h +++ b/drivers/usb-common.h @@ -1,6 +1,43 @@ /* usb-common.h - prototypes for the common useful USB functions + * NOTE that it aims to consolidate use of different USB-related APIs + * such as libusb-0.1 and libusb-1.0 in a way that minimizes the coding + * difference for majority of NUT - so typedef'ing or converting various + * data types and method signatures. + * + * Beside your system headers (content varies between distros) you can + * find some documentation online: + * - libusb-1.0: + * https://github.com/libusb/libusb/blob/master/libusb/libusb.h + * https://libusb.sourceforge.io/api-1.0/ + * https://libusb.sourceforge.io/api-1.0/libusb_api.html + * https://github.com/libusb/libusb/wiki + * https://nxmnpg.lemoda.net/3/libusb (one page, easy to search) + * - libusb-0.1 is nowadays hard to find, original web-site and + * sourceforge project were discontinued over the past years. + * A rendered copy of the libusb-0.1 Developers Guide was noted at: + * http://transit.iut2.upmf-grenoble.fr/doc/libusb-dev/html/index.html + * http://transit.iut2.upmf-grenoble.fr/doc/libusb-dev/html/functions.html + * Original SGML for that seems to be in source tarball such as + * http://deb.debian.org/debian/pool/main/libu/libusb/libusb_0.1.12.orig.tar.gz + * + * Related (but currently not directly used) projects include: + * - libusb-win32 port based on libusb-0.1 API (bug-fix-only mode, + * new projects should use libusb Windows backend): + * https://sourceforge.net/p/libusb-win32/wiki/Documentation/ + * - (Currently not in NUT codebase scope, but might help...) + * > A compatibility layer allowing applications written for + * > libusb-0.1 to work with libusb-1.0. libusb-compat-0.1 + * > attempts to look, feel, smell and walk like libusb-0.1. + * Mostly. Details (and known differences) documented at: + * https://github.com/libusb/libusb-compat-0.1 + * + * Also note that at least currently this does not deal with non-libusb + * APIs (important when looking for method signatures in documentation, + * since e.g. Linux Kernel USB subsystem uses some of libusb-0.1 method + * names, but with different set, type and order of arguments!) - Copyright (C) 2008 Arnaud Quette + Copyright (C) 2008 - 2016 Arnaud Quette + Copyright (C) 2021 Jim Klimov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,10 +57,389 @@ #ifndef NUT_USB_COMMON_H #define NUT_USB_COMMON_H -#include "nut_stdint.h" /* for uint16_t */ +#include "config.h" /* be sure to know all about the system config */ + +/* Note: usb-common.h (this file) is included by nut_libusb.h, + * so not looping the includes ;) + */ +#include "nut_stdint.h" /* for uint16_t, UINT16_MAX, PRIsize, etc. */ +#include "common.h" /* for fatalx() etc. */ + +#if defined HAVE_LIMITS_H +# include /* PATH_MAX for usb.h, among other stuff */ +#endif + +#if defined HAVE_SYS_PARAM_H +# include +#endif #include -#include + +#if (!WITH_LIBUSB_1_0) && (!WITH_LIBUSB_0_1) +#error "configure script error: Neither WITH_LIBUSB_1_0 nor WITH_LIBUSB_0_1 is set" +#endif + +#if (WITH_LIBUSB_1_0) && (WITH_LIBUSB_0_1) +#error "configure script error: Both WITH_LIBUSB_1_0 and WITH_LIBUSB_0_1 are set" +#endif + +/* Select version-specific libusb header file and define a sort of + * "Compatibility layer" between libusb 0.1 and 1.0 + */ +#if WITH_LIBUSB_1_0 +# include + + /* Simply remap libusb functions/structures from 0.1 to 1.0 */ + + /* Structures */ + /* #define usb_dev_handle libusb_device_handle */ + typedef libusb_device_handle usb_dev_handle; +/* These typedefs are also named in libshut.h, so we can consistenly + * handle the "ifdef SHUT_MODE" handling in libhid.c and some drivers. + * These symbolic names are used in all the headers and are expected to + * match binary code of object files at (monolithic) driver build time. + * + * The MIN/MAX definitions here are primarily to generalize range-check + * code (especially if anything is done outside the libraries). + * FIXME: It may make sense to constrain the limits to lowest common + * denominator that should fit alll of libusb-0.1, libusb-1.0 and libshut, + * so that any build of the practical (driver) code knows to not exceed + * any use-case. + */ + typedef uint8_t usb_ctrl_requesttype; + #define USB_CTRL_REQUESTTYPE_MIN 0 + #define USB_CTRL_REQUESTTYPE_MAX UINT8_MAX + + typedef uint8_t usb_ctrl_request; + #define USB_CTRL_REQUEST_MIN 0 + #define USB_CTRL_REQUEST_MAX UINT8_MAX + + typedef unsigned char usb_ctrl_endpoint; + #define USB_CTRL_ENDPOINT_MIN 0 + #define USB_CTRL_ENDPOINT_MAX UCHAR_MAX + + typedef uint16_t usb_ctrl_msgvalue; + #define USB_CTRL_MSGVALUE_MIN 0 + #define USB_CTRL_MSGVALUE_MAX UINT16_MAX + + typedef uint16_t usb_ctrl_repindex; + #define USB_CTRL_REPINDEX_MIN 0 + #define USB_CTRL_REPINDEX_MAX UINT16_MAX + + typedef uint8_t usb_ctrl_strindex; + #define USB_CTRL_STRINDEX_MIN 0 + #define USB_CTRL_STRINDEX_MAX UINT8_MAX + + typedef uint8_t usb_ctrl_descindex; + #define USB_CTRL_DESCINDEX_MIN 0 + #define USB_CTRL_DESCINDEX_MAX UINT8_MAX + + typedef unsigned char* usb_ctrl_charbuf; + typedef unsigned char usb_ctrl_char; + #define USB_CTRL_CHAR_MIN 0 + #define USB_CTRL_CHAR_MAX UCHAR_MAX + + /* Here MIN/MAX should not matter much, type mostly used for casting */ + typedef uint16_t usb_ctrl_charbufsize; + #define USB_CTRL_CHARBUFSIZE_MIN 0 + #define USB_CTRL_CHARBUFSIZE_MAX UINT16_MAX + #define PRI_NUT_USB_CTRL_CHARBUFSIZE PRIu16 + + typedef unsigned int usb_ctrl_timeout_msec; /* in milliseconds */ + /* Note: there does not seem to be a standard type + * for milliseconds, like there is an useconds_t */ + #define USB_CTRL_TIMEOUTMSEC_MIN 0 + #define USB_CTRL_TIMEOUTMSEC_MAX UINT_MAX + + /* defines */ + #define USB_CLASS_PER_INTERFACE LIBUSB_CLASS_PER_INTERFACE + #define USB_DT_STRING LIBUSB_DT_STRING + #define USB_ENDPOINT_IN LIBUSB_ENDPOINT_IN + #define USB_ENDPOINT_OUT LIBUSB_ENDPOINT_OUT + #define USB_RECIP_ENDPOINT LIBUSB_RECIPIENT_ENDPOINT + #define USB_RECIP_INTERFACE LIBUSB_RECIPIENT_INTERFACE + #define USB_REQ_SET_DESCRIPTOR LIBUSB_REQUEST_SET_DESCRIPTOR + #define USB_TYPE_CLASS LIBUSB_REQUEST_TYPE_CLASS + #define USB_TYPE_VENDOR LIBUSB_REQUEST_TYPE_VENDOR + + #define ERROR_ACCESS LIBUSB_ERROR_ACCESS + #define ERROR_BUSY LIBUSB_ERROR_BUSY + #define ERROR_IO LIBUSB_ERROR_IO + #define ERROR_NO_DEVICE LIBUSB_ERROR_NO_DEVICE + #define ERROR_NOT_FOUND LIBUSB_ERROR_NOT_FOUND + #define ERROR_OVERFLOW LIBUSB_ERROR_OVERFLOW + #define ERROR_PIPE LIBUSB_ERROR_PIPE + #define ERROR_TIMEOUT LIBUSB_ERROR_TIMEOUT + + /* Functions, including range-checks to convert data types of the two APIs. + * Follows an example from libusb-1.0 headers that liberally cast int args + * of one method to uint16_t to call another; at least we do so with checks: */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNUSED_FUNCTION) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNUSED_FUNCTION +# pragma GCC diagnostic ignored "-Wunused-function" +#endif + /* #define usb_control_msg libusb_control_transfer */ + static inline int usb_control_msg(usb_dev_handle *dev, int requesttype, + int request, int value, int index, + usb_ctrl_charbuf bytes, int size, int timeout) + { + /* + Map from libusb-0.1 API => libusb-1.0 API: + int LIBUSB_CALL libusb_control_transfer( + libusb_device_handle *dev_handle, uint8_t request_type, + uint8_t bRequest, uint16_t wValue, uint16_t wIndex, + unsigned char *data, uint16_t wLength, unsigned int timeout); + Note: In libusb-0.1 bytes was a (char*) but our consumer code + was already fixed to use "usb_ctrl_charbuf" to match other methods. + */ + + if (requesttype < 0 || (uintmax_t)requesttype > UINT8_MAX + || request < 0 || (uintmax_t)request > UINT8_MAX + || value < 0 || (uintmax_t)value > UINT16_MAX + || index < 0 || (uintmax_t)index > UINT16_MAX + || size < 0 || (uintmax_t)size > UINT16_MAX + || timeout < 0 + ) { + fatalx(EXIT_FAILURE, + "usb_control_msg() args out of range for libusb_control_transfer() implementation"); + } + + return libusb_control_transfer( + dev, + (uint8_t)requesttype, + (uint8_t)request, + (uint16_t)value, + (uint16_t)index, + (unsigned char *)bytes, + (uint16_t)size, + (unsigned int) timeout + ); + } + + static inline int usb_interrupt_read(usb_dev_handle *dev, int ep, + usb_ctrl_charbuf bytes, int size, int timeout) + { + /* NOTE: Also for routines below: + Map from libusb-0.1 API => libusb-1.0 API plus change of logic per below code: + int LIBUSB_CALL libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *actual_length, unsigned int timeout); + Note: In libusb-0.1 bytes was a (char*) but our consumer code + was already fixed to use "usb_ctrl_charbuf" to match other methods. + */ + int ret; + + if (ep < 0 || (uintmax_t)ep > UCHAR_MAX + || timeout < 0 + ) { + fatalx(EXIT_FAILURE, + "usb_interrupt_read() args out of range for libusb_interrupt_transfer() implementation"); + } + + ret = libusb_interrupt_transfer(dev, (unsigned char)ep, (unsigned char *) bytes, + size, &size, (unsigned int)timeout); + /* In case of success, return the operation size, as done with libusb 0.1 */ + return (ret == LIBUSB_SUCCESS)?size:ret; + } + + static inline int usb_interrupt_write(usb_dev_handle *dev, int ep, + const usb_ctrl_charbuf bytes, int size, int timeout) + { + /* See conversion comments above */ + int ret; + + if (ep < 0 || (uintmax_t)ep > UCHAR_MAX + || timeout < 0 + ) { + fatalx(EXIT_FAILURE, + "usb_interrupt_write() args out of range for libusb_interrupt_transfer() implementation"); + } + + ret = libusb_interrupt_transfer(dev, (unsigned char)ep, (unsigned char *) bytes, + size, &size, (unsigned int)timeout); + /* In case of success, return the operation size, as done with libusb 0.1 */ + return (ret == LIBUSB_SUCCESS)?size:ret; + } + + static inline int usb_bulk_read(usb_dev_handle *dev, int ep, + usb_ctrl_charbuf bytes, int size, int timeout) + { + /* See conversion comments above */ + int ret; + + if (ep < 0 || (uintmax_t)ep > UCHAR_MAX + || timeout < 0 + ) { + fatalx(EXIT_FAILURE, + "usb_bulk_read() args out of range for libusb_interrupt_transfer() implementation"); + } + + ret = libusb_interrupt_transfer(dev, (unsigned char)ep, (unsigned char *) bytes, + size, &size, (unsigned int)timeout); + /* In case of success, return the operation size, as done with libusb 0.1 */ + return (ret == LIBUSB_SUCCESS)?size:ret; + } + + static inline int usb_bulk_write(usb_dev_handle *dev, int ep, + usb_ctrl_charbuf bytes, int size, int timeout) + { + /* See conversion comments above */ + int ret; + + if (ep < 0 || (uintmax_t)ep > UCHAR_MAX + || timeout < 0 + ) { + fatalx(EXIT_FAILURE, + "usb_bulk_write() args out of range for libusb_interrupt_transfer() implementation"); + } + + ret = libusb_interrupt_transfer(dev, (unsigned char)ep, (unsigned char *) bytes, + size, &size, (unsigned int)timeout); + /* In case of success, return the operation size, as done with libusb 0.1 */ + return (ret == LIBUSB_SUCCESS)?size:ret; + } + + static inline int usb_get_string(usb_dev_handle *dev, int index, int langid, + usb_ctrl_charbuf buf, size_t buflen) + { + /* + Map from libusb-0.1 API (originally "char* buf") => libusb-1.0 API: + int libusb_get_string_descriptor(libusb_device_handle *dev_handle, + uint8_t desc_index, uint16_t langid, unsigned char *data, int length) + */ + + if (index < 0 || (uintmax_t)index > UINT8_MAX + || langid < 0 || (uintmax_t)langid > UINT16_MAX + || (uintmax_t)buflen > INT_MAX + ) { + fatalx(EXIT_FAILURE, + "usb_get_string() args out of range for libusb_get_string_descriptor() implementation"); + } + + return libusb_get_string_descriptor( + dev, + (uint8_t)index, + (uint16_t)langid, + (unsigned char *)buf, + (int) buflen + ); + + } + + static inline int usb_get_string_simple(usb_dev_handle *dev, int index, + usb_ctrl_charbuf buf, size_t buflen) + { + /* + Map from libusb-0.1 API (originally "char* buf") => libusb-1.0 API: + int LIBUSB_CALL libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle, + uint8_t desc_index, unsigned char *data, int length); + */ + + if (index < 0 || (uintmax_t)index > UINT8_MAX + || (uintmax_t)buflen > INT_MAX + ) { + fatalx(EXIT_FAILURE, + "usb_get_string_simple() args out of range for libusb_get_string_descriptor_ascii() implementation"); + } + + return libusb_get_string_descriptor_ascii( + dev, + (uint8_t)index, + (unsigned char *)buf, + (int) buflen + ); + } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNUSED_FUNCTION) +# pragma GCC diagnostic pop +#endif + + /* Functions for which simple mappings seem to suffice (no build warnings emitted): */ + #define usb_claim_interface libusb_claim_interface + #define usb_clear_halt libusb_clear_halt + #define usb_close libusb_close + #define usb_set_configuration libusb_set_configuration + #define usb_release_interface libusb_release_interface + #define usb_reset libusb_reset_device + + /* FIXME: some original libusb1.c code cast the (int) argument + * as (enum libusb_error) - should we force that in the macro? */ + #define nut_usb_strerror(a) libusb_strerror(a) +#endif /* WITH_LIBUSB_1_0 */ + +/* Note: Checked above that in practice we handle some one libusb API */ +#if WITH_LIBUSB_0_1 +# include + /* Structures */ + /* See detailed comments above, in libusb-1.0 definitions + * FIXME: It may make sense to constrain the limits to lowest common + * denominator that should fit alll of libusb-0.1, libusb-1.0 and libshut, + * so that any build of the practical (driver) code knows to not exceed + * any use-case. + */ + /* no typedef for usb_dev_handle - part of libusb-0.1 API names */ + + typedef int usb_ctrl_requesttype; + #define USB_CTRL_REQUESTTYPE_MIN INT_MIN + #define USB_CTRL_REQUESTTYPE_MAX INT_MAX + + typedef int usb_ctrl_request; + #define USB_CTRL_REQUEST_MIN INT_MIN + #define USB_CTRL_REQUEST_MAX INT_MAX + + typedef int usb_ctrl_endpoint; + #define USB_CTRL_ENDPOINT_MIN INT_MIN + #define USB_CTRL_ENDPOINT_MAX INT_MAX + + typedef int usb_ctrl_msgvalue; + #define USB_CTRL_MSGVALUE_MIN INT_MIN + #define USB_CTRL_MSGVALUE_MAX INT_MAX + + typedef int usb_ctrl_repindex; + #define USB_CTRL_REPINDEX_MIN INT_MIN + #define USB_CTRL_REPINDEX_MAX INT_MAX + + typedef int usb_ctrl_strindex; + #define USB_CTRL_STRINDEX_MIN INT_MIN + #define USB_CTRL_STRINDEX_MAX INT_MAX + + typedef int usb_ctrl_descindex; + #define USB_CTRL_DESCINDEX_MIN INT_MIN + #define USB_CTRL_DESCINDEX_MAX INT_MAX + + /* Here MIN/MAX should not matter much, type mostly used for casting */ + typedef char* usb_ctrl_charbuf; + typedef char usb_ctrl_char; + #define USB_CTRL_CHAR_MIN CHAR_MIN + #define USB_CTRL_CHAR_MAX CHAR_MAX + + typedef int usb_ctrl_charbufsize; + #define USB_CTRL_CHARBUFSIZE_MIN INT_MIN + #define USB_CTRL_CHARBUFSIZE_MAX INT_MAX + /* There is no PRIi :) So we define directly by spec */ + #define PRI_NUT_USB_CTRL_CHARBUFSIZE "i" + + typedef int usb_ctrl_timeout_msec; /* in milliseconds */ + #define USB_CTRL_TIMEOUTMSEC_MIN INT_MIN + #define USB_CTRL_TIMEOUTMSEC_MAX INT_MAX + + /* defines */ + #define ERROR_ACCESS -EACCES + #define ERROR_BUSY -EBUSY + #define ERROR_IO -EIO + #define ERROR_NO_DEVICE -ENODEV + #define ERROR_NOT_FOUND -ENOENT + #define ERROR_OVERFLOW -EOVERFLOW + #define ERROR_PIPE -EPIPE + #define ERROR_TIMEOUT -ETIMEDOUT + + /* Functions for which simple mappings seem to suffice (no build warnings emitted): */ + #define nut_usb_strerror(a) usb_strerror() +#endif /* WITH_LIBUSB_0_1 */ + +/* USB standard timeout [ms] */ +#define USB_TIMEOUT 5000 /*! * USBDevice_t: Describe a USB device. This structure contains exactly @@ -35,12 +451,17 @@ * corresponding string did not exist or could not be retrieved. */ typedef struct USBDevice_s { - uint16_t VendorID; /*!< Device's Vendor ID */ - uint16_t ProductID; /*!< Device's Product ID */ - char *Vendor; /*!< Device's Vendor Name */ - char *Product; /*!< Device's Product Name */ - char *Serial; /* Product serial number */ - char *Bus; /* Bus name, e.g. "003" */ + /* These 5 data points are common properties of an USB device: */ + uint16_t VendorID; /*!< Device's Vendor ID */ + uint16_t ProductID; /*!< Device's Product ID */ + char *Vendor; /*!< Device's Vendor Name */ + char *Product; /*!< Device's Product Name */ + char *Serial; /*!< Product serial number */ + /* These data points can be determined by the driver for some devices + or by libusb to detail its connection topology: */ + char *Bus; /*!< Bus name, e.g. "003" */ + uint16_t bcdDevice; /*!< Device release number */ + char *Device; /*!< Device name on the bus, e.g. "001" */ } USBDevice_t; /*! @@ -75,9 +496,9 @@ void USBFreeRegexMatcher(USBDeviceMatcher_t *matcher); #define USB_DEVICE(vendorID, productID) vendorID, productID typedef struct { - int vendorID; - int productID; - void *(*fun)(void); /* handler for specific processing */ + uint16_t vendorID; + uint16_t productID; + void *(*fun)(USBDevice_t *); /* handler for specific processing */ } usb_device_id_t; #define NOT_SUPPORTED 0 @@ -87,7 +508,14 @@ typedef struct { /* Function used to match a VendorID/ProductID pair against a list of * supported devices. Return values: * NOT_SUPPORTED (0), POSSIBLY_SUPPORTED (1) or SUPPORTED (2) */ -int is_usb_device_supported(usb_device_id_t *usb_device_id_list, - int dev_VendorID, int dev_ProductID); +int is_usb_device_supported(usb_device_id_t *usb_device_id_list, + USBDevice_t *device); + +void nut_usb_addvars(void); + +/* Tell the users that port="auto" should be used for USB, + * and other values are quietly ignored. Implemented once + * here, to use in several USB-capable drivers. */ +void warn_if_bad_usb_port_filename(const char *fn); #endif /* NUT_USB_COMMON_H */ diff --git a/drivers/usbhid-ups.c b/drivers/usbhid-ups.c index c925a81..dfe65a3 100644 --- a/drivers/usbhid-ups.c +++ b/drivers/usbhid-ups.c @@ -1,10 +1,11 @@ /* usbhid-ups.c - Driver for USB and serial (MGE SHUT) HID UPS units * * Copyright (C) - * 2003-2009 Arnaud Quette + * 2003-2022 Arnaud Quette * 2005 John Stamp * 2005-2006 Peter Selinger * 2007-2009 Arjen de Korte + * 2016 Eaton / Arnaud Quette * * This program was sponsored by MGE UPS SYSTEMS, and now Eaton * @@ -27,7 +28,7 @@ */ #define DRIVER_NAME "Generic HID driver" -#define DRIVER_VERSION "0.35" +#define DRIVER_VERSION "0.47" #include "main.h" #include "libhid.h" @@ -37,18 +38,27 @@ /* include all known subdrivers */ #include "mge-hid.h" + #ifndef SHUT_MODE + /* explore stub goes first, others alphabetically */ #include "explore-hid.h" #include "apc-hid.h" + #include "arduino-hid.h" #include "belkin-hid.h" #include "cps-hid.h" - #include "liebert-hid.h" - #include "powercom-hid.h" - #include "tripplite-hid.h" + #include "delta_ups-hid.h" + #include "ever-hid.h" #include "idowell-hid.h" + #include "legrand-hid.h" + #include "liebert-hid.h" + #include "openups-hid.h" + #include "powercom-hid.h" + #include "powervar-hid.h" + #include "salicru-hid.h" + #include "tripplite-hid.h" #endif -/* master list of avaiable subdrivers */ +/* Reference list of avaiable subdrivers */ static subdriver_t *subdriver_list[] = { #ifndef SHUT_MODE &explore_subdriver, @@ -56,12 +66,19 @@ static subdriver_t *subdriver_list[] = { &mge_subdriver, #ifndef SHUT_MODE &apc_subdriver, + &arduino_subdriver, &belkin_subdriver, &cps_subdriver, - &liebert_subdriver, - &powercom_subdriver, - &tripplite_subdriver, + &delta_ups_subdriver, + &ever_subdriver, &idowell_subdriver, + &legrand_subdriver, + &liebert_subdriver, + &openups_subdriver, + &powercom_subdriver, + &powervar_subdriver, + &salicru_subdriver, + &tripplite_subdriver, #endif NULL }; @@ -89,19 +106,40 @@ typedef enum { HU_WALKMODE_FULL_UPDATE } walkmode_t; +/* Compatibility layer between libusb 0.1 and 1.0, for errno/return codes */ +#if WITH_LIBUSB_0_1 || (defined SHUT_MODE) + #define ERROR_BUSY -EBUSY + #define ERROR_NO_DEVICE -ENODEV + #define ERROR_ACCESS -EACCES + #define ERROR_IO -EIO + #define ERROR_NOT_FOUND -ENOENT + #define ERROR_TIMEOUT -ETIMEDOUT + #define ERROR_OVERFLOW -EOVERFLOW + #define ERROR_PIPE -EPIPE +#else /* for libusb 1.0 */ + #define ERROR_BUSY LIBUSB_ERROR_BUSY + #define ERROR_NO_DEVICE LIBUSB_ERROR_NO_DEVICE + #define ERROR_ACCESS LIBUSB_ERROR_ACCESS + #define ERROR_IO LIBUSB_ERROR_IO + #define ERROR_NOT_FOUND LIBUSB_ERROR_NOT_FOUND + #define ERROR_TIMEOUT LIBUSB_ERROR_TIMEOUT + #define ERROR_OVERFLOW LIBUSB_ERROR_OVERFLOW + #define ERROR_PIPE LIBUSB_ERROR_PIPE +#endif + /* pointer to the active subdriver object (changed in callback() function) */ static subdriver_t *subdriver = NULL; /* Global vars */ static HIDDevice_t *hd = NULL; -static HIDDevice_t curDevice = { 0x0000, 0x0000, NULL, NULL, NULL, NULL }; +static HIDDevice_t curDevice = { 0x0000, 0x0000, NULL, NULL, NULL, NULL, 0, NULL }; static HIDDeviceMatcher_t *subdriver_matcher = NULL; #ifndef SHUT_MODE static HIDDeviceMatcher_t *exact_matcher = NULL; static HIDDeviceMatcher_t *regex_matcher = NULL; #endif static int pollfreq = DEFAULT_POLLFREQ; -static int ups_status = 0; +static unsigned ups_status = 0; static bool_t data_has_changed = FALSE; /* for SEMI_STATIC data polling */ #ifndef SUN_LIBUSB bool_t use_interrupt_pipe = TRUE; @@ -109,7 +147,14 @@ bool_t use_interrupt_pipe = TRUE; bool_t use_interrupt_pipe = FALSE; #endif static time_t lastpoll; /* Timestamp the last polling */ -hid_dev_handle_t udev; +hid_dev_handle_t udev = HID_DEV_HANDLE_CLOSED; + +/** + * CyberPower UT series sometime need a bit of help deciding their online status. + * This quirk is to enable the special handling of OL & DISCHRG at the same time + * as being OB (on battery power/no mains power). Enabled by device config flag. + */ +static int onlinedischarge = 0; /* support functions */ static hid_info_t *find_nut_info(const char *varname); @@ -122,7 +167,8 @@ static void ups_status_set(void); static bool_t hid_ups_walk(walkmode_t mode); static int reconnect_ups(void); static int ups_infoval_set(hid_info_t *item, double value); -static int callback(hid_dev_handle_t udev, HIDDevice_t *hd, unsigned char *rdbuf, int rdlen); +static int callback(hid_dev_handle_t argudev, HIDDevice_t *arghd, + usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen); #ifdef DEBUG static double interval(void); #endif @@ -131,40 +177,6 @@ static double interval(void); HIDDesc_t *pDesc = NULL; /* parsed Report Descriptor */ reportbuf_t *reportbuf = NULL; /* buffer for most recent reports */ -/* ---------------------------------------------------------------------- */ -/* data for processing boolean values from UPS */ - -#define STATUS(x) ((unsigned)1<> 9); /* negative value represents pre-1980 date */ + /* TOTHINK: About the comment below... + * Does bit-shift keep the negativeness on all architectures? + */ + /* negative value represents pre-1980 date: */ + year = 1980 + ((long)value >> 9); month = ((long)value >> 5) & 0x0f; day = (long)value & 0x1f; - snprintf(buf, sizeof(buf), "%04d/%02d/%02d", year, month, day); + snprintf(buf, sizeof(buf), "%04ld/%02ld/%02ld", year, month, day); return buf; } +static double date_conversion_reverse(const char* date_string) +{ + long year, month, day; + long date; + + sscanf(date_string, "%04ld/%02ld/%02ld", &year, &month, &day); + if(year - 1980 > 127 || month > 12 || day > 31) + return 0; + date = (year - 1980) << 9; + date += month << 5; + date += day; + + return (double) date; +} + info_lkp_t date_conversion[] = { - { 0, NULL, date_conversion_fun } + { 0, NULL, date_conversion_fun, date_conversion_reverse } }; /* returns statically allocated string - must not use it again before @@ -442,8 +473,9 @@ static const char *hex_conversion_fun(double value) return buf; } +/* FIXME? Do we need an inverse "nuf()" here? */ info_lkp_t hex_conversion[] = { - { 0, NULL, hex_conversion_fun } + { 0, NULL, hex_conversion_fun, NULL } }; /* returns statically allocated string - must not use it again before @@ -455,8 +487,9 @@ static const char *stringid_conversion_fun(double value) return HIDGetIndexString(udev, (int)value, buf, sizeof(buf)); } +/* FIXME? Do we need an inverse "nuf()" here? */ info_lkp_t stringid_conversion[] = { - { 0, NULL, stringid_conversion_fun } + { 0, NULL, stringid_conversion_fun, NULL } }; /* returns statically allocated string - must not use it again before @@ -470,8 +503,9 @@ static const char *divide_by_10_conversion_fun(double value) return buf; } +/* FIXME? Do we need an inverse "nuf()" here? */ info_lkp_t divide_by_10_conversion[] = { - { 0, NULL, divide_by_10_conversion_fun } + { 0, NULL, divide_by_10_conversion_fun, NULL } }; /* returns statically allocated string - must not use it again before @@ -480,13 +514,24 @@ static const char *kelvin_celsius_conversion_fun(double value) { static char buf[20]; - snprintf(buf, sizeof(buf), "%.1f", value - 273.15); + /* check if the value is in the Kelvin range, to + * detect buggy value (already expressed in °C), as found + * on some HP implementation */ + if ((value >= 273) && (value <= 373)) { + /* the value is indeed in °K */ + snprintf(buf, sizeof(buf), "%.1f", value - 273.15); + } + else { + /* else, this is actually °C, not °K! */ + snprintf(buf, sizeof(buf), "%.1f", value); + } return buf; } +/* FIXME? Do we need an inverse "nuf()" here? */ info_lkp_t kelvin_celsius_conversion[] = { - { 0, NULL, kelvin_celsius_conversion_fun } + { 0, NULL, kelvin_celsius_conversion_fun, NULL } }; /*! @@ -497,12 +542,19 @@ info_lkp_t kelvin_celsius_conversion[] = { #ifndef SHUT_MODE static int match_function_subdriver(HIDDevice_t *d, void *privdata) { int i; + NUT_UNUSED_VARIABLE(privdata); + + upsdebugx(2, "%s (non-SHUT mode): matching a device...", __func__); for (i=0; subdriver_list[i] != NULL; i++) { if (subdriver_list[i]->claim(d)) { return 1; } } + + upsdebugx(2, "%s (non-SHUT mode): failed to match a subdriver " + "to vendor and/or product ID", + __func__); return 0; } @@ -526,24 +578,31 @@ int instcmd(const char *cmdname, const char *extradata) if (!strcasecmp(cmdname, "beeper.off")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, - "The 'beeper.off' command has been renamed to 'beeper.disable'"); + "The 'beeper.off' command has been " + "renamed to 'beeper.disable'"); return instcmd("beeper.disable", NULL); } if (!strcasecmp(cmdname, "beeper.on")) { /* compatibility mode for old command */ upslogx(LOG_WARNING, - "The 'beeper.on' command has been renamed to 'beeper.enable'"); + "The 'beeper.on' command has been " + "renamed to 'beeper.enable'"); return instcmd("beeper.enable", NULL); } - upsdebugx(1, "instcmd(%s, %s)", cmdname, extradata ? extradata : "[NULL]"); + upsdebugx(1, "instcmd(%s, %s)", + cmdname, + extradata ? extradata : "[NULL]"); /* Retrieve and check netvar & item_path */ hidups_item = find_nut_info(cmdname); /* Check for fallback if not found */ if (hidups_item == NULL) { + upsdebugx(3, "%s: cmdname '%s' not found; " + "checking for alternatives", + __func__, cmdname); if (!strcasecmp(cmdname, "load.on")) { return instcmd("load.on.delay", "0"); @@ -556,22 +615,43 @@ int instcmd(const char *cmdname, const char *extradata) if (!strcasecmp(cmdname, "shutdown.return")) { int ret; + /* Ensure "ups.start.auto" is set to "yes", + * if supported */ + if (dstate_getinfo("ups.start.auto")) { + setvar("ups.start.auto", "yes"); + } + ret = instcmd("load.on.delay", dstate_getinfo("ups.delay.start")); if (ret != STAT_INSTCMD_HANDLED) { return ret; } + /* Some UPS's (e.g. TrippLive AVR750U w/ 3024 protocol) don't accept + * commands that arrive too rapidly, so add this arbitary wait, + * which has proven to be long enough to avoid this problem in practice */ + usleep(125000); + return instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown")); } if (!strcasecmp(cmdname, "shutdown.stayoff")) { int ret; + /* Ensure "ups.start.auto" is set to "no", if supported */ + if (dstate_getinfo("ups.start.auto")) { + setvar("ups.start.auto", "no"); + } + ret = instcmd("load.on.delay", "-1"); if (ret != STAT_INSTCMD_HANDLED) { return ret; } + /* Some UPS's (e.g. TrippLive AVR750U w/ 3024 protocol) don't accept + * commands that arrive too rapidly, so add this arbitary wait, + * which has proven to be long enough to avoid this problem in practice */ + usleep(125000); + return instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown")); } @@ -579,6 +659,11 @@ int instcmd(const char *cmdname, const char *extradata) return STAT_INSTCMD_INVALID; } + upsdebugx(3, "%s: using Path '%s'", + __func__, + (hidups_item->hidpath ? hidups_item->hidpath : "[NULL]") + ); + /* Check if the item is an instant command */ if (!(hidups_item->hidflags & HU_TYPE_CMD)) { upsdebugx(2, "instcmd: %s is not an instant command\n", cmdname); @@ -597,7 +682,7 @@ int instcmd(const char *cmdname, const char *extradata) /* Actual variable setting */ if (HIDSetDataValue(udev, hidups_item->hiddata, value) == 1) { - upsdebugx(5, "instcmd: SUCCEED\n"); + upsdebugx(3, "instcmd: SUCCEED\n"); /* Set the status so that SEMI_STATIC vars are polled */ data_has_changed = TRUE; return STAT_INSTCMD_HANDLED; @@ -697,27 +782,46 @@ void upsdrv_makevartable(void) upsdebugx(1, "upsdrv_makevartable..."); - snprintf(temp, sizeof(temp), "Set shutdown delay, in seconds (default=%s)", DEFAULT_OFFDELAY); + snprintf(temp, sizeof(temp), + "Set low battery level, in %% (default=%s)", + DEFAULT_LOWBATT); + addvar (VAR_VALUE, HU_VAR_LOWBATT, temp); + + snprintf(temp, sizeof(temp), + "Set shutdown delay, in seconds (default=%s)", + DEFAULT_OFFDELAY); addvar(VAR_VALUE, HU_VAR_OFFDELAY, temp); - snprintf(temp, sizeof(temp), "Set startup delay, in seconds (default=%s)", DEFAULT_ONDELAY); + snprintf(temp, sizeof(temp), + "Set startup delay, in seconds (default=%s)", + DEFAULT_ONDELAY); addvar(VAR_VALUE, HU_VAR_ONDELAY, temp); - snprintf(temp, sizeof(temp), "Set polling frequency, in seconds, to reduce data flow (default=%d)", + snprintf(temp, sizeof(temp), + "Set polling frequency, in seconds, to reduce data flow (default=%d)", DEFAULT_POLLFREQ); addvar(VAR_VALUE, HU_VAR_POLLFREQ, temp); addvar(VAR_FLAG, "pollonly", "Don't use interrupt pipe, only use polling"); + addvar(VAR_FLAG, "onlinedischarge", + "Set to treat discharging while online as being offline"); + #ifndef SHUT_MODE /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */ - addvar(VAR_VALUE, "vendor", "Regular expression to match UPS Manufacturer string"); - addvar(VAR_VALUE, "product", "Regular expression to match UPS Product string"); - addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial number"); - addvar(VAR_VALUE, "vendorid", "Regular expression to match UPS Manufacturer numerical ID (4 digits hexadecimal)"); - addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)"); - addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name"); - addvar(VAR_FLAG, "explore", "Diagnostic matching of unsupported UPS"); + nut_usb_addvars(); + + addvar(VAR_FLAG, "explore", + "Diagnostic matching of unsupported UPS"); + addvar(VAR_FLAG, "maxreport", + "Activate tweak for buggy APC Back-UPS firmware"); + addvar(VAR_FLAG, "interruptonly", + "Don't use polling, only use interrupt pipe"); + addvar(VAR_VALUE, "interruptsize", + "Number of bytes to read from interrupt pipe"); +#else + addvar(VAR_VALUE, "notification", + "Set notification type, (ignored, only for backward compatibility)"); #endif } @@ -726,7 +830,7 @@ void upsdrv_makevartable(void) void upsdrv_updateinfo(void) { hid_info_t *item; - HIDData_t *event[MAX_EVENT_NUM]; + HIDData_t *event[MAX_EVENT_NUM], *found_data; int i, evtCount; double value; time_t now; @@ -738,7 +842,7 @@ void upsdrv_updateinfo(void) /* check for device availability to set datastale! */ if (hd == NULL) { /* don't flood reconnection attempts */ - if (now < (int)(lastpoll + poll_interval)) { + if (now < (lastpoll + poll_interval)) { return; } @@ -763,7 +867,29 @@ void upsdrv_updateinfo(void) /* Get HID notifications on Interrupt pipe first */ if (use_interrupt_pipe == TRUE) { evtCount = HIDGetEvents(udev, event, MAX_EVENT_NUM); - upsdebugx(1, "Got %i HID objects...", (evtCount >= 0) ? evtCount : 0); + switch (evtCount) + { + case ERROR_BUSY: /* Device or resource busy */ + upslog_with_errno(LOG_CRIT, "Got disconnected by another driver"); + goto fallthrough_reconnect; +#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ + case -EPERM: /* Operation not permitted */ +#endif + case ERROR_NO_DEVICE: /* No such device */ + case ERROR_ACCESS: /* Permission denied */ + case ERROR_IO: /* I/O error */ +#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ + case -ENXIO: /* No such device or address */ +#endif + case ERROR_NOT_FOUND: /* No such file or directory */ + fallthrough_reconnect: + /* Uh oh, got to reconnect! */ + hd = NULL; + return; + default: + upsdebugx(1, "Got %i HID objects...", (evtCount >= 0) ? evtCount : 0); + break; + } } else { evtCount = 0; upsdebugx(1, "Not using interrupt pipe..."); @@ -776,14 +902,24 @@ void upsdrv_updateinfo(void) continue; if (nut_debug_level >= 2) { - upsdebugx(2, "Path: %s, Type: %s, ReportID: 0x%02x, Offset: %i, Size: %i, Value: %g", + upsdebugx(2, + "Path: %s, Type: %s, ReportID: 0x%02x, " + "Offset: %i, Size: %i, Value: %g", HIDGetDataItem(event[i], subdriver->utab), HIDDataType(event[i]), event[i]->ReportID, event[i]->Offset, event[i]->Size, value); } /* Skip Input reports, if we don't use the Feature report */ - item = find_hid_info(FindObject_with_Path(pDesc, &(event[i]->Path), ITEM_FEATURE)); + found_data = FindObject_with_Path(pDesc, &(event[i]->Path), interrupt_only ? ITEM_INPUT:ITEM_FEATURE); + if(!found_data && !interrupt_only) { + found_data = FindObject_with_Path(pDesc, &(event[i]->Path), ITEM_INPUT); + } + if(!found_data) { + upsdebugx(2, "Could not find event as either ITEM_INPUT or ITEM_FEATURE?"); + continue; + } + item = find_hid_info(found_data); if (!item) { upsdebugx(3, "NUT doesn't use this HID object"); continue; @@ -792,12 +928,14 @@ void upsdrv_updateinfo(void) ups_infoval_set(item, value); } #ifdef DEBUG - upsdebugx(1, "took %.3f seconds handling interrupt reports...\n", interval()); + upsdebugx(1, "took %.3f seconds handling interrupt reports...\n", + interval()); #endif /* clear status buffer before begining */ status_init(); - /* Do a full update (polling) every pollfreq or upon data change (ie setvar/instcmd) */ + /* Do a full update (polling) every pollfreq + * or upon data change (ie setvar/instcmd) */ if ((now > (lastpoll + pollfreq)) || (data_has_changed == TRUE)) { upsdebugx(1, "Full update..."); @@ -824,7 +962,8 @@ void upsdrv_updateinfo(void) dstate_dataok(); #ifdef DEBUG - upsdebugx(1, "took %.3f seconds handling feature reports...\n", interval()); + upsdebugx(1, "took %.3f seconds handling feature reports...\n", + interval()); #endif } @@ -835,7 +974,6 @@ void upsdrv_initinfo(void) upsdebugx(1, "upsdrv_initinfo..."); dstate_setinfo("driver.version.data", "%s", subdriver->name); - dstate_setinfo("driver.version.internal", DRIVER_VERSION); /* init polling frequency */ val = getval(HU_VAR_POLLFREQ); @@ -861,18 +999,25 @@ void upsdrv_initups(void) { int ret; char *val; + + upsdebugx(2, "Initializing an USB-connected UPS with library %s " \ + "(NUT subdriver name='%s' ver='%s')", + dstate_getinfo("driver.version.usb"), + comm_driver->name, comm_driver->version ); + #ifdef SHUT_MODE /*! * SHUT is a serial protocol, so it needs * only the device path */ - upsdebugx(1, "upsdrv_initups..."); + upsdebugx(1, "upsdrv_initups (SHUT)..."); subdriver_matcher = device_path; #else - char *regex_array[6]; + char *regex_array[7]; - upsdebugx(1, "upsdrv_initups..."); + upsdebugx(1, "upsdrv_initups (non-SHUT)..."); + warn_if_bad_usb_port_filename(device_path); subdriver_matcher = &subdriver_matcher_struct; @@ -881,6 +1026,11 @@ void upsdrv_initups(void) fatalx(EXIT_FAILURE, "must specify \"vendorid\" when using \"explore\""); } + /* Activate maxreport tweak */ + if (testvar("maxreport")) { + max_report_size = 1; + } + /* process the UPS selection options */ regex_array[0] = getval("vendorid"); regex_array[1] = getval("productid"); @@ -888,6 +1038,7 @@ void upsdrv_initups(void) regex_array[3] = getval("product"); regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); + regex_array[6] = getval("device"); ret = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED); switch(ret) @@ -896,8 +1047,18 @@ void upsdrv_initups(void) break; case -1: fatal_with_errno(EXIT_FAILURE, "HIDNewRegexMatcher()"); +#ifndef HAVE___ATTRIBUTE__NORETURN + exit(EXIT_FAILURE); + /* Should not get here in practice, but + * compiler is afraid we can fall through */ +#endif default: fatalx(EXIT_FAILURE, "invalid regular expression: %s", regex_array[ret]); +#ifndef HAVE___ATTRIBUTE__NORETURN + exit(EXIT_FAILURE); + /* Should not get here in practice, but + * compiler is afraid we can fall through */ +#endif } /* link the matchers */ @@ -912,13 +1073,42 @@ void upsdrv_initups(void) hd = &curDevice; - upsdebugx(1, "Detected a UPS: %s/%s", hd->Vendor ? hd->Vendor : "unknown", + upsdebugx(1, "Detected a UPS: %s/%s", + hd->Vendor ? hd->Vendor : "unknown", hd->Product ? hd->Product : "unknown"); + /* Activate Powercom tweaks */ + if (testvar("interruptonly")) { + interrupt_only = 1; + } + + /* Activate Cyberpower tweaks */ + if (testvar("onlinedischarge")) { + onlinedischarge = 1; + } + + val = getval("interruptsize"); + if (val) { + int ipv = atoi(val); + if (ipv > 0) { + interrupt_size = (unsigned int)ipv; + } else { + fatalx(EXIT_FAILURE, "Error: invalid interruptsize: %d", ipv); + } + } + if (hid_ups_walk(HU_WALKMODE_INIT) == FALSE) { fatalx(EXIT_FAILURE, "Can't initialize data from HID UPS"); } + if (dstate_getinfo("battery.charge.low")) { + /* Retrieve user defined battery settings */ + val = getval(HU_VAR_LOWBATT); + if (val) { + dstate_setinfo("battery.charge.low", "%ld", strtol(val, NULL, 10)); + } + } + if (dstate_getinfo("ups.delay.start")) { /* Retrieve user defined delay settings */ val = getval(HU_VAR_ONDELAY); @@ -967,6 +1157,7 @@ void upsdrv_cleanup(void) free(curDevice.Product); free(curDevice.Serial); free(curDevice.Bus); + free(curDevice.Device); #endif } @@ -974,7 +1165,7 @@ void upsdrv_cleanup(void) * Support functions *********************************************************************/ -void possibly_supported(const char *mfr, HIDDevice_t *hd) +void possibly_supported(const char *mfr, HIDDevice_t *arghd) { upsdebugx(0, "This %s device (%04x:%04x) is not (or perhaps not yet) supported\n" @@ -982,7 +1173,7 @@ void possibly_supported(const char *mfr, HIDDevice_t *hd) "this does not fix the problem, try running the driver with the\n" "'-x productid=%04x' option. Please report your results to the NUT user's\n" "mailing list .\n", - mfr, hd->VendorID, hd->ProductID, hd->ProductID); + mfr, arghd->VendorID, arghd->ProductID, arghd->ProductID); } /* Update ups_status to remember this status item. Interpretation is @@ -1016,15 +1207,41 @@ static void process_boolean_info(const char *nutvalue) upsdebugx(5, "Warning: %s not in list of known values", nutvalue); } -static int callback(hid_dev_handle_t udev, HIDDevice_t *hd, unsigned char *rdbuf, int rdlen) +static int callback( + hid_dev_handle_t argudev, + HIDDevice_t *arghd, + usb_ctrl_charbuf rdbuf, + usb_ctrl_charbufsize rdlen) { int i; const char *mfr = NULL, *model = NULL, *serial = NULL; #ifndef SHUT_MODE int ret; #endif - upsdebugx(2, "Report Descriptor size = %d", rdlen); - upsdebug_hex(3, "Report Descriptor", rdbuf, rdlen); + upsdebugx(2, "Report Descriptor size = %" PRI_NUT_USB_CTRL_CHARBUFSIZE, rdlen); + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +#endif + if ((uintmax_t)rdlen < (uintmax_t)SIZE_MAX) { + upsdebug_hex(3, "Report Descriptor", rdbuf, (size_t)rdlen); + } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) ) +# pragma GCC diagnostic pop +#endif + + /* Save the global "hd" for this driver instance */ + hd = arghd; + udev = argudev; /* Parse Report Descriptor */ Free_ReportDesc(pDesc); @@ -1058,7 +1275,10 @@ static int callback(hid_dev_handle_t udev, HIDDevice_t *hd, unsigned char *rdbuf upslogx(2, "Using subdriver: %s", subdriver->name); - HIDDumpTree(udev, subdriver->utab); + if (subdriver->fix_report_desc(arghd, pDesc)) { + upsdebugx(2, "Report Descriptor Fixed"); + } + HIDDumpTree(udev, arghd, subdriver->utab); #ifndef SHUT_MODE /* create a new matcher for later matching */ @@ -1117,6 +1337,15 @@ static double interval(void) } #endif +/* default subdriver function which doesn't attempt to fix + * any issues in the parsed HID Report Descriptor */ +int fix_report_desc(HIDDevice_t *arg_pDev, HIDDesc_t *arg_pDesc) { + NUT_UNUSED_VARIABLE(arg_pDev); + NUT_UNUSED_VARIABLE(arg_pDesc); + + return 0; +} + /* walk ups variables and set elements of the info array. */ static bool_t hid_ups_walk(walkmode_t mode) { @@ -1124,7 +1353,14 @@ static bool_t hid_ups_walk(walkmode_t mode) double value; int retcode; - /* 3 modes: HU_WALKMODE_INIT, HU_WALKMODE_QUICK_UPDATE and HU_WALKMODE_FULL_UPDATE */ +#ifndef SHUT_MODE + /* extract the VendorId for further testing */ + int vendorID = curDevice.VendorID; + int productID = curDevice.ProductID; +#endif + + /* 3 modes: HU_WALKMODE_INIT, HU_WALKMODE_QUICK_UPDATE + * and HU_WALKMODE_FULL_UPDATE */ /* Device data walk ----------------------------- */ for (item = subdriver->hid2nut; item->info_type != NULL; item++) { @@ -1200,22 +1436,62 @@ static bool_t hid_ups_walk(walkmode_t mode) break; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# pragma clang diagnostic ignored "-Wunreachable-code" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: fatalx(EXIT_FAILURE, "hid_ups_walk: unknown update mode!"); } +#ifdef __clang__ +# pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif + +#ifndef SHUT_MODE + /* skip report 0x54 for Tripplite SU3000LCD2UHV due to firmware bug */ + if ((vendorID == 0x09ae) && (productID == 0x1330)) { + if (item->hiddata && (item->hiddata->ReportID == 0x54)) { + continue; + } + } +#endif retcode = HIDGetDataValue(udev, item->hiddata, &value, poll_interval); switch (retcode) { - case -EBUSY: /* Device or resource busy */ + case ERROR_BUSY: /* Device or resource busy */ upslog_with_errno(LOG_CRIT, "Got disconnected by another driver"); + goto fallthrough_reconnect; +#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ case -EPERM: /* Operation not permitted */ - case -ENODEV: /* No such device */ - case -EACCES: /* Permission denied */ - case -EIO: /* I/O error */ +#endif + case ERROR_NO_DEVICE: /* No such device */ + case ERROR_ACCESS: /* Permission denied */ + case ERROR_IO: /* I/O error */ +#if WITH_LIBUSB_0_1 /* limit to libusb 0.1 implementation */ case -ENXIO: /* No such device or address */ - case -ENOENT: /* No such file or directory */ +#endif + case ERROR_NOT_FOUND: /* No such file or directory */ + fallthrough_reconnect: /* Uh oh, got to reconnect! */ hd = NULL; return FALSE; @@ -1226,20 +1502,27 @@ static bool_t hid_ups_walk(walkmode_t mode) case 0: continue; - case -ETIMEDOUT: /* Connection timed out */ - case -EOVERFLOW: /* Value too large for defined data type */ + case ERROR_TIMEOUT: /* Connection timed out */ + case ERROR_OVERFLOW: /* Value too large for defined data type */ +#if EPROTO && WITH_LIBUSB_0_1 case -EPROTO: /* Protocol error */ - case -EPIPE: /* Broken pipe */ +#endif + case ERROR_PIPE: /* Broken pipe */ default: /* Don't know what happened, try again later... */ continue; } - upsdebugx(2, "Path: %s, Type: %s, ReportID: 0x%02x, Offset: %i, Size: %i, Value: %g", - item->hidpath, HIDDataType(item->hiddata), item->hiddata->ReportID, + upsdebugx(2, + "Path: %s, Type: %s, ReportID: 0x%02x, " + "Offset: %i, Size: %i, Value: %g", + item->hidpath, HIDDataType(item->hiddata), + item->hiddata->ReportID, item->hiddata->Offset, item->hiddata->Size, value); if (item->hidflags & HU_TYPE_CMD) { + upsdebugx(3, "Adding command '%s' using Path '%s'", + item->info_type, item->hidpath); dstate_addcmd(item->info_type); continue; } @@ -1286,6 +1569,10 @@ static int reconnect_ups(void) upsdebugx(4, "= device has been disconnected, try to reconnect ="); upsdebugx(4, "=================================================="); + /* Try to close the previous handle */ + if (udev) + comm_driver->close(udev); + ret = comm_driver->open(&udev, &curDevice, subdriver_matcher, NULL); if (ret > 0) { @@ -1337,6 +1624,12 @@ static void ups_alarm_set(void) } } +/* Return the current value of ups_status */ +unsigned ups_status_get(void) +{ + return ups_status; +} + /* Convert the local status information to NUT format and set NUT status. */ static void ups_status_set(void) @@ -1349,10 +1642,27 @@ static void ups_status_set(void) dstate_delinfo("input.transfer.reason"); } - if (ups_status & STATUS(ONLINE)) { - status_set("OL"); /* on line */ - } else { + + if (!(ups_status & STATUS(ONLINE))) { status_set("OB"); /* on battery */ + } else if ((ups_status & STATUS(DISCHRG))) { + /* if online */ + if (onlinedischarge) { + /* if we treat OL+DISCHRG as being offline */ + status_set("OB"); /* on battery */ + } else { + if (!(ups_status & STATUS(CAL))) { + /* if in OL+DISCHRG unknowingly, warn user */ + upslogx(LOG_WARNING, "%s: seems that UPS [%s] is in OL+DISCHRG state now. " + "Is it calibrating or do you perhaps want to set 'onlinedischarge' option? " + "Some UPS models (e.g. CyberPower UT series) emit OL+DISCHRG when offline.", + __func__, upsname); + } + /* if we're calibrating */ + status_set("OL"); /* on line */ + } + } else if ((ups_status & STATUS(ONLINE))) { + status_set("OL"); } if ((ups_status & STATUS(DISCHRG)) && !(ups_status & STATUS(DEPLETED))) { @@ -1415,6 +1725,11 @@ static hid_info_t *find_hid_info(const HIDData_t *hiddata) { hid_info_t *hidups_item; + if(!hiddata) { + upsdebugx(2, "%s: hiddata == NULL", __func__); + return NULL; + } + for (hidups_item = subdriver->hid2nut; hidups_item->info_type != NULL ; hidups_item++) { /* Skip server side vars */ @@ -1444,12 +1759,16 @@ static long hu_find_valinfo(info_lkp_t *hid2info, const char* value) for (info_lkp = hid2info; info_lkp->nut_value != NULL; info_lkp++) { if (!(strcmp(info_lkp->nut_value, value))) { - upsdebugx(5, "hu_find_valinfo: found %s (value: %ld)", info_lkp->nut_value, info_lkp->hid_value); + upsdebugx(5, + "hu_find_valinfo: found %s (value: %ld)", + info_lkp->nut_value, info_lkp->hid_value); return info_lkp->hid_value; } } - upsdebugx(3, "hu_find_valinfo: no matching HID value for this INFO_* value (%s)", value); + upsdebugx(3, + "hu_find_valinfo: no matching HID value for this INFO_* value (%s)", + value); return -1; } @@ -1466,12 +1785,16 @@ static const char *hu_find_infoval(info_lkp_t *hid2info, const double value) /* use 'value' as an index for a lookup in an array */ for (info_lkp = hid2info; info_lkp->nut_value != NULL; info_lkp++) { if (info_lkp->hid_value == (long)value) { - upsdebugx(5, "hu_find_infoval: found %s (value: %ld)", info_lkp->nut_value, (long)value); + upsdebugx(5, + "hu_find_infoval: found %s (value: %ld)", + info_lkp->nut_value, (long)value); return info_lkp->nut_value; } } - upsdebugx(3, "hu_find_infoval: no matching INFO_* value for this HID value (%g)", value); + upsdebugx(3, + "hu_find_infoval: no matching INFO_* value for this HID value (%g)", + value); return NULL; } @@ -1502,7 +1825,19 @@ static int ups_infoval_set(hid_info_t *item, double value) dstate_setinfo(item->info_type, "%s", nutvalue); } else { +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif dstate_setinfo(item->info_type, item->dfl, value); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif } return 1; diff --git a/drivers/usbhid-ups.h b/drivers/usbhid-ups.h index 96bce48..5cdf7e6 100644 --- a/drivers/usbhid-ups.h +++ b/drivers/usbhid-ups.h @@ -37,11 +37,13 @@ extern hid_dev_handle_t udev; extern bool_t use_interrupt_pipe; /* Set to FALSE if interrupt reports should not be used */ /* Driver's parameters */ +#define HU_VAR_LOWBATT "lowbatt" #define HU_VAR_ONDELAY "ondelay" #define HU_VAR_OFFDELAY "offdelay" #define HU_VAR_POLLFREQ "pollfreq" /* Parameters default values */ +#define DEFAULT_LOWBATT "30" /* percentage of battery charge to consider the UPS in low battery state */ #define DEFAULT_ONDELAY "30" /* Delay between return of utility power */ /* and powering up of load, in seconds */ /* CAUTION: ondelay > offdelay */ @@ -50,7 +52,9 @@ extern bool_t use_interrupt_pipe; /* Set to FALSE if interrupt reports should /* The driver will wait for Interrupt */ /* and do "light poll" in the meantime */ -#define MAX_STRING_SIZE 128 +#ifndef MAX_STRING_SIZE +#define MAX_STRING_SIZE 128 +#endif /* --------------------------------------------------------------- */ @@ -65,6 +69,9 @@ typedef struct { double (*nuf)(const char *nut_value); /* optional NUT to HID mapping */ } info_lkp_t; +/* accessor on the status */ +extern unsigned ups_status_get(void); + /* declarations of public lookup tables */ /* boolean status values from UPS */ extern info_lkp_t online_info[]; @@ -109,6 +116,40 @@ extern info_lkp_t stringid_conversion[]; extern info_lkp_t divide_by_10_conversion[]; extern info_lkp_t kelvin_celsius_conversion[]; +/* ---------------------------------------------------------------------- */ +/* data for processing boolean values from UPS */ + +#define STATUS(x) ((unsigned)1<%s<>%s<\n",temp,temp+3); + upsdebugx(1, "ups.model >%s<>%s<\n",temp,temp+3); } - + /* ups.mfr */ if (get_data("vDm?",temp)) return; dstate_setinfo("ups.mfr", "%s", temp+3); - upsdebugx(1, "ups.mfr >%s<>%s<\n",temp,temp+3); + upsdebugx(1, "ups.mfr >%s<>%s<\n",temp,temp+3); + - /* ups.serial */ if (exist_ups_serial) - { + { if (get_data("vDS?",temp)) return; dstate_setinfo("ups.serial", "%s", temp+3); - } - upsdebugx(1, "ups.serial >%s<>%s<\n",temp,temp+3); + } + upsdebugx(1, "ups.serial >%s<>%s<\n",temp,temp+3); - /* ups.firmware */ + /* ups.firmware */ if (get_data("vDV?",temp)) return; dstate_setinfo("ups.firmware", "%s", temp+3); - upsdebugx(1, "ups.firmware >%s<>%s<\n",temp,temp+3); + upsdebugx(1, "ups.firmware >%s<>%s<\n",temp,temp+3); /* ups.temperature */ if (exist_ups_temperature) - { + { if (get_data("vBT?",temp)) return; dstate_setinfo("ups.temperature", "%s", temp+3); - } - upsdebugx(1, "ups.temperature >%s<>%s<\n",temp,temp+3); + } + upsdebugx(1, "ups.temperature >%s<>%s<\n",temp,temp+3); /* ups.load */ if (get_data("vO0L?",temp)) return; dstate_setinfo("ups.load", "%s", temp+4); - upsdebugx(1, "ups.load >%s<>%s<\n",temp,temp+4); + upsdebugx(1, "ups.load >%s<>%s<\n",temp,temp+4); - /* ups protocol */ - /*if (get_data("vDC?",temp)) return; + /* ups protocol */ + /* + if (get_data("vDC?",temp)) return; dstate_setinfo("ups.protocol", "%s", temp+3; - upsdebugx(1, "ups.protocol >%s<>%s<\n",temp,temp+3; + upsdebugx(1, "ups.protocol >%s<>%s<\n",temp,temp+3; */ /************** input.x *****************/ - + /* input.voltage */ if (get_data("vI0U?",temp)) return; dstate_setinfo("input.voltage", "%s", temp+4); - upsdebugx(1, "input.voltage >%s<>%s<\n",temp,temp+4); + upsdebugx(1, "input.voltage >%s<>%s<\n",temp,temp+4); + - /* input.transfer.low */ if (get_data("vFi?",temp)) return; dstate_setinfo("input.transfer.low", "%s", temp+3); - upsdebugx(1, "input.transfer.low >%s<>%s<\n",temp,temp+3); + upsdebugx(1, "input.transfer.low >%s<>%s<\n",temp,temp+3); + - /* input.transfer.high */ if (get_data("vFj?",temp)) return; dstate_setinfo("input.transfer.high", "%s", temp+3); @@ -422,75 +423,75 @@ void upsdrv_updateinfo(void) /* input.frequency */ if (get_data("vI0f?",temp)) return; dstate_setinfo("input.frequency", "%2.1f", atof(temp+4) / 10.0); - upsdebugx(1, "input.frequency >%s<>%s<\n",temp,temp+4); + upsdebugx(1, "input.frequency >%s<>%s<\n",temp,temp+4); + + + /*************** output.x ********************************/ - /*************** output.x ********************************/ - - /* output.voltage */ if (get_data("vO0U?",temp)) return; dstate_setinfo("output.voltage", "%s", temp+4); - upsdebugx(1, "output.voltage >%s<>%s<\n",temp,temp+4); + upsdebugx(1, "output.voltage >%s<>%s<\n",temp,temp+4); + - /* output.frequency */ if (get_data("vOf?",temp)) return; dstate_setinfo("output.frequency", "%2.1f", atof(temp+3) / 10.0); - upsdebugx(1, "output.frequency >%s<>%s<\n",temp,temp+3); + upsdebugx(1, "output.frequency >%s<>%s<\n",temp,temp+3); /* output.current */ if (exist_output_current) - { + { if (get_data("vO0I?",temp)) return; dstate_setinfo("output.current", "%2.1f", atof(temp+4) / 10.0); - } - upsdebugx(1, "output.current >%s<>%s<\n",temp,temp+4); - - + } + upsdebugx(1, "output.current >%s<>%s<\n",temp,temp+4); + + /*************** battery.x *******************************/ - + /* battery charge */ if (exist_battery_charge) - { + { if (get_data("vBC?",temp)) return; dstate_setinfo("battery.charge", "%s", temp+3); - } - upsdebugx(1, "battery.charge >%s<>%s<\n",temp,temp+3); - + } + upsdebugx(1, "battery.charge >%s<>%s<\n",temp,temp+3); + /* battery.voltage */ if (get_data("vBU?",temp)) return; dstate_setinfo("battery.voltage", "%2.1f", atof(temp+3) / 10.0); - upsdebugx(1, "battery.voltage >%s<>%s<\n",temp,temp+3); - + upsdebugx(1, "battery.voltage >%s<>%s<\n",temp,temp+3); + /* battery.current */ if (exist_battery_current) - { + { if (get_data("vBI?",temp)) return; dstate_setinfo("battery.current", "%2.1f", atof(temp+3) / 10.0); - } - upsdebugx(1, "battery.current >%s<>%s<\n",temp,temp+3); + } + upsdebugx(1, "battery.current >%s<>%s<\n",temp,temp+3); /* battery.temperature */ if (exist_battery_temperature) - { + { if (get_data("vBT?",temp)) return; dstate_setinfo("battery.temperature", "%s", temp+3); - } - upsdebugx(1, "battery.temperature >%s<>%s<\n",temp,temp+3); + } + upsdebugx(1, "battery.temperature >%s<>%s<\n",temp,temp+3); /* battery.runtime */ if (exist_battery_runtime) - { + { if (get_data("vBt?",temp)) return; runtime_sec = strtol(temp+3, NULL, 10)*60; - snprintf(temp, sizeof(temp), "%d", runtime_sec); + snprintf(temp, sizeof(temp), "%ld", runtime_sec); dstate_setinfo("battery.runtime", "%s", temp); - } - upsdebugx(1, "battery.runtime >%s<>%d<\n",temp,runtime_sec); + } + upsdebugx(1, "battery.runtime >%s<>%ld<\n",temp,runtime_sec); dstate_dataok(); } @@ -500,7 +501,7 @@ void upsdrv_shutdown(void) ser_send(upsfd, "vCc0!%c", ENDCHAR); usleep(UPS_DELAY); - ser_send(upsfd, "vCb%i!%c", sdwdelay, ENDCHAR); + ser_send(upsfd, "vCb%i!%c", sdwdelay, ENDCHAR); } void upsdrv_help(void) @@ -511,25 +512,27 @@ void upsdrv_help(void) void upsdrv_makevartable(void) { addvar(VAR_VALUE, "usd", "Seting delay before shutdown"); - addvar(VAR_VALUE, "modelname", "Seting model name"); + addvar(VAR_VALUE, "modelname", "Seting model name"); } void upsdrv_initups(void) { char temp[ LENGTH_TEMP ], *usd = NULL; /* = NULL je dulezite jen pro prekladac */ - - + + upsfd = ser_open(device_path); ser_set_speed(upsfd, device_path, B1200); - if ((usd = getval("usd"))) { + if ((usd = getval("usd"))) + { sdwdelay=atoi(usd); upsdebugx(1, "(-x) Delay before shutdown %i",sdwdelay); } - if ((model_name = getval("modelname"))) { - /* kdyz modelname nebylo zadano je vraceno NULL*/ + if ((model_name = getval("modelname"))) + { + /* kdyz modelname nebylo zadano je vraceno NULL*/ upsdebugx(1, "(-x) UPS Name %s",model_name); } diff --git a/drivers/xppc-mib.c b/drivers/xppc-mib.c new file mode 100644 index 0000000..cfbc450 --- /dev/null +++ b/drivers/xppc-mib.c @@ -0,0 +1,133 @@ +/* xppc-mib.c - subdriver to monitor XPPC SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2012 Arnaud Quette + * 2014 Charles Lepple + * + * Note: this subdriver was initially generated as a "stub" by the + * scripts/subdriver/gen-snmp-subdriver.sh script. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "xppc-mib.h" + +#define XPPC_MIB_VERSION "0.4" + +#define XPPC_SYSOID ".1.3.6.1.4.1.935" + +/* To create a value lookup structure (as needed on the 2nd line of the example + * below), use the following kind of declaration, outside of the present snmp_info_t[]: + * static info_lkp_t xpcc_onbatt_info[] = { + * { 1, "OB", NULL, NULL }, + * { 2, "OL", NULL, NULL }, + * { 0, NULL, NULL, NULL } + * }; + */ + +/* upsBaseBatteryStatus */ +static info_lkp_t xpcc_onbatt_info[] = { + { 1, "", NULL, NULL }, /* unknown */ + { 2, "", NULL, NULL }, /* batteryNormal */ + { 3, "LB", NULL, NULL }, /* batteryLow */ + { 0, NULL, NULL, NULL } +}; + +/* +upsBaseOutputStatus OBJECT-TYPE + SYNTAX INTEGER { + unknown(1), + onLine(2), + onBattery(3), + onBoost(4), + sleeping(5), + onBypass(6), + rebooting(7), + standBy(8), + onBuck(9) } +*/ +static info_lkp_t xpcc_power_info[] = { + { 1, "", NULL, NULL }, /* unknown */ + { 2, "OL", NULL, NULL }, /* onLine */ + { 3, "OB", NULL, NULL }, /* onBattery */ + { 4, "OL BOOST", NULL, NULL }, /* onBoost */ + { 5, "OFF", NULL, NULL }, /* sleeping */ + { 6, "BYPASS", NULL, NULL }, /* onBypass */ + { 7, "", NULL, NULL }, /* rebooting */ + { 8, "OFF", NULL, NULL }, /* standBy */ + { 9, "OL TRIM", NULL, NULL }, /* onBuck */ + { 0, NULL, NULL, NULL } +}; + +/* XPPC Snmp2NUT lookup table */ +static snmp_info_t xppc_mib[] = { + + /* Data format: + * { info_type, info_flags, info_len, OID, dfl, flags, oid2info, setvar }, + * + * info_type: NUT INFO_ or CMD_ element name + * info_flags: flags to set in addinfo + * info_len: length of strings if STR + * cmd value if CMD, multiplier otherwise + * OID: SNMP OID or NULL + * dfl: default value + * flags: snmp-ups internal flags (FIXME: ...) + * oid2info: lookup table between OID and NUT values + * + * Example: + * { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, + * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, xpcc_onbatt_info }, + * + * To create a value lookup structure (as needed on the 2nd line), use the + * following kind of declaration, outside of the present snmp_info_t[]: + * static info_lkp_t xpcc_onbatt_info[] = { + * { 1, "OB" }, + * { 2, "OL" }, + * { 0, NULL } + * }; + */ + + /* standard MIB items */ + { "device.description", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.1.0", NULL, SU_FLAG_OK, NULL }, + { "device.contact", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.4.0", NULL, SU_FLAG_OK, NULL }, + { "device.location", ST_FLAG_STRING | ST_FLAG_RW, SU_INFOSIZE, ".1.3.6.1.2.1.1.6.0", NULL, SU_FLAG_OK, NULL }, + + { "ups.mfr", ST_FLAG_STRING, SU_INFOSIZE, NULL, "Tripp Lite / Phoenixtec", + SU_FLAG_STATIC | SU_FLAG_ABSENT | SU_FLAG_OK, NULL }, + + /* upsBaseIdentModel.0 = STRING: "Intelligent" */ + { "ups.model", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.1.1.1.0", "Generic Phoenixtec SNMP device", SU_FLAG_OK, NULL }, + /* upsBaseBatteryStatus.0 = INTEGER: batteryNormal(2) */ + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.2.1.1.0", "", SU_STATUS_BATT | SU_TYPE_INT | SU_FLAG_OK, xpcc_onbatt_info }, + /* upsSmartBatteryCapacity.0 = INTEGER: 100 */ + { "battery.charge", 0, 1, ".1.3.6.1.4.1.935.1.1.1.2.2.1.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, + /* upsSmartBatteryTemperature.0 = INTEGER: 260 */ + { "ups.temperature", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.2.2.3.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, + /* upsSmartInputLineVoltage.0 = INTEGER: 1998 */ + { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.3.2.1.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, + /* upsBaseOutputStatus.0 = INTEGER: onLine(2) */ + { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.935.1.1.1.4.1.1.0", "", SU_TYPE_INT | SU_STATUS_PWR, xpcc_power_info }, + /* upsSmartOutputVoltage.0 = INTEGER: 2309 */ + { "output.voltage", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.4.2.1.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, + /* upsSmartOutputFrequency.0 = INTEGER: 500 */ + { "output.frequency", 0, 0.1, ".1.3.6.1.4.1.935.1.1.1.4.2.2.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, + /* upsSmartOutputLoad.0 = INTEGER: 7 */ + { "ups.load", 0, 1, ".1.3.6.1.4.1.935.1.1.1.4.2.3.0", NULL, SU_TYPE_INT | SU_FLAG_OK, NULL }, + + /* end of structure. */ + { NULL, 0, 0, NULL, NULL, 0, NULL } +}; + +mib2nut_info_t xppc = { "xppc", XPPC_MIB_VERSION, NULL, NULL, xppc_mib, XPPC_SYSOID, NULL }; diff --git a/drivers/xppc-mib.h b/drivers/xppc-mib.h new file mode 100644 index 0000000..e2a360b --- /dev/null +++ b/drivers/xppc-mib.h @@ -0,0 +1,29 @@ +/* xppc-mib.h - subdriver to monitor XPPC SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2012 Arnaud Quette + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef XPPC_MIB_H +#define XPPC_MIB_H + +#include "main.h" +#include "snmp-ups.h" + +extern mib2nut_info_t xppc; + +#endif /* XPPC_MIB_H */ diff --git a/include/Makefile.am b/include/Makefile.am index d599ac0..c16d8c3 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1,28 +1,39 @@ -EXTRA_DIST = attribute.h common.h extstate.h parseconf.h proto.h \ - state.h timehead.h upsconf.h nut_stdint.h +dist_noinst_HEADERS = attribute.h common.h extstate.h parseconf.h proto.h \ + state.h str.h timehead.h upsconf.h nut_float.h nut_stdint.h nut_platform.h # http://www.gnu.org/software/automake/manual/automake.html#Clean BUILT_SOURCES = nut_version.h CLEANFILES = nut_version.h +MAINTAINERCLEANFILES = Makefile.in .dirstamp -# magic to include SVN revision number in NUT version string +# magic to include Git version information in NUT version string +# (for builds not made from the tagged commit in a Git workspace) nut_version.h: FORCE - @GITREV=`git describe --tags 2>/dev/null | sed 's/^v\([0-9]\)/\1/' `; \ - if [ -z "$$GITREV" ]; \ - then SVNREV=`LANG=C svnversion -n $(top_srcdir) 2>/dev/null`; \ - if [ -z "$$SVNREV" -o "$$SVNREV" = "exported" ]; \ - then NUT_VERSION="$(PACKAGE_VERSION)"; \ - else NUT_VERSION="$(PACKAGE_VERSION)-$$SVNREV"; \ - fi ; \ - else NUT_VERSION="$$GITREV"; \ - fi ; \ - echo '/* Autogenerated file. Do not change. */' > _nut_version.h ; \ - echo '/* This file was generated by "make". */' >> _nut_version.h ; \ - echo "#define NUT_VERSION_MACRO \"$$NUT_VERSION\"" >> _nut_version.h ; \ - echo "NUT_VERSION: \"$$NUT_VERSION\"" - -test -f nut_version.h || cp _nut_version.h nut_version.h - -cmp -s _nut_version.h nut_version.h || cp _nut_version.h nut_version.h - -rm -f _nut_version.h + @GITREV="`git describe --tags 2>/dev/null | sed -e 's/^v\([0-9]\)/\1/' -e 's,^.*/,,'`" || GITREV=""; \ + { echo '/* Autogenerated file. Do not change. */' ; \ + echo '/* This file was generated by "make". */' ; \ + if [ -z "$$GITREV" ]; then \ + NUT_VERSION="$(PACKAGE_VERSION)"; \ + echo '/* The version number is set by AC_INIT in configure.ac. */' ; \ + else \ + NUT_VERSION="$$GITREV"; \ + echo '/* The version number is determined by Git source commit hash' ; \ + echo ' * and number of commits since the most recent Git tag (if not' ; \ + echo ' * building the newest tagged commit itself - then just the tag).'; \ + echo ' */' ; \ + fi ; \ + echo "#define NUT_VERSION_MACRO \"$$NUT_VERSION\"" ; \ + } > "$@.tmp" ; \ + echo "NUT_VERSION: \"$$NUT_VERSION\"" + -test -f "$@" || cp "$@.tmp" "$@" + -cmp -s "$@.tmp" "$@" || cp "$@.tmp" "$@" + -rm -f "$@.tmp" FORCE: + +# counter part of BUILT_SOURCES: since nut_version is not a direct +# deps of a local target, we must clean it by ourselves before the +# distribution +dist-hook: + rm -f $(distdir)/nut_version.h diff --git a/include/Makefile.in b/include/Makefile.in index 678a6e2..fed410a 100644 --- a/include/Makefile.in +++ b/include/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -14,7 +13,63 @@ # PARTICULAR PURPOSE. @SET_MAKE@ + VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -35,45 +90,98 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = include -DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ - $(srcdir)/config.h.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(dist_noinst_HEADERS) \ + $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +HEADERS = $(dist_noinst_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) \ + config.h.in +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/config.h.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -84,16 +192,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -102,11 +219,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -116,14 +230,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -134,21 +249,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -162,35 +286,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -201,8 +336,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -221,18 +360,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -242,13 +384,14 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ -EXTRA_DIST = attribute.h common.h extstate.h parseconf.h proto.h \ - state.h timehead.h upsconf.h nut_stdint.h +dist_noinst_HEADERS = attribute.h common.h extstate.h parseconf.h proto.h \ + state.h str.h timehead.h upsconf.h nut_float.h nut_stdint.h nut_platform.h # http://www.gnu.org/software/automake/manual/automake.html#Clean BUILT_SOURCES = nut_version.h CLEANFILES = nut_version.h +MAINTAINERCLEANFILES = Makefile.in .dirstamp all: $(BUILT_SOURCES) config.h $(MAKE) $(AM_MAKEFLAGS) all-am @@ -265,14 +408,13 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu include/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu include/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -285,10 +427,8 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): config.h: stamp-h1 - @if test ! -f $@; then \ - rm -f stamp-h1; \ - $(MAKE) $(AM_MAKEFLAGS) stamp-h1; \ - else :; fi + @test -f $@ || rm -f stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) stamp-h1 stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status @rm -f stamp-h1 @@ -306,14 +446,63 @@ mostlyclean-libtool: clean-libtool: -rm -rf .libs _libs -tags: TAGS -TAGS: -ctags: CTAGS -CTAGS: +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am -distdir: $(DISTFILES) +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -343,14 +532,18 @@ distdir: $(DISTFILES) || exit 1; \ fi; \ done + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" distdir="$(distdir)" \ + dist-hook check-am: all-am check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am -all-am: Makefile config.h +all-am: Makefile $(HEADERS) config.h installdirs: install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am -install-exec: install-exec-am +install-exec: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data: install-data-am uninstall: uninstall-am @@ -359,10 +552,15 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -376,13 +574,14 @@ maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am distclean: distclean-am -rm -f Makefile -distclean-am: clean-am distclean-generic distclean-hdr +distclean-am: clean-am distclean-generic distclean-hdr distclean-tags dvi: dvi-am @@ -442,43 +641,56 @@ ps-am: uninstall-am: -.MAKE: all check install install-am install-strip +.MAKE: all check install install-am install-exec install-strip -.PHONY: all all-am check check-am clean clean-generic clean-libtool \ - distclean distclean-generic distclean-hdr distclean-libtool \ - distdir dvi dvi-am html html-am info info-am install \ - install-am install-data install-data-am install-dvi \ +.PHONY: CTAGS GTAGS TAGS all all-am check check-am clean clean-generic \ + clean-libtool cscopelist-am ctags ctags-am dist-hook distclean \ + distclean-generic distclean-hdr distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - uninstall uninstall-am + tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile -# magic to include SVN revision number in NUT version string +# magic to include Git version information in NUT version string +# (for builds not made from the tagged commit in a Git workspace) nut_version.h: FORCE - @GITREV=`git describe --tags 2>/dev/null | sed 's/^v\([0-9]\)/\1/' `; \ - if [ -z "$$GITREV" ]; \ - then SVNREV=`LANG=C svnversion -n $(top_srcdir) 2>/dev/null`; \ - if [ -z "$$SVNREV" -o "$$SVNREV" = "exported" ]; \ - then NUT_VERSION="$(PACKAGE_VERSION)"; \ - else NUT_VERSION="$(PACKAGE_VERSION)-$$SVNREV"; \ - fi ; \ - else NUT_VERSION="$$GITREV"; \ - fi ; \ - echo '/* Autogenerated file. Do not change. */' > _nut_version.h ; \ - echo '/* This file was generated by "make". */' >> _nut_version.h ; \ - echo "#define NUT_VERSION_MACRO \"$$NUT_VERSION\"" >> _nut_version.h ; \ - echo "NUT_VERSION: \"$$NUT_VERSION\"" - -test -f nut_version.h || cp _nut_version.h nut_version.h - -cmp -s _nut_version.h nut_version.h || cp _nut_version.h nut_version.h - -rm -f _nut_version.h + @GITREV="`git describe --tags 2>/dev/null | sed -e 's/^v\([0-9]\)/\1/' -e 's,^.*/,,'`" || GITREV=""; \ + { echo '/* Autogenerated file. Do not change. */' ; \ + echo '/* This file was generated by "make". */' ; \ + if [ -z "$$GITREV" ]; then \ + NUT_VERSION="$(PACKAGE_VERSION)"; \ + echo '/* The version number is set by AC_INIT in configure.ac. */' ; \ + else \ + NUT_VERSION="$$GITREV"; \ + echo '/* The version number is determined by Git source commit hash' ; \ + echo ' * and number of commits since the most recent Git tag (if not' ; \ + echo ' * building the newest tagged commit itself - then just the tag).'; \ + echo ' */' ; \ + fi ; \ + echo "#define NUT_VERSION_MACRO \"$$NUT_VERSION\"" ; \ + } > "$@.tmp" ; \ + echo "NUT_VERSION: \"$$NUT_VERSION\"" + -test -f "$@" || cp "$@.tmp" "$@" + -cmp -s "$@.tmp" "$@" || cp "$@.tmp" "$@" + -rm -f "$@.tmp" FORCE: +# counter part of BUILT_SOURCES: since nut_version is not a direct +# deps of a local target, we must clean it by ourselves before the +# distribution +dist-hook: + rm -f $(distdir)/nut_version.h + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/include/attribute.h b/include/attribute.h index 3fe3f4a..f6ce29b 100644 --- a/include/attribute.h +++ b/include/attribute.h @@ -1,12 +1,71 @@ -/* portability hacks for __attribute__ usage in other header files */ +/* attribute.h - portability hacks for __attribute__ usage in other header files -#ifndef ATTRIBUTE_H_SEEN -#define ATTRIBUTE_H_SEEN 1 + Copyright (C) 2001 Russell Kroll + 2005 Arnaud Quette + 2020 Jim Klimov -#ifndef __attribute__ -# if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) || __STRICT_ANSI__ -# define __attribute__(x) + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_ATTRIBUTE_H_SEEN +#define NUT_ATTRIBUTE_H_SEEN 1 + +/* To complicate matters, compilers with native support + * for the keyword may expose or not expose it as a macro... + * but for those that perform such courtesy, or are known + * supporters, we can put up the flag. For others, someone + * with those compilers should check and file PRs to NUT. + */ +#if (!defined HAVE___ATTRIBUTE__) || (HAVE___ATTRIBUTE__ == 0) +# if ( defined(__GNUC__) && ( __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 8) ) ) || ( defined(__STRICT_ANSI__) && __STRICT_ANSI__ ) +# ifndef __attribute__ +# define __attribute__(x) +# endif +# ifndef HAVE___ATTRIBUTE__ +# define HAVE___ATTRIBUTE__ 0 +# endif +# else +# if defined(__clang__) || defined(__GNUC__) || defined(__SUNPRO_C) +# ifndef HAVE___ATTRIBUTE__ +# define HAVE___ATTRIBUTE__ 1 +# endif +# else +# ifndef HAVE___ATTRIBUTE__ +# define HAVE___ATTRIBUTE__ 0 +# endif +# endif # endif #endif -#endif /* ATTRIBUTE_H_SEEN */ +#if (!defined HAVE___ATTRIBUTE__) || (HAVE___ATTRIBUTE__ == 0) +# ifdef HAVE___ATTRIBUTE__UNUSED_ARG +# undef HAVE___ATTRIBUTE__UNUSED_ARG +# endif +# ifdef HAVE___ATTRIBUTE__UNUSED_FUNC +# undef HAVE___ATTRIBUTE__UNUSED_FUNC +# endif +# ifdef HAVE___ATTRIBUTE__NORETURN +# undef HAVE___ATTRIBUTE__NORETURN +# endif +# ifdef HAVE___ATTRIBUTE__ +# undef HAVE___ATTRIBUTE__ +# endif +#endif + +/* Other source files now can simply check for `ifdef HAVE___ATTRIBUTE__*` + * as usual, and not bother about 0/1 values of the macro as well. + */ + +#endif /* NUT_ATTRIBUTE_H_SEEN */ diff --git a/include/common.h b/include/common.h index 431751c..ff48ecd 100644 --- a/include/common.h +++ b/include/common.h @@ -1,6 +1,3 @@ -#ifndef NUT_COMMON_H -#define NUT_COMMON_H - /* common.h - prototypes for the common useful functions Copyright (C) 2000 Russell Kroll @@ -20,6 +17,9 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef NUT_COMMON_H_SEEN +#define NUT_COMMON_H_SEEN 1 + #include "config.h" /* must be the first header */ /* Need this on AIX when using xlc to get alloca */ @@ -33,9 +33,23 @@ #include #include #include + +#ifdef HAVE_SYS_SIGNAL_H +#include +#endif +#ifdef HAVE_SIGNAL_H #include +#endif + #include -#include + +#ifdef HAVE_STRINGS_H +#include /* for strncasecmp() and strcasecmp() */ +#endif +#ifdef HAVE_STRING_H +#include /* for strdup() and many others */ +#endif + #include #include #include @@ -43,9 +57,34 @@ #include "timehead.h" #include "attribute.h" #include "proto.h" +#include "str.h" + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif extern const char *UPS_VERSION; +/* Use in code to notify the developers and quiesce the compiler that + * (for this codepath) the argument or variable is unused intentionally. + * void f(int x) { + * NUT_UNUSED_VARIABLE(x); + * ... + * } + * + * Note that solutions which mark up function arguments or employ this or + * that __attribute__ proved not portable enough for wherever NUT builds. + */ +#define NUT_UNUSED_VARIABLE(x) (void)(x) + +/** @brief Default timeout (in seconds) for network operations, as used by `upsclient` and `nut-scanner`. */ +#define DEFAULT_NETWORK_TIMEOUT 5 + +/** @brief Default timeout (in seconds) for retrieving the result of a `TRACKING`-enabled operation (e.g. `INSTCMD`, `SET VAR`). */ +#define DEFAULT_TRACKING_TIMEOUT 10 + /* get the syslog ready for us */ void open_syslog(const char *progname); @@ -64,13 +103,30 @@ void chroot_start(const char *path); /* write a pid file - is a full pathname *or* just the program name */ void writepid(const char *name); +/* parses string buffer into a pid_t if it passes + * a few sanity checks; returns -1 on error */ +pid_t parsepid(const char *buf); + /* send a signal to another running process */ int sendsignal(const char *progname, int sig); int snprintfcat(char *dst, size_t size, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 3, 4))); -/* open , get the pid, then send it */ +/* Report maximum platform value for the pid_t */ +pid_t get_max_pid_t(void); + +/* send sig to pid after some sanity checks, returns + * -1 for error, or zero for a successfully sent signal */ +int sendsignalpid(pid_t pid, int sig); + +/* open , get the pid, then send it + * returns zero for successfully sent signal, + * negative for errors: + * -3 PID file not found + * -2 PID file not parsable + * -1 Error sending signal + */ int sendsignalfn(const char *pidfn, int sig); const char *xbasename(const char *file); @@ -88,15 +144,53 @@ const char * dflt_statepath(void); /* Return the alternate path for pid files */ const char * altpidpath(void); +/* Die with a standard message if socket filename is too long */ +void check_unix_socket_filename(const char *fn); + +/* upslog*() messages are sent to syslog always; + * their life after that is out of NUT's control */ void upslog_with_errno(int priority, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); void upslogx(int priority, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); -void upsdebug_with_errno(int level, const char *fmt, ...) + +/* upsdebug*() messages are only logged if debugging + * level is high enough. To speed up a bit (minimize + * passing of ultimately ignored data trough the stack) + * these are "hidden" implementations wrapped into + * macros for earlier routine names spread around the + * codebase, they would check debug level first and + * only if logging should happen - call the routine + * and pass around pointers and other data. + */ +void s_upsdebug_with_errno(int level, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); -void upsdebugx(int level, const char *fmt, ...) +void s_upsdebugx(int level, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))); -void upsdebug_hex(int level, const char *msg, const void *buf, int len); +void s_upsdebug_hex(int level, const char *msg, const void *buf, size_t len); +void s_upsdebug_ascii(int level, const char *msg, const void *buf, size_t len); +/* These macros should help avoid run-time overheads + * passing data for messages nobody would ever see. + * + * Also NOTE: the "level" may be specified by callers in various ways, + * e.g. as a "X ? Y : Z" style expression; to catch those expansions + * transparently we hide them into parentheses as "(label)". + * + * For stricter C99 compatibility, all parameters specified to a macro + * must be populated by caller (so we do not handle "fmt, args..." where + * the args part may be skipped by caller because fmt is a fixed string). + * Note it is then up to the caller (and compiler checks) that at least + * one argument is provided, the format string (maybe fixed) -- as would + * be required by the actual s_upsdebugx*() method after macro evaluation. + */ +#define upsdebug_with_errno(level, ...) \ + do { if (nut_debug_level >= (level)) { s_upsdebug_with_errno((level), __VA_ARGS__); } } while(0) +#define upsdebugx(level, ...) \ + do { if (nut_debug_level >= (level)) { s_upsdebugx((level), __VA_ARGS__); } } while(0) +#define upsdebug_hex(level, msg, buf, len) \ + do { if (nut_debug_level >= (level)) { s_upsdebug_hex((level), msg, buf, len); } } while(0) +#define upsdebug_ascii(level, msg, buf, len) \ + do { if (nut_debug_level >= (level)) { s_upsdebug_ascii((level), msg, buf, len); } } while(0) void fatal_with_errno(int status, const char *fmt, ...) __attribute__ ((__format__ (__printf__, 2, 3))) __attribute__((noreturn)); @@ -111,15 +205,18 @@ void *xcalloc(size_t number, size_t size); void *xrealloc(void *ptr, size_t size); char *xstrdup(const char *string); -char *rtrim(char *in, const char sep); +ssize_t select_read(const int fd, void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec); +ssize_t select_write(const int fd, const void *buf, const size_t buflen, const time_t d_sec, const suseconds_t d_usec); -int select_read(const int fd, void *buf, const size_t buflen, const long d_sec, const long d_usec); -int select_write(const int fd, const void *buf, const size_t buflen, const long d_sec, const long d_usec); +char * get_libname(const char* base_libname); /* Buffer sizes used for various functions */ #define SMALLBUF 512 #define LARGEBUF 1024 +/** @brief (Minimum) Size that a string must have to hold a UUID4 (i.e. UUID4 length + the terminating null character). */ +#define UUID4_LEN 37 + /* Provide declarations for getopt() global variables */ #ifdef NEED_GETOPT_H @@ -143,4 +240,10 @@ extern int optind; # define setegid(x) setresgid(-1,x,-1) /* Works for HP-UX 10.20 */ #endif -#endif /* NUT_COMMON_H */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_COMMON_H_SEEN */ diff --git a/include/config.h.in b/include/config.h.in index ad12ff1..1f0b9a8 100644 --- a/include/config.h.in +++ b/include/config.h.in @@ -1,4 +1,4 @@ -/* include/config.h.in. Generated from configure.in by autoheader. */ +/* include/config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD @@ -6,6 +6,24 @@ /* Path for pid files of drivers and upsd (usually STATEPATH) */ #undef ALTPIDPATH +/* host env spec we built on */ +#undef AUTOTOOLS_BUILD_ALIAS + +/* host OS short spec we built on */ +#undef AUTOTOOLS_BUILD_SHORT_ALIAS + +/* host env spec we run on */ +#undef AUTOTOOLS_HOST_ALIAS + +/* host OS short spec we run on */ +#undef AUTOTOOLS_HOST_SHORT_ALIAS + +/* host env spec we built for */ +#undef AUTOTOOLS_TARGET_ALIAS + +/* host OS short spec we built for */ +#undef AUTOTOOLS_TARGET_SHORT_ALIAS + /* Default path for user executables */ #undef BINDIR @@ -15,6 +33,15 @@ /* Default path for configuration files */ #undef CONFPATH +/* Compiler flags for cppunit tests */ +#undef CPPUNIT_NUT_CXXFLAGS + +/* Define processor type */ +#undef CPU_TYPE + +/* Define to 1 if your C++ compiler doesn't accept -c and -o together. */ +#undef CXX_NO_MINUS_C_MINUS_O + /* Default path for data files */ #undef DATADIR @@ -30,8 +57,23 @@ MSVC and with C++ compilers. */ #undef FLEXIBLE_ARRAY_MEMBER -/* addons run as user */ -#undef HAL_USER +/* Define to the type of arg 1 for getnameinfo. */ +#undef GETNAMEINFO_TYPE_ARG1 + +/* Define to the type of arg 2 for getnameinfo. */ +#undef GETNAMEINFO_TYPE_ARG2 + +/* Define to the type of args 4 and 6 for getnameinfo. */ +#undef GETNAMEINFO_TYPE_ARG46 + +/* Define to the type of arg 7 for getnameinfo. */ +#undef GETNAMEINFO_TYPE_ARG7 + +/* Define to 1 if you have the `abs' function. */ +#undef HAVE_ABS + +/* Define to 1 if you have the `abs_val' function. */ +#undef HAVE_ABS_VAL /* Define to 1 if you have the `atexit' function. */ #undef HAVE_ATEXIT @@ -54,8 +96,29 @@ /* Define to 1 if C supports variable-length arrays. */ #undef HAVE_C_VARARRAYS -/* Define to 1 if you have the header file. */ -#undef HAVE_DBUS_DBUS_GLIB_H +/* Define to 1 if you have the declaration of `i2c_smbus_access', and to 0 if + you don't. */ +#undef HAVE_DECL_I2C_SMBUS_ACCESS + +/* Define to 1 if you have the declaration of `i2c_smbus_read_block_data', and + to 0 if you don't. */ +#undef HAVE_DECL_I2C_SMBUS_READ_BLOCK_DATA + +/* Define to 1 if you have the declaration of `i2c_smbus_read_byte_data', and + to 0 if you don't. */ +#undef HAVE_DECL_I2C_SMBUS_READ_BYTE_DATA + +/* Define to 1 if you have the declaration of `i2c_smbus_read_word_data', and + to 0 if you don't. */ +#undef HAVE_DECL_I2C_SMBUS_READ_WORD_DATA + +/* Define to 1 if you have the declaration of `i2c_smbus_write_byte_data', and + to 0 if you don't. */ +#undef HAVE_DECL_I2C_SMBUS_WRITE_BYTE_DATA + +/* Define to 1 if you have the declaration of `i2c_smbus_write_word_data', and + to 0 if you don't. */ +#undef HAVE_DECL_I2C_SMBUS_WRITE_WORD_DATA /* Define to 1 if you have the declaration of `LOG_UPTO', and to 0 if you don't. */ @@ -80,18 +143,36 @@ /* Define to 1 if you have the header file. */ #undef HAVE_DLFCN_H +/* Define to 1 if you have the `fabs' function. */ +#undef HAVE_FABS + +/* Define to 1 if you have the `fabsf' function. */ +#undef HAVE_FABSF + +/* Define to 1 if you have the `fabsl' function. */ +#undef HAVE_FABSL + /* Define to 1 if you have the `fcvt' function. */ #undef HAVE_FCVT /* Define to 1 if you have the `fcvtl' function. */ #undef HAVE_FCVTL +/* Define to 1 if you have the `fileno' function. */ +#undef HAVE_FILENO + +/* Define to 1 if you have . */ +#undef HAVE_FLOAT_H + /* Define to 1 if you have the `flock' function. */ #undef HAVE_FLOCK /* Define if FreeIPMI support is available */ #undef HAVE_FREEIPMI +/* Define if FreeIPMI 1.1.X / 1.2.X support is available */ +#undef HAVE_FREEIPMI_11X_12X + /* Define to 1 if you have the header file. */ #undef HAVE_FREEIPMI_FREEIPMI_H @@ -113,12 +194,6 @@ /* Define to 1 if you have the `getpassphrase' function. */ #undef HAVE_GETPASSPHRASE -/* Define to 1 if you have the header file. */ -#undef HAVE_GLIB_H - -/* Define to 1 if you have the `g_timeout_add_seconds' function. */ -#undef HAVE_G_TIMEOUT_ADD_SECONDS - /* Define to 1 if you have the `init_snmp' function. */ #undef HAVE_INIT_SNMP @@ -131,18 +206,46 @@ /* Define if you have Boutell's libgd installed */ #undef HAVE_LIBGD -/* Define to 1 if you have the `libhal_device_new_changeset' function. */ -#undef HAVE_LIBHAL_DEVICE_NEW_CHANGESET - -/* Define to 1 if you have the header file. */ -#undef HAVE_LIBHAL_H - /* Define to enable libltdl support */ #undef HAVE_LIBLTDL /* Define to 1 if you have the header file. */ #undef HAVE_LIBPOWERMAN_H +/* Define to 1 if you have the `libusb_detach_kernel_driver' function. */ +#undef HAVE_LIBUSB_DETACH_KERNEL_DRIVER + +/* Define to 1 if you have the `libusb_detach_kernel_driver_np' function. */ +#undef HAVE_LIBUSB_DETACH_KERNEL_DRIVER_NP + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIBUSB_H + +/* Define to 1 if you have the `libusb_init' function. */ +#undef HAVE_LIBUSB_INIT + +/* Define to 1 if you have the `libusb_kernel_driver_active' function. */ +#undef HAVE_LIBUSB_KERNEL_DRIVER_ACTIVE + +/* Define to 1 if you have the `libusb_set_auto_detach_kernel_driver' + function. */ +#undef HAVE_LIBUSB_SET_AUTO_DETACH_KERNEL_DRIVER + +/* Define to 1 if you have the `libusb_strerror' function. */ +#undef HAVE_LIBUSB_STRERROR + +/* Define to 1 if you have . */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have . */ +#undef HAVE_LINUX_I2C_DEV_H + +/* Define to 1 if you have . */ +#undef HAVE_LINUX_SMBUS_H + +/* Define to 1 if you have the `localtime_r' function. */ +#undef HAVE_LOCALTIME_R + /* Define to 1 if you have the `lockf' function. */ #undef HAVE_LOCKF @@ -155,9 +258,30 @@ /* Define to 1 if you have the header file. */ #undef HAVE_LTDL_H +/* Define to 1 if you have . */ +#undef HAVE_MATH_H + /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H +/* Define to 1 if you have the header file. */ +#undef HAVE_MODBUS_H + +/* Define to 1 if you have the `modbus_new_rtu' function. */ +#undef HAVE_MODBUS_NEW_RTU + +/* Define to 1 if you have the `modbus_new_tcp' function. */ +#undef HAVE_MODBUS_NEW_TCP + +/* Define to 1 if you have the `modbus_set_byte_timeout' function. */ +#undef HAVE_MODBUS_SET_BYTE_TIMEOUT + +/* Define to 1 if you have the `modbus_set_response_timeout' function. */ +#undef HAVE_MODBUS_SET_RESPONSE_TIMEOUT + +/* Define to 1 if you have the header file. */ +#undef HAVE_NETDB_H + /* Define to 1 if you have the header file. */ #undef HAVE_NET_SNMP_NET_SNMP_CONFIG_H @@ -173,6 +297,12 @@ /* Define to 1 if you have the `ne_xml_dispatch_request' function. */ #undef HAVE_NE_XML_DISPATCH_REQUEST +/* Define to 1 if you have the header file. */ +#undef HAVE_NSS_H + +/* Define to 1 if you have the `NSS_Init' function. */ +#undef HAVE_NSS_INIT + /* Define to 1 if you have the `on_exit' function. */ #undef HAVE_ON_EXIT @@ -182,9 +312,299 @@ /* Define to 1 if you have the `pm_connect' function. */ #undef HAVE_PM_CONNECT +/* Define to 1 if you have the `pow10' function. */ +#undef HAVE_POW10 + +/* define if your compiler has pragmas for GCC diagnostic ignored + "-Wc++98-compat(-pedantic)" and for push-pop support */ +#undef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT + +/* define if your compiler has pragmas for GCC diagnostic ignored + "-Wc++98-compat(-pedantic)" and for push-pop support (outside function + bodies) */ +#undef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT_BESIDEFUNC + +/* define if your compiler has pragmas for GCC diagnostic ignored + "-Wformat-nonliteral" or "-Wformat-security" and for push-pop support */ +#undef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL + +/* define if your compiler has pragmas for GCC diagnostic ignored + "-Wformat-nonliteral" or "-Wformat-security" and for push-pop support + (outside function bodies) */ +#undef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL_BESIDEFUNC + +/* define if your compiler has pragmas for GCC diagnostic ignored + "-Wformat-truncation" or "-Werror=stringop-truncation" and for push-pop + support */ +#undef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION + +/* define if your compiler has pragmas for GCC diagnostic ignored + "-Wformat-truncation" or "-Werror=stringop-truncation" and for push-pop + support (outside function bodies) */ +#undef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION_BESIDEFUNC + +/* define if your compiler has pragmas for GCC diagnostic ignored + "-Wunreachable-code(-break)" and for push-pop support */ +#undef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE + +/* define if your compiler has pragmas for GCC diagnostic ignored + "-Wunreachable-code(-break)" and for push-pop support (outside function + bodies) */ +#undef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_BESIDEFUNC + +/* define if your compiler has #pragma clang diagnostic ignored + "-Wunreachable-code-return" */ +#undef HAVE_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_RETURN + +/* define if your compiler has #pragma clang diagnostic ignored + "-Wunreachable-code-return" (outside functions) */ +#undef HAVE_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_RETURN_BESIDEFUNC + +/* define if your compiler has #pragma clang diagnostic push and pop */ +#undef HAVE_PRAGMA_CLANG_DIAGNOSTIC_PUSH_POP + +/* define if your compiler has #pragma clang diagnostic push and pop outside + function bodies */ +#undef HAVE_PRAGMA_CLANG_DIAGNOSTIC_PUSH_POP_BESIDEFUNC + +/* define if your compiler has #pragma clang diagnostic push and pop inside + function bodies */ +#undef HAVE_PRAGMA_CLANG_DIAGNOSTIC_PUSH_POP_INSIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored "-Warray-bounds" + */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ARRAY_BOUNDS + +/* define if your compiler has #pragma GCC diagnostic ignored "-Warray-bounds" + (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ARRAY_BOUNDS_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored "-Wassign-enum" + */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ASSIGN_ENUM + +/* define if your compiler has #pragma GCC diagnostic ignored "-Wassign-enum" + (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ASSIGN_ENUM_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored "-Wcast-align" + */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN + +/* define if your compiler has #pragma GCC diagnostic ignored "-Wcast-align" + (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wcovered-switch-default" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wcovered-switch-default" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored "-Wc++98-compat" + */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT + +/* define if your compiler has #pragma GCC diagnostic ignored "-Wc++98-compat" + (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wc++98-compat-pedantic" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT_PEDANTIC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wc++98-compat-pedantic" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT_PEDANTIC_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wexit-time-destructors" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wexit-time-destructors" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wextra-semi-stmt" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wextra-semi-stmt" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wformat-nonliteral" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wformat-nonliteral" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wformat-overflow" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_OVERFLOW + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wformat-overflow" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_OVERFLOW_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wformat-security" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wformat-security" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wformat-truncation" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wformat-truncation" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wglobal-constructors" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wglobal-constructors" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored "-Wsign-compare" + */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_COMPARE + +/* define if your compiler has #pragma GCC diagnostic ignored "-Wsign-compare" + (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_COMPARE_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wsign-conversion" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wsign-conversion" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wstrict-prototypes" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wstrict-prototypes" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wstringop-truncation" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRINGOP_TRUNCATION + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wstringop-truncation" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRINGOP_TRUNCATION_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wtautological-compare" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wtautological-compare" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wtautological-constant-out-of-range-compare" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wtautological-constant-out-of-range-compare" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wtautological-type-limit-compare" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wtautological-type-limit-compare" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wtautological-unsigned-zero-compare" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wtautological-unsigned-zero-compare" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored "-Wtype-limits" + */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS + +/* define if your compiler has #pragma GCC diagnostic ignored "-Wtype-limits" + (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wunreachable-code" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wunreachable-code" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wunreachable-code-break" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_BREAK + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wunreachable-code-break" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_BREAK_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wunreachable-code-return" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_RETURN + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wunreachable-code-return" (outside functions) */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_RETURN_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic ignored + "-Wunused-function" */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNUSED_FUNCTION + +/* define if your compiler has #pragma GCC diagnostic push and pop */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP + +/* define if your compiler has #pragma GCC diagnostic push and pop outside + function bodies */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC + +/* define if your compiler has #pragma GCC diagnostic push and pop inside + function bodies */ +#undef HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC + +/* define if your libc can printf("%s", NULL) sanely */ +#undef HAVE_PRINTF_STRING_NULL + /* Define to enable pthread support code */ #undef HAVE_PTHREAD +/* Define to enable pthread_tryjoin support code */ +#undef HAVE_PTHREAD_TRYJOIN + +/* Define to 1 if you have the `round' function. */ +#undef HAVE_ROUND + +/* Define to 1 if you have with usable sem_t sem_init() and + sem_destroy(). */ +#undef HAVE_SEMAPHORE + +/* Define to 1 if you have . */ +#undef HAVE_SEMAPHORE_H + /* Define to 1 if you have the `setenv' function. */ #undef HAVE_SETENV @@ -197,14 +617,23 @@ /* Define to 1 if you have the `setsid' function. */ #undef HAVE_SETSID +/* Define to 1 if you have the `sigaction' function. */ +#undef HAVE_SIGACTION + +/* Define to 1 if you have the `sigemptyset' function. */ +#undef HAVE_SIGEMPTYSET + +/* Define to 1 if you have . */ +#undef HAVE_SIGNAL_H + /* Define to 1 if you have the `snprintf' function. */ #undef HAVE_SNPRINTF -/* Define to enable SSL development code */ -#undef HAVE_SSL +/* Define to 1 if you have the `SSL_CTX_new' function. */ +#undef HAVE_SSL_CTX_NEW -/* Define to 1 if you have the `SSL_library_init' function. */ -#undef HAVE_SSL_LIBRARY_INIT +/* Define to 1 if you have the header file. */ +#undef HAVE_SSL_H /* Define to 1 if you have the header file. */ #undef HAVE_STDARG_H @@ -215,21 +644,46 @@ /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H +/* defined if standard library has, and C standard allows, the + strcasecmp(s1,s2) method */ +#undef HAVE_STRCASECMP + +/* defined if standard library has, and C standard allows, the strdup(s) + method */ +#undef HAVE_STRDUP + /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR -/* Define to 1 if you have the header file. */ +/* Define to 1 if you have . */ #undef HAVE_STRINGS_H -/* Define to 1 if you have the header file. */ +/* Define to 1 if you have . */ #undef HAVE_STRING_H +/* defined if standard library has, and C standard allows, the + strncasecmp(s1,s2,n) method */ +#undef HAVE_STRNCASECMP + /* Define to 1 if you have the `strptime' function. */ #undef HAVE_STRPTIME +/* Define to 1 if you have the `strtok_r' function. */ +#undef HAVE_STRTOK_R + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_MODEM_H +/* Define to 1 if you have with usable struct rlimit and + getrlimit(). */ +#undef HAVE_SYS_RESOURCE_H + +/* Define to 1 if you have . */ +#undef HAVE_SYS_SIGNAL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_SOCKET_H + /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H @@ -248,9 +702,18 @@ /* Define to 1 if you have the `tcsendbreak' function. */ #undef HAVE_TCSENDBREAK +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TIME_H + /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H +/* Define to 1 if the system has the type `unsigned long long int'. */ +#undef HAVE_UNSIGNED_LONG_LONG_INT + /* Define to 1 if you have the `usb_detach_kernel_driver_np' function. */ #undef HAVE_USB_DETACH_KERNEL_DRIVER_NP @@ -260,6 +723,10 @@ /* Define to 1 if you have the `usb_init' function. */ #undef HAVE_USB_INIT +/* defined if standard library has, and C standard allows, the usleep(us) + method */ +#undef HAVE_USLEEP + /* Use uu_lock for locking (FreeBSD) */ #undef HAVE_UU_LOCK @@ -269,17 +736,44 @@ /* Define to 1 if you have the `vsnprintf' function. */ #undef HAVE_VSNPRINTF +/* Define to 1 if you have the windows.h header file. */ +#undef HAVE_WINDOWS_H + +/* Define to 1 if you have the winsock2.h header file. */ +#undef HAVE_WINSOCK2_H + /* Define to enable libwrap support */ #undef HAVE_WRAP +/* Define to 1 if you have the ws2tcpip.h header file. */ +#undef HAVE_WS2TCPIP_H + +/* define if your compiler has __attribute__ */ +#undef HAVE___ATTRIBUTE__ + +/* define if your compiler has __attribute__((noreturn)) */ +#undef HAVE___ATTRIBUTE__NORETURN + +/* define if your compiler has __attribute__((unused)) for function arguments + */ +#undef HAVE___ATTRIBUTE__UNUSED_ARG + +/* define if your compiler has __attribute__((unused)) for functions */ +#undef HAVE___ATTRIBUTE__UNUSED_FUNC + /* Default path for HTML files */ #undef HTMLPATH +/* Default path for system libraries */ +#undef LIBDIR + +/* Default path for system exec-libraries */ +#undef LIBEXECDIR + /* Desired syslog facility - see syslog(3) */ #undef LOG_FACILITY -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ +/* Define to the sub-directory where libtool stores uninstalled libraries. */ #undef LT_OBJDIR /* Define to use explicit getopt declarations */ @@ -288,8 +782,53 @@ /* Define if getopt.h is needed */ #undef NEED_GETOPT_H -/* Define to 1 if your C compiler doesn't accept -c and -o together. */ -#undef NO_MINUS_C_MINUS_O +/* Variable or macro by this name is not resolvable */ +#undef NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 + +/* Variable or macro by this name is not resolvable */ +#undef NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol + +/* Variable or macro by this name is not resolvable */ +#undef NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol + +/* Variable or macro by this name is not resolvable */ +#undef NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol + +/* Variable or macro by this name is not resolvable */ +#undef NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol + +/* Variable or macro by this name is not resolvable */ +#undef NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol + +/* Variable or macro by this name is not resolvable */ +#undef NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol + +/* Variable or macro by this name is not resolvable */ +#undef NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol + +/* Variable or macro by this name is not resolvable */ +#undef NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol + +/* Variable or macro by this name is not resolvable */ +#undef NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol + +/* Variable or macro by this name is not resolvable */ +#undef NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol + +/* ${COMMENT} */ +#undef NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32 + +/* ${COMMENT} */ +#undef NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields + +/* ${COMMENT} */ +#undef NUT_MODBUS_TIMEOUT_ARG_timeval + +/* ${COMMENT} */ +#undef NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields + +/* NUT network protocol version */ +#undef NUT_NETVERSION /* Name of package */ #undef PACKAGE @@ -324,6 +863,12 @@ /* User to switch to if started as root */ #undef RUN_AS_USER +/* Default path for system executables */ +#undef SBINDIR + +/* The size of `void *', as computed by sizeof. */ +#undef SIZEOF_VOID_P + /* Path for UPS driver state files */ #undef STATEPATH @@ -333,7 +878,8 @@ /* Define to 1 for Sun version of the libusb. */ #undef SUN_LIBUSB -/* Define to 1 if you can safely include both and . */ +/* Define to 1 if you can safely include both and . This + macro is deemed obsolete by autotools. */ #undef TIME_WITH_SYS_TIME /* NUT tree version */ @@ -376,12 +922,12 @@ /* Define to enable development files support */ #undef WITH_DEV +/* Define to enable overall documentation generation */ +#undef WITH_DOCS + /* Define to enable IPMI support using FreeIPMI */ #undef WITH_FREEIPMI -/* Define to enable HAL support */ -#undef WITH_HAL - /* Define to enable IPMI support */ #undef WITH_IPMI @@ -391,16 +937,38 @@ /* Define to enable Powerman PDU support */ #undef WITH_LIBPOWERMAN +/* Define to 1 for version 0.1 of the libusb (via pkg-config or + libusb-config). */ +#undef WITH_LIBUSB_0_1 + +/* Define to 1 for version 1.0 of the libusb (via pkg-config). */ +#undef WITH_LIBUSB_1_0 + +/* Define to enable I2C support */ +#undef WITH_LINUX_I2C + +/* Define to enable Mac OS X meta-driver */ +#undef WITH_MACOSX + +/* Define to enable Modbus support */ +#undef WITH_MODBUS + /* Define to enable Neon HTTP support */ #undef WITH_NEON +/* Define to enable SSL support using Mozilla NSS */ +#undef WITH_NSS + +/* Define to enable SSL support using OpenSSL */ +#undef WITH_OPENSSL + /* Define to enable serial support */ #undef WITH_SERIAL /* Define to enable SNMP support */ #undef WITH_SNMP -/* Define to enable SSL development code */ +/* Define to enable SSL */ #undef WITH_SSL /* Define to enable USB support */ diff --git a/include/extstate.h b/include/extstate.h index cb178c3..a511d62 100644 --- a/include/extstate.h +++ b/include/extstate.h @@ -1,26 +1,69 @@ -/* external state structures used by things like upsd */ +/* extstate.h - external state structures used by things like upsd -#ifndef EXTSTATE_H_SEEN -#define EXTSTATE_H_SEEN 1 + Copyright (C) 2001 Russell Kroll + 2005 Arnaud Quette + 2007 Peter Selinger + 2008 Arjen de Korte + 2013 Emilien Kia + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_EXTSTATE_H_SEEN +#define NUT_EXTSTATE_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif /* this could be made dynamic if someone really needs more than this... */ #define ST_MAX_VALUE_LEN 256 /* state tree flags */ -#define ST_FLAG_RW 0x0001 -#define ST_FLAG_STRING 0x0002 -#define ST_FLAG_IMMUTABLE 0x0004 +#define ST_FLAG_NONE 0x0000 +#define ST_FLAG_RW 0x0001 +#define ST_FLAG_STRING 0x0002 /* not STRING implies NUMBER */ +#define ST_FLAG_NUMBER 0x0004 +#define ST_FLAG_IMMUTABLE 0x0008 /* list of possible ENUM values */ typedef struct enum_s { - char *val; + char *val; struct enum_s *next; } enum_t; +/* RANGE boundaries */ +typedef struct range_s { + int min; + int max; + struct range_s *next; +} range_t; + /* list of instant commands */ typedef struct cmdlist_s { - char *name; - struct cmdlist_s *next; + char *name; + struct cmdlist_s *next; } cmdlist_t; -#endif /* EXTSTATE_H_SEEN */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_EXTSTATE_H_SEEN */ diff --git a/include/nut_float.h b/include/nut_float.h new file mode 100644 index 0000000..290edb4 --- /dev/null +++ b/include/nut_float.h @@ -0,0 +1,54 @@ +/* + * nut_float.h - Network UPS Tools include files for floating point routines + * and ways to compare if non-integers are close enough to assume equal + * + * Copyright (C) 2020 Jim Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef NUT_FLOAT_H_SEEN +#define NUT_FLOAT_H_SEEN 1 + +#include "config.h" + +#if defined HAVE_FLOAT_H +# include +#endif + +#if defined HAVE_MATH_H +# include +#endif + +/* Modern compilers frown upon direct comparisons of floating point numbers + * since their imprecise internal representations can misfire, and really we + * care if they are indiscernably close. To aviod warnings like -Wfloat-equal + * we prefer to compare with methods defined below. Note: despite the "floating" + * name, fabs() seems to be defined over doubles or wider types, e.g.: + * double fabs(double x); + * float fabsf(float x); + * long double fabsl(long double x); + * To be on the safer side, we compare the difference to be smaller or equal to + * the Epsilon for the respective numeric type, in order to be equivalent to a + * zero for our needs. Various libs define it as "the next representable number + * after 1.0 which is not equal to 1.0" for the discrete maths involved; no talk + * about exactly comparing it to zero or whether it is the smallest representable + * non-zero value... + */ +#define f_equal(x, y) ( fabsf((float)(x) - (float)(y)) <= FLT_EPSILON ) +#define d_equal(x, y) ( fabs((double)(x) - (double)(y)) <= DBL_EPSILON ) +#define ld_equal(x, y) ( fabsl((long double)(x) - (long double)(y)) <= LDBL_EPSILON ) + +#endif /* NUT_FLOAT_H_SEEN */ diff --git a/include/nut_platform.h b/include/nut_platform.h new file mode 100644 index 0000000..ba640c5 --- /dev/null +++ b/include/nut_platform.h @@ -0,0 +1,125 @@ +/** + * \brief Platform-specific checks + * + * The header performs checks to resolve the actual build platform. + * It defines macra that may be later used to produce platform-tailored + * code. + * + * Be careful when writing platform-specific code; avoid that if possible. + * + * References: + * http://nadeausoftware.com/articles/2012/01/c_c_tip_how_use_compiler_predefined_macros_detect_operating_system + * + * \author Vaclav Krpec + * \date 2012/10/12 + */ + +#ifndef NUT_PLATFORM_H_SEEN +#define NUT_PLATFORM_H_SEEN 1 + +/* + * In case doxygen source doc isn't generated + * (which is the case at time of writing this), + * just check the doxygen-documented (i.e. "**" + * prefixed) NUT_PLATFORM_* macra, below. + */ + +/* Apple Mac OS X, iOS and Darwin */ +#if (defined __APPLE__ && defined __MACH__) + /** Apple OS based on Mach ukernel */ + #define NUT_PLATFORM_APPLE_MACH + + #include + + #if (defined TARGET_OS_EMBEDDED) + /** iOS (implies \ref NUT_PLATFORM_APPLE_MACH) */ + #define NUT_PLATFORM_APPLE_IOS + #endif + #if (defined TARGET_IPHONE_SIMULATOR) + /** iOS simulator (implies \ref NUT_PLATFORM_APPLE_MACH) */ + #define NUT_PLATFORM_APPLE_IOS_SIMULATOR + #endif + #if (defined TARGET_OS_IPHONE) + /** iPhone (implies \ref NUT_PLATFORM_APPLE_MACH) */ + #define NUT_PLATFORM_APPLE_IPHONE + #endif + #if (defined TARGET_OS_MAC) + /** Mac OS X (implies \ref NUT_PLATFORM_APPLE_MACH) */ + #define NUT_PLATFORM_APPLE_OSX + #endif +#endif + +/* + * GCC AIX issue: __unix__ nor __unix are not defined in older GCC + * Addressed in GCC 4.7.0, see + * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39950 + * Remove if no longer necessary + */ +#if (defined _AIX && !defined __unix__) + #define __unix__ +#endif + +/* Microsoft Windows */ +#if (defined _WIN32 || defined _WIN64) + /** Windows */ + #define NUT_PLATFORM_MS_WINDOWS + + #if (defined NTDDI_WIN8 && NTDDI_VERSION >= NTDDI_WIN8) + /** Windows 8 */ + #define NUT_PLATFORM_MS_WINDOWS8 + #endif + +/* UNIX */ +/* Note that Apple OSX doesn't define __unix__ nor __unix; are they ashamed or something? */ +#elif (defined __unix__ || defined __unix || defined NUT_PLATFORM_APPLE_MACH) + #include + #include + + /** UNIX */ + #define NUT_PLATFORM_UNIX + + #if (defined _POSIX_VERSION) + /** POSIX (implies \ref NUT_PLATFORM_UNIX), expands to POSIX version */ + #define NUT_PLATFORM_POSIX _POSIX_VERSION + #endif + + #if (defined __linux__) + /** Linux (implies \ref NUT_PLATFORM_UNIX) */ + #define NUT_PLATFORM_LINUX + #endif + #if (defined __sun && defined __SVR4) + /** Solaris (implies \ref NUT_PLATFORM_UNIX) */ + #define NUT_PLATFORM_SOLARIS + #endif + #if (defined __hpux) + /** Hewlett-Packard HP-UX (implies \ref NUT_PLATFORM_UNIX) */ + #define NUT_PLATFORM_HPUX + #endif + #if (defined _AIX) + /** AIX (implies \ref NUT_PLATFORM_UNIX) */ + #define NUT_PLATFORM_AIX + #endif + + /* Note that BSD is defined in sys/param.h */ + #if (defined BSD) + /** BSD (implies \ref NUT_PLATFORM_UNIX) */ + #define NUT_PLATFORM_BSD + + #if (defined __DragonFly__) + /** DragonFly (implies \ref NUT_PLATFORM_UNIX, \ref NUT_PLATFORM_BSD) */ + #define NUT_PLATFORM_DRAGONFLY + #elif (defined __FreeBSD__) + /** FreeBSD (implies \ref NUT_PLATFORM_UNIX, \ref NUT_PLATFORM_BSD) */ + #define NUT_PLATFORM_FREEBSD + #elif (defined __OpenBSD__) + /** OpenBSD (implies \ref NUT_PLATFORM_UNIX, \ref NUT_PLATFORM_BSD) */ + #define NUT_PLATFORM_OPENBSD + #elif (defined __NetBSD__) + /** NetBSD (implies \ref NUT_PLATFORM_UNIX, \ref NUT_PLATFORM_BSD) */ + #define NUT_PLATFORM_NETBSD + #endif + #endif +#endif + +#endif /* NUT_PLATFORM_H_SEEN */ + diff --git a/include/nut_stdint.h b/include/nut_stdint.h index 35292ee..722038a 100644 --- a/include/nut_stdint.h +++ b/include/nut_stdint.h @@ -19,10 +19,18 @@ */ #ifndef NUT_STDINT_H_SEEN -#define NUT_STDINT_H_SEEN +#define NUT_STDINT_H_SEEN 1 #include "config.h" +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#ifndef __STDC_CONSTANT_MACROS +#define __STDC_CONSTANT_MACROS 1 +#endif + #if defined HAVE_INTTYPES_H # include #endif @@ -31,4 +39,33 @@ # include #endif +#if defined HAVE_LIMITS_H +# include +#endif + +#ifndef SIZE_MAX +#define SIZE_MAX ((size_t)(-1LL)) +#endif + +#ifndef SSIZE_MAX +#define SSIZE_MAX ((ssize_t)(-1LL)) +#endif + +/* Printing format for size_t and ssize_t */ +#ifndef PRIsize +# if defined(__MINGW32__) +# define PRIsize "u" +# else +# define PRIsize "zu" +# endif +#endif + +#ifndef PRIssize +# if defined(__MINGW32__) +# define PRIssize "d" +# else +# define PRIssize "zd" +# endif +#endif /* format for size_t and ssize_t */ + #endif /* NUT_STDINT_H_SEEN */ diff --git a/include/nut_version.h b/include/nut_version.h deleted file mode 100644 index 9db8b19..0000000 --- a/include/nut_version.h +++ /dev/null @@ -1,3 +0,0 @@ -/* Autogenerated file. Do not change. */ -/* This file was generated by "make". */ -#define NUT_VERSION_MACRO "2.6.3-3369:3371" diff --git a/include/parseconf.h b/include/parseconf.h index d85c89d..bb7584b 100644 --- a/include/parseconf.h +++ b/include/parseconf.h @@ -39,7 +39,7 @@ typedef struct { FILE *f; /* stream to current file */ int state; /* current parser state */ int ch; /* last character read */ - + char **arglist; /* array of pointers to words */ size_t *argsize; /* list of sizes for realloc */ size_t numargs; /* max usable in arglist */ diff --git a/include/proto.h b/include/proto.h index 2a7d654..c783755 100644 --- a/include/proto.h +++ b/include/proto.h @@ -1,8 +1,37 @@ -#ifndef PROTO_H -#define PROTO_H +/* proto.h - fill in the gaps about prototypes and definitions for portability + + Copyright (C) 2001 Russell Kroll + 2005 Arnaud Quette + 2006 Peter Selinger + 2013 Emilien Kia + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_PROTO_H_SEEN +#define NUT_PROTO_H_SEEN 1 #include "attribute.h" +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + #if !defined(HAVE_SNPRINTF) || !defined(HAVE_VSNPRINTF) /* Define this as a fall through, HAVE_STDARG_H is probably already set */ @@ -79,4 +108,10 @@ int vprintf(const char *, va_list); int putenv(char *); #endif -#endif /* PROTO_H */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_PROTO_H_SEEN */ diff --git a/include/state.h b/include/state.h index cb1814e..97249a2 100644 --- a/include/state.h +++ b/include/state.h @@ -1,6 +1,8 @@ /* state.h - Network UPS Tools common state management functions - Copyright (C) 2003 Russell Kroll + Copyright (C) + 2003 Russell Kroll + 2012 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,11 +19,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef STATE_H_SEEN -#define STATE_H_SEEN +#ifndef NUT_STATE_H_SEEN +#define NUT_STATE_H_SEEN 1 #include "extstate.h" +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + #define ST_SOCK_BUF_LEN 512 typedef struct st_tree_s { @@ -35,9 +43,10 @@ typedef struct st_tree_s { size_t safesize; int flags; - int aux; + long aux; struct enum_s *enum_list; + struct range_s *range_list; struct st_tree_s *left; struct st_tree_s *right; @@ -45,18 +54,27 @@ typedef struct st_tree_s { int state_setinfo(st_tree_t **nptr, const char *var, const char *val); int state_addenum(st_tree_t *root, const char *var, const char *val); +int state_addrange(st_tree_t *root, const char *var, const int min, const int max); int state_setaux(st_tree_t *root, const char *var, const char *auxs); const char *state_getinfo(st_tree_t *root, const char *var); int state_getflags(st_tree_t *root, const char *var); -int state_getaux(st_tree_t *root, const char *var); +long state_getaux(st_tree_t *root, const char *var); const enum_t *state_getenumlist(st_tree_t *root, const char *var); -void state_setflags(st_tree_t *root, const char *var, int numflags, char **flags); +const range_t *state_getrangelist(st_tree_t *root, const char *var); +void state_setflags(st_tree_t *root, const char *var, size_t numflags, char **flags); int state_addcmd(cmdlist_t **list, const char *cmd); void state_infofree(st_tree_t *node); void state_cmdfree(cmdlist_t *list); int state_delcmd(cmdlist_t **list, const char *cmd); int state_delinfo(st_tree_t **root, const char *var); int state_delenum(st_tree_t *root, const char *var, const char *val); +int state_delrange(st_tree_t *root, const char *var, const int min, const int max); st_tree_t *state_tree_find(st_tree_t *node, const char *var); -#endif /* STATE_H_SEEN */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_STATE_H_SEEN */ diff --git a/include/str.h b/include/str.h new file mode 100644 index 0000000..500c078 --- /dev/null +++ b/include/str.h @@ -0,0 +1,142 @@ +/* str.h - Common string-related functions + * + * Copyright (C) + * 2000 Russell Kroll + * 2015 Daniele Pezzini + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef NUT_STR_H_SEEN +#define NUT_STR_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/* Some compilers and/or C libraries do not handle printf("%s", NULL) correctly */ +#ifndef NUT_STRARG +# ifdef HAVE_PRINTF_STRING_NULL +# define NUT_STRARG(x) x +# else +# define NUT_STRARG(x) (x?x:"(null)") +# endif +#endif + +/* Remove all + * - leading and trailing (str_trim[_m]()) + * - leading (str_ltrim[_m]()) + * - trailing (str_rtrim[_m])) + * instances of + * - *character* (plain versions) + * - each character in *characters* ('_m' versions) + * from a string. + * - *string*: null-terminated byte string from which characters are to be removed; + * - *character*: character that has to be removed from *string*; + * - *characters*: null-terminated byte string of characters to be removed from string. + * Return: + * - NULL, if *string* is NULL, otherwise + * - *string* without the specified characters (upto an empty string). */ +char *str_trim(char *string, const char character); +char *str_trim_m(char *string, const char *characters); +char *str_ltrim(char *string, const char character); +char *str_ltrim_m(char *string, const char *characters); +char *str_rtrim(char *string, const char character); +char *str_rtrim_m(char *string, const char *characters); + +/* Remove all + * - leading and trailing (str_trim_space()) + * - leading (str_ltrim_space()) + * - trailing (str_rtrim_space()) + * spaces (as identified by isspace()) from a string. + * - *string*: null-terminated byte string from which spaces are to be removed. + * Return: + * - NULL, if *string* is NULL, otherwise + * - *string* without the specified spaces (upto an empty string). */ +char *str_trim_space(char *string); +char *str_ltrim_space(char *string); +char *str_rtrim_space(char *string); + +/* Tell whether a string can be converted to a number of type str_is_[_strict](). + * - *string*: the null-terminated byte string to check; + * - *base*: the base the string must conform to. + * The same restrictions of the corresponding str_to_[_strict]() functions apply. + * If *string* can be converted to a valid number of type , return 1. + * Otherwise, return 0 with errno set to: + * - ENOMEM, if available memory is insufficient; + * - EINVAL, if the value of *base* is not supported or no conversion could be performed; + * - ERANGE, if the converted value would be out of the acceptable range of . */ +int str_is_short(const char *string, const int base); +int str_is_short_strict(const char *string, const int base); +int str_is_ushort(const char *string, const int base); +int str_is_ushort_strict(const char *string, const int base); +int str_is_int(const char *string, const int base); +int str_is_int_strict(const char *string, const int base); +int str_is_uint(const char *string, const int base); +int str_is_uint_strict(const char *string, const int base); +int str_is_long(const char *string, const int base); +int str_is_long_strict(const char *string, const int base); +int str_is_ulong(const char *string, const int base); +int str_is_ulong_strict(const char *string, const int base); +int str_is_double(const char *string, const int base); +int str_is_double_strict(const char *string, const int base); + +/* Convert a string to a number of type str_to_[_strict](). + * - *string*: the null-terminated byte string to convert, + * 'strict' versions' strings shall not contain spaces (as identified by isspace()), + * - short, int, long: strtol()'s restrictions apply, + * - ushort, uint, ulong: strtoul()'s restrictions apply, plus: + * - plus ('+') and minus ('-') signs (and hence negative values) are not supported, + * - double: strtod()'s restrictions apply, plus: + * - infinity and nan are not supported, + * - radix character (decimal point character) must be a period ('.'); + * - *number*: a pointer to a that will be filled upon execution; + * - *base*: the base the string must conform to, + * - short, ushort, int, uint, long, ulong: acceptable values as in strtol()/strtoul(), + * - double: 0 for auto-select, 10 or 16. + * On success, return 1 with *number* being the result of the conversion of *string*. + * On failure, return 0 with *number* being 0 and errno set to: + * - ENOMEM, if available memory is insufficient; + * - EINVAL, if the value of *base* is not supported or no conversion can be performed; + * - ERANGE, if the converted value is out of the acceptable range of . */ +int str_to_short(const char *string, short *number, const int base); +int str_to_short_strict(const char *string, short *number, const int base); +int str_to_ushort(const char *string, unsigned short *number, const int base); +int str_to_ushort_strict(const char *string, unsigned short *number, const int base); +int str_to_int(const char *string, int *number, const int base); +int str_to_int_strict(const char *string, int *number, const int base); +int str_to_uint(const char *string, unsigned int *number, const int base); +int str_to_uint_strict(const char *string, unsigned int *number, const int base); +int str_to_long(const char *string, long *number, const int base); +int str_to_long_strict(const char *string, long *number, const int base); +int str_to_ulong(const char *string, unsigned long *number, const int base); +int str_to_ulong_strict(const char *string, unsigned long *number, const int base); +int str_to_double(const char *string, double *number, const int base); +int str_to_double_strict(const char *string, double *number, const int base); + +/* Return non-zero if string s ends exactly with suff + * Note: s=NULL always fails the test; otherwise suff=NULL always matches + */ +int str_ends_with(const char *s, const char *suff); +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_STR_H_SEEN */ diff --git a/include/timehead.h b/include/timehead.h index 13182b7..aabcf2a 100644 --- a/include/timehead.h +++ b/include/timehead.h @@ -1,13 +1,36 @@ -/* from the autoconf docs: sanely include the right time headers everywhere */ +/* timehead.h - from the autoconf docs: sanely include the right time headers everywhere -#if TIME_WITH_SYS_TIME + Copyright (C) 2001 Russell Kroll + 2005 Arnaud Quette + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_TIMEHEAD_H_SEEN +#define NUT_TIMEHEAD_H_SEEN 1 + +#ifdef TIME_WITH_SYS_TIME # include # include #else -# if HAVE_SYS_TIME_H +# ifdef HAVE_SYS_TIME_H # include # else # include # endif #endif +#endif /* NUT_TIMEHEAD_H_SEEN */ diff --git a/include/upsconf.h b/include/upsconf.h index 89c90ed..4ae1b27 100644 --- a/include/upsconf.h +++ b/include/upsconf.h @@ -1,7 +1,44 @@ +/* upsconf.h - prototypes for upsconf.c + + Copyright (C) 2001 Russell Kroll + 2005 Arnaud Quette + 2013 Emilien Kia + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_UPSCONF_H_SEEN +#define NUT_UPSCONF_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + /* callback function from read_upsconf */ void do_upsconf_args(char *upsname, char *var, char *val); - + /* open the ups.conf, parse it, and call back do_upsconf_args() */ void read_upsconf(void); - +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_UPSCONF_H_SEEN */ diff --git a/install-sh b/install-sh index 6781b98..ec298b5 100755 --- a/install-sh +++ b/install-sh @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2009-04-28.21; # UTC +scriptversion=2020-11-14.01; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -35,25 +35,21 @@ scriptversion=2009-04-28.21; # UTC # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent -# `make' implicit rules from creating a file called install from it +# 'make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. +tab=' ' nl=' ' -IFS=" "" $nl" +IFS=" $tab$nl" -# set DOITPROG to echo to test this script +# Set DOITPROG to "echo" to test this script. -# Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} -if test -z "$doit"; then - doit_exec=exec -else - doit_exec=$doit -fi +doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. @@ -68,22 +64,16 @@ mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} -posix_glob='?' -initialize_posix_glob=' - test "$posix_glob" != "?" || { - if (set -f) 2>/dev/null; then - posix_glob= - else - posix_glob=: - fi - } -' - posix_mkdir= # Desired mode of installed file. mode=0755 +# Create dirs (including intermediate dirs) using mode 755. +# This is like GNU 'install' as of coreutils 8.32 (2020). +mkdir_umask=22 + +backupsuffix= chgrpcmd= chmodcmd=$chmodprog chowncmd= @@ -97,7 +87,7 @@ dir_arg= dst_arg= copy_on_change=false -no_target_directory= +is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE @@ -114,18 +104,28 @@ Options: --version display version info and exit. -c (ignored) - -C install only if different (preserve the last data modification time) + -C install only if different (preserve data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. + -p pass -p to $cpprog. -s $stripprog installed files. + -S SUFFIX attempt to back up existing files, with suffix SUFFIX. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG + +By default, rm is invoked with -f; when overridden with RMPROG, +it's up to you to specify -f if you want it. + +If -S is not specified, no backups are attempted. + +Email bug reports to bug-automake@gnu.org. +Automake home page: https://www.gnu.org/software/automake/ " while test $# -ne 0; do @@ -137,42 +137,62 @@ while test $# -ne 0; do -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" - shift;; + shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 - case $mode in - *' '* | *' '* | *' -'* | *'*'* | *'?'* | *'['*) - echo "$0: invalid mode: $mode" >&2 - exit 1;; - esac - shift;; + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; -o) chowncmd="$chownprog $2" - shift;; + shift;; + + -p) cpprog="$cpprog -p";; -s) stripcmd=$stripprog;; - -t) dst_arg=$2 - shift;; + -S) backupsuffix="$2" + shift;; - -T) no_target_directory=true;; + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; - --) shift - break;; + --) shift + break;; - -*) echo "$0: invalid option: $1" >&2 - exit 1;; + -*) echo "$0: invalid option: $1" >&2 + exit 1;; *) break;; esac shift done +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. @@ -186,6 +206,10 @@ if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then fi shift # arg dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac done fi @@ -194,13 +218,26 @@ if test $# -eq 0; then echo "$0: no input file specified." >&2 exit 1 fi - # It's OK to call `install-sh -d' without argument. + # It's OK to call 'install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then - trap '(exit $?); exit' 1 2 13 15 + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. @@ -211,16 +248,16 @@ if test -z "$dir_arg"; then *[0-7]) if test -z "$stripcmd"; then - u_plus_rw= + u_plus_rw= else - u_plus_rw='% 200' + u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then - u_plus_rw= + u_plus_rw= else - u_plus_rw=,u+rw + u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac @@ -228,9 +265,9 @@ fi for src do - # Protect names starting with `-'. + # Protect names problematic for 'test' and other utilities. case $src in - -*) src=./$src;; + -* | [=\(\)!]) src=./$src;; esac if test -n "$dir_arg"; then @@ -238,6 +275,10 @@ do dstdir=$dst test -d "$dstdir" dstdir_status=$? + # Don't chown directories that already exist. + if test $dstdir_status = 0; then + chowncmd="" + fi else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command @@ -252,185 +293,150 @@ do echo "$0: no destination specified." >&2 exit 1 fi - dst=$dst_arg - # Protect names starting with `-'. - case $dst in - -*) dst=./$dst;; - esac - # If destination is a directory, append the input filename; won't work - # if double slashes aren't ignored. + # If destination is a directory, append the input filename. if test -d "$dst"; then - if test -n "$no_target_directory"; then - echo "$0: $dst_arg: Is a directory" >&2 - exit 1 + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 fi dstdir=$dst - dst=$dstdir/`basename "$src"` + dstbase=`basename "$src"` + case $dst in + */) dst=$dst$dstbase;; + *) dst=$dst/$dstbase;; + esac dstdir_status=0 else - # Prefer dirname, but fall back on a substitute if dirname fails. - dstdir=` - (dirname "$dst") 2>/dev/null || - expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$dst" : 'X\(//\)[^/]' \| \ - X"$dst" : 'X\(//\)$' \| \ - X"$dst" : 'X\(/\)' \| . 2>/dev/null || - echo X"$dst" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q' - ` - + dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi fi + case $dstdir in + */) dstdirslash=$dstdir;; + *) dstdirslash=$dstdir/;; + esac + obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') - # Create intermediate dirs using mode 755 as modified by the umask. - # This is like FreeBSD 'install' as of 1997-10-28. - umask=`umask` - case $stripcmd.$umask in - # Optimize common cases. - *[2367][2367]) mkdir_umask=$umask;; - .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi - *[0-7]) - mkdir_umask=`expr $umask + 22 \ - - $umask % 100 % 40 + $umask % 20 \ - - $umask % 10 % 4 + $umask % 2 - `;; - *) mkdir_umask=$umask,go-w;; - esac + posix_mkdir=false + # The $RANDOM variable is not portable (e.g., dash). Use it + # here however when possible just to lower collision chance. + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - # With -d, create the new directory with the user-specified mode. - # Otherwise, rely on $mkdir_umask. - if test -n "$dir_arg"; then - mkdir_mode=-m$mode + trap ' + ret=$? + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null + exit $ret + ' 0 + + # Because "mkdir -p" follows existing symlinks and we likely work + # directly in world-writeable /tmp, make sure that the '$tmpdir' + # directory is successfully created first before we actually test + # 'mkdir -p'. + if (umask $mkdir_umask && + $mkdirprog $mkdir_mode "$tmpdir" && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + test_tmpdir="$tmpdir/a" + ls_ld_tmpdir=`ls -ld "$test_tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" else - mkdir_mode= + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null fi - - posix_mkdir=false - case $umask in - *[123567][0-7][0-7]) - # POSIX mkdir -p sets u+wx bits regardless of umask, which - # is incompatible with FreeBSD 'install' when (umask & 300) != 0. - ;; - *) - tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 - - if (umask $mkdir_umask && - exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 - then - if test -z "$dir_arg" || { - # Check for POSIX incompatibilities with -m. - # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writeable bit of parent directory when it shouldn't. - # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. - ls_ld_tmpdir=`ls -ld "$tmpdir"` - case $ls_ld_tmpdir in - d????-?r-*) different_mode=700;; - d????-?--*) different_mode=755;; - *) false;; - esac && - $mkdirprog -m$different_mode -p -- "$tmpdir" && { - ls_ld_tmpdir_1=`ls -ld "$tmpdir"` - test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" - } - } - then posix_mkdir=: - fi - rmdir "$tmpdir/d" "$tmpdir" - else - # Remove any dirs left behind by ancient mkdir implementations. - rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null - fi - trap '' 0;; - esac;; + trap '' 0;; esac if $posix_mkdir && ( - umask $mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else - # The umask is ridiculous, or mkdir does not conform to POSIX, + # mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in - /*) prefix='/';; - -*) prefix='./';; - *) prefix='';; + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; esac - eval "$initialize_posix_glob" - oIFS=$IFS IFS=/ - $posix_glob set -f + set -f set fnord $dstdir shift - $posix_glob set +f + set +f IFS=$oIFS prefixes= for d do - test -z "$d" && continue + test X"$d" = X && continue - prefix=$prefix$d - if test -d "$prefix"; then - prefixes= - else - if $posix_mkdir; then - (umask=$mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break - # Don't fail if two instances are running concurrently. - test -d "$prefix" || exit 1 - else - case $prefix in - *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; - *) qprefix=$prefix;; - esac - prefixes="$prefixes '$qprefix'" - fi - fi - prefix=$prefix/ + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ done if test -n "$prefixes"; then - # Don't fail if two instances are running concurrently. - (umask $mkdir_umask && - eval "\$doit_exec \$mkdirprog $prefixes") || - test -d "$dstdir" || exit 1 - obsolete_mkdir_used=true + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true fi fi fi @@ -443,14 +449,25 @@ do else # Make a couple of temp file names in the proper directory. - dsttmp=$dstdir/_inst.$$_ - rmtmp=$dstdir/_rm.$$_ + dsttmp=${dstdirslash}_inst.$$_ + rmtmp=${dstdirslash}_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. - (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + (umask $cp_umask && + { test -z "$stripcmd" || { + # Create $dsttmp read-write so that cp doesn't create it read-only, + # which would cause strip to fail. + if test -z "$doit"; then + : >"$dsttmp" # No need to fork-exec 'touch'. + else + $doit touch "$dsttmp" + fi + } + } && + $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # @@ -465,20 +482,24 @@ do # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && - old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && - new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && - - eval "$initialize_posix_glob" && - $posix_glob set -f && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && - $posix_glob set +f && - + set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else + # If $backupsuffix is set, and the file being installed + # already exists, attempt a backup. Don't worry if it fails, + # e.g., if mv doesn't support -f. + if test -n "$backupsuffix" && test -f "$dst"; then + $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null + fi + # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || @@ -486,24 +507,24 @@ do # to itself, or perhaps because mv is so ancient that it does not # support -f. { - # Now remove or move aside any old file at destination location. - # We try this two ways since rm can't unlink itself on some - # systems and the destination file might be busy for other - # reasons. In this case, the final cleanup might fail but the new - # file should still install successfully. - { - test ! -f "$dst" || - $doit $rmcmd -f "$dst" 2>/dev/null || - { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && - { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } - } || - { echo "$0: cannot unlink or rename $dst" >&2 - (exit 1); exit 1 - } - } && + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && - # Now rename the file to the real destination. - $doit $mvcmd "$dsttmp" "$dst" + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 @@ -512,9 +533,9 @@ do done # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff --git a/lib/Makefile.am b/lib/Makefile.am index 0838407..f4d36cc 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -2,8 +2,11 @@ if WITH_DEV if WITH_PKG_CONFIG - pkgconfig_DATA = libupsclient.pc libnutscan.pc + pkgconfig_DATA = libupsclient.pc libnutscan.pc libnutclient.pc libnutclientstub.pc else bin_SCRIPTS = libupsclient-config endif endif + +CLEANFILES = *-spellchecked +MAINTAINERCLEANFILES = Makefile.in .dirstamp diff --git a/lib/Makefile.in b/lib/Makefile.in index fb06b28..6414edd 100644 --- a/lib/Makefile.in +++ b/lib/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -19,6 +18,61 @@ VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -39,37 +93,47 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = lib -DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ - $(srcdir)/libnutscan.pc.in $(srcdir)/libupsclient-config.in \ - $(srcdir)/libupsclient.pc.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h -CONFIG_CLEAN_FILES = libupsclient-config libupsclient.pc libnutscan.pc +CONFIG_CLEAN_FILES = libupsclient-config libupsclient.pc \ + libnutclient.pc libnutclientstub.pc libnutscan.pc CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ @@ -92,17 +156,48 @@ am__nobase_list = $(am__nobase_strip_setup); \ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgconfigdir)" SCRIPTS = $(bin_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac DATA = $(pkgconfig_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/libnutclient.pc.in \ + $(srcdir)/libnutclientstub.pc.in $(srcdir)/libnutscan.pc.in \ + $(srcdir)/libupsclient-config.in $(srcdir)/libupsclient.pc.in \ + README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -113,16 +208,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -131,11 +235,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -145,14 +246,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -163,21 +265,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -191,35 +302,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -230,8 +352,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -250,18 +376,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -271,8 +400,10 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ -@WITH_DEV_TRUE@@WITH_PKG_CONFIG_TRUE@pkgconfig_DATA = libupsclient.pc libnutscan.pc +@WITH_DEV_TRUE@@WITH_PKG_CONFIG_TRUE@pkgconfig_DATA = libupsclient.pc libnutscan.pc libnutclient.pc libnutclientstub.pc @WITH_DEV_TRUE@@WITH_PKG_CONFIG_FALSE@bin_SCRIPTS = libupsclient-config +CLEANFILES = *-spellchecked +MAINTAINERCLEANFILES = Makefile.in .dirstamp all: all-am .SUFFIXES: @@ -288,14 +419,13 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lib/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu lib/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -310,12 +440,19 @@ libupsclient-config: $(top_builddir)/config.status $(srcdir)/libupsclient-config cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ libupsclient.pc: $(top_builddir)/config.status $(srcdir)/libupsclient.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +libnutclient.pc: $(top_builddir)/config.status $(srcdir)/libnutclient.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +libnutclientstub.pc: $(top_builddir)/config.status $(srcdir)/libnutclientstub.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ libnutscan.pc: $(top_builddir)/config.status $(srcdir)/libnutscan.pc.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-binSCRIPTS: $(bin_SCRIPTS) @$(NORMAL_INSTALL) - test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ @@ -343,9 +480,7 @@ uninstall-binSCRIPTS: @list='$(bin_SCRIPTS)'; test -n "$(bindir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ - test -n "$$list" || exit 0; \ - echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(bindir)" && rm -f $$files + dir='$(DESTDIR)$(bindir)'; $(am__uninstall_files_from_dir) mostlyclean-libtool: -rm -f *.lo @@ -354,8 +489,11 @@ clean-libtool: -rm -rf .libs _libs install-pkgconfigDATA: $(pkgconfig_DATA) @$(NORMAL_INSTALL) - test -z "$(pkgconfigdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -369,17 +507,18 @@ uninstall-pkgconfigDATA: @$(NORMAL_UNINSTALL) @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - test -n "$$files" || exit 0; \ - echo " ( cd '$(DESTDIR)$(pkgconfigdir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(pkgconfigdir)" && rm -f $$files -tags: TAGS -TAGS: + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) +tags TAGS: -ctags: CTAGS -CTAGS: +ctags CTAGS: + +cscope cscopelist: -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -426,13 +565,19 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) @@ -441,6 +586,7 @@ distclean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am @@ -510,17 +656,19 @@ uninstall-am: uninstall-binSCRIPTS uninstall-pkgconfigDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ - distclean distclean-generic distclean-libtool distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-binSCRIPTS install-data install-data-am install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-pkgconfigDATA install-ps \ - install-ps-am install-strip installcheck installcheck-am \ - installdirs maintainer-clean maintainer-clean-generic \ - mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ - ps ps-am uninstall uninstall-am uninstall-binSCRIPTS \ - uninstall-pkgconfigDATA + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-binSCRIPTS install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-pkgconfigDATA install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ + uninstall-am uninstall-binSCRIPTS uninstall-pkgconfigDATA + +.PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/lib/README b/lib/README index fe8d182..6e3fac7 100644 --- a/lib/README +++ b/lib/README @@ -12,6 +12,7 @@ Introduction NUT provides several libraries, to ease interfacing with 3rd party programs: - *libupsclient*, to interact with NUT server (upsd), +- *libnutclient*, to interact with NUT server at high level, - *libnutscan*, to discover NUT supported devices. External applications, such as asapm-ups, wmnut, and others, currently use it. @@ -44,7 +45,7 @@ To get LD_FLAGS, use: References: linkman:libupsclient-config[1] manual page, -NOTE: this feature may evolve (name change), or even disapear in the future. +NOTE: this feature may evolve (name change), or even disappear in the future. pkgconfig support ----------------- @@ -111,11 +112,11 @@ to libupsclient-config if not available. It will issue an error if none is found! The same is also valid for other NUT libraries, such as libnutscan. -Simply replace 'libupsclient' occurences in the above example, by the name +Simply replace 'libupsclient' occurrences in the above example, by the name of the desired library (for example, 'libnutscan'). NOTE: this is an alternate method. Use of PKG_CHECK_MODULES macro should be -prefered. +preferred. Future consideration diff --git a/lib/libnutclient.pc.in b/lib/libnutclient.pc.in new file mode 100644 index 0000000..6b7b81d --- /dev/null +++ b/lib/libnutclient.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +sysconfdir=@CONFPATH@ +statepath=@STATEPATH@ +nutuser=@RUN_AS_USER@ + +Name: libnutclient +Description: UPS monitoring with Network UPS Tools +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lnutclient +Cflags: -I${includedir} diff --git a/lib/libnutclientstub.pc.in b/lib/libnutclientstub.pc.in new file mode 100644 index 0000000..b730a63 --- /dev/null +++ b/lib/libnutclientstub.pc.in @@ -0,0 +1,13 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ +sysconfdir=@CONFPATH@ +statepath=@STATEPATH@ +nutuser=@RUN_AS_USER@ + +Name: libnutclientstub +Description: Stub for UPS monitoring with Network UPS Tools +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lnutclientstub +Cflags: -I${includedir} diff --git a/lib/libupsclient.pc.in b/lib/libupsclient.pc.in index 4ad31fb..8efce40 100644 --- a/lib/libupsclient.pc.in +++ b/lib/libupsclient.pc.in @@ -9,5 +9,6 @@ nutuser=@RUN_AS_USER@ Name: libupsclient Description: UPS monitoring with Network UPS Tools Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lupsclient @LIBSSL_LIBS@ -Cflags: -I${includedir} @LIBSSL_CFLAGS@ +Libs: -L${libdir} -lupsclient +Cflags: -I${includedir} +Requires: @LIBSSL_REQUIRES@ diff --git a/ltmain.sh b/ltmain.sh index b4a3231..21e5e07 100755 --- a/ltmain.sh +++ b/ltmain.sh @@ -1,9 +1,12 @@ +#! /bin/sh +## DO NOT EDIT - This file generated from ./build-aux/ltmain.in +## by inline-source v2014-01-03.01 -# libtool (GNU libtool) 2.4 +# libtool (GNU libtool) 2.4.6 +# Provide generalized library-building support services. # Written by Gordon Matzigkeit , 1996 -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, -# 2007, 2008, 2009, 2010 Free Software Foundation, Inc. +# Copyright (C) 1996-2015 Free Software Foundation, Inc. # This is free software; see the source for copying conditions. There is NO # warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. @@ -23,170 +26,670 @@ # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with GNU Libtool; see the file COPYING. If not, a copy -# can be downloaded from http://www.gnu.org/licenses/gpl.html, -# or obtained by writing to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# along with this program. If not, see . -# Usage: $progname [OPTION]... [MODE-ARG]... -# -# Provide generalized library-building support services. -# -# --config show all configuration variables -# --debug enable verbose shell tracing -# -n, --dry-run display commands without modifying any files -# --features display basic configuration information and exit -# --mode=MODE use operation mode MODE -# --preserve-dup-deps don't remove duplicate dependency libraries -# --quiet, --silent don't print informational messages -# --no-quiet, --no-silent -# print informational messages (default) -# --tag=TAG use configuration variables from tag TAG -# -v, --verbose print more informational messages than default -# --no-verbose don't print the extra informational messages -# --version print version information -# -h, --help, --help-all print short, long, or detailed help message -# -# MODE must be one of the following: -# -# clean remove files from the build directory -# compile compile a source file into a libtool object -# execute automatically set library path, then run a program -# finish complete the installation of libtool libraries -# install install libraries or executables -# link create a library or an executable -# uninstall remove libraries from an installed directory -# -# MODE-ARGS vary depending on the MODE. When passed as first option, -# `--mode=MODE' may be abbreviated as `MODE' or a unique abbreviation of that. -# Try `$progname --help --mode=MODE' for a more detailed description of MODE. -# -# When reporting a bug, please describe a test case to reproduce it and -# include the following information: -# -# host-triplet: $host -# shell: $SHELL -# compiler: $LTCC -# compiler flags: $LTCFLAGS -# linker: $LD (gnu? $with_gnu_ld) -# $progname: (GNU libtool) 2.4 Debian-2.4-2ubuntu1 -# automake: $automake_version -# autoconf: $autoconf_version -# -# Report bugs to . -# GNU libtool home page: . -# General help using GNU software: . PROGRAM=libtool PACKAGE=libtool -VERSION="2.4 Debian-2.4-2ubuntu1" -TIMESTAMP="" -package_revision=1.3293 +VERSION="2.4.6 Debian-2.4.6-15" +package_revision=2.4.6 -# Be Bourne compatible -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + +## ------ ## +## Usage. ## +## ------ ## + +# Run './libtool --help' for help with using this script from the +# command line. + + +## ------------------------------- ## +## User overridable command paths. ## +## ------------------------------- ## + +# After configure completes, it has a better idea of some of the +# shell tools we need than the defaults used by the functions shared +# with bootstrap, so set those here where they can still be over- +# ridden by the user, but otherwise take precedence. + +: ${AUTOCONF="autoconf"} +: ${AUTOMAKE="automake"} + + +## -------------------------- ## +## Source external libraries. ## +## -------------------------- ## + +# Much of our low-level functionality needs to be sourced from external +# libraries, which are installed to $pkgauxdir. + +# Set a version string for this script. +scriptversion=2015-01-20.17; # UTC + +# General shell script boiler plate, and helper functions. +# Written by Gary V. Vaughan, 2004 + +# Copyright (C) 2004-2015 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. + +# As a special exception to the GNU General Public License, if you distribute +# this file as part of a program or library that is built using GNU Libtool, +# you may include this file under the same distribution terms that you use +# for the rest of that program. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNES FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Please report bugs or propose patches to gary@gnu.org. + + +## ------ ## +## Usage. ## +## ------ ## + +# Evaluate this file near the top of your script to gain access to +# the functions and variables defined here: +# +# . `echo "$0" | ${SED-sed} 's|[^/]*$||'`/build-aux/funclib.sh +# +# If you need to override any of the default environment variable +# settings, do that before evaluating this file. + + +## -------------------- ## +## Shell normalisation. ## +## -------------------- ## + +# Some shells need a little help to be as Bourne compatible as possible. +# Before doing anything else, make sure all that help has been provided! + +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else - case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac + case `(set -o) 2>/dev/null` in *posix*) set -o posix ;; esac fi -BIN_SH=xpg4; export BIN_SH # for Tru64 -DUALCASE=1; export DUALCASE # for MKS sh -# A function that is used when there is no print builtin or printf. -func_fallback_echo () -{ - eval 'cat <<_LTECHO_EOF -$1 -_LTECHO_EOF' -} - -# NLS nuisances: We save the old values to restore during execute mode. -lt_user_locale= -lt_safe_locale= -for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +# NLS nuisances: We save the old values in case they are required later. +_G_user_locale= +_G_safe_locale= +for _G_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES do - eval "if test \"\${$lt_var+set}\" = set; then - save_$lt_var=\$$lt_var - $lt_var=C - export $lt_var - lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" - lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" + eval "if test set = \"\${$_G_var+set}\"; then + save_$_G_var=\$$_G_var + $_G_var=C + export $_G_var + _G_user_locale=\"$_G_var=\\\$save_\$_G_var; \$_G_user_locale\" + _G_safe_locale=\"$_G_var=C; \$_G_safe_locale\" fi" done -LC_ALL=C -LANGUAGE=C -export LANGUAGE LC_ALL -$lt_unset CDPATH +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH +# Make sure IFS has a sensible default +sp=' ' +nl=' +' +IFS="$sp $nl" + +# There are apparently some retarded systems that use ';' as a PATH separator! +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + + +## ------------------------- ## +## Locate command utilities. ## +## ------------------------- ## + + +# func_executable_p FILE +# ---------------------- +# Check that FILE is an executable regular file. +func_executable_p () +{ + test -f "$1" && test -x "$1" +} + + +# func_path_progs PROGS_LIST CHECK_FUNC [PATH] +# -------------------------------------------- +# Search for either a program that responds to --version with output +# containing "GNU", or else returned by CHECK_FUNC otherwise, by +# trying all the directories in PATH with each of the elements of +# PROGS_LIST. +# +# CHECK_FUNC should accept the path to a candidate program, and +# set $func_check_prog_result if it truncates its output less than +# $_G_path_prog_max characters. +func_path_progs () +{ + _G_progs_list=$1 + _G_check_func=$2 + _G_PATH=${3-"$PATH"} + + _G_path_prog_max=0 + _G_path_prog_found=false + _G_save_IFS=$IFS; IFS=${PATH_SEPARATOR-:} + for _G_dir in $_G_PATH; do + IFS=$_G_save_IFS + test -z "$_G_dir" && _G_dir=. + for _G_prog_name in $_G_progs_list; do + for _exeext in '' .EXE; do + _G_path_prog=$_G_dir/$_G_prog_name$_exeext + func_executable_p "$_G_path_prog" || continue + case `"$_G_path_prog" --version 2>&1` in + *GNU*) func_path_progs_result=$_G_path_prog _G_path_prog_found=: ;; + *) $_G_check_func $_G_path_prog + func_path_progs_result=$func_check_prog_result + ;; + esac + $_G_path_prog_found && break 3 + done + done + done + IFS=$_G_save_IFS + test -z "$func_path_progs_result" && { + echo "no acceptable sed could be found in \$PATH" >&2 + exit 1 + } +} + + +# We want to be able to use the functions in this file before configure +# has figured out where the best binaries are kept, which means we have +# to search for them ourselves - except when the results are already set +# where we skip the searches. + +# Unless the user overrides by setting SED, search the path for either GNU +# sed, or the sed that truncates its output the least. +test -z "$SED" && { + _G_sed_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for _G_i in 1 2 3 4 5 6 7; do + _G_sed_script=$_G_sed_script$nl$_G_sed_script + done + echo "$_G_sed_script" 2>/dev/null | sed 99q >conftest.sed + _G_sed_script= + + func_check_prog_sed () + { + _G_path_prog=$1 + + _G_count=0 + printf 0123456789 >conftest.in + while : + do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo '' >> conftest.nl + "$_G_path_prog" -f conftest.sed conftest.out 2>/dev/null || break + diff conftest.out conftest.nl >/dev/null 2>&1 || break + _G_count=`expr $_G_count + 1` + if test "$_G_count" -gt "$_G_path_prog_max"; then + # Best one so far, save it but keep looking for a better one + func_check_prog_result=$_G_path_prog + _G_path_prog_max=$_G_count + fi + # 10*(2^10) chars as input seems more than enough + test 10 -lt "$_G_count" && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out + } + + func_path_progs "sed gsed" func_check_prog_sed $PATH:/usr/xpg4/bin + rm -f conftest.sed + SED=$func_path_progs_result +} + + +# Unless the user overrides by setting GREP, search the path for either GNU +# grep, or the grep that truncates its output the least. +test -z "$GREP" && { + func_check_prog_grep () + { + _G_path_prog=$1 + + _G_count=0 + _G_path_prog_max=0 + printf 0123456789 >conftest.in + while : + do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo 'GREP' >> conftest.nl + "$_G_path_prog" -e 'GREP$' -e '-(cannot match)-' conftest.out 2>/dev/null || break + diff conftest.out conftest.nl >/dev/null 2>&1 || break + _G_count=`expr $_G_count + 1` + if test "$_G_count" -gt "$_G_path_prog_max"; then + # Best one so far, save it but keep looking for a better one + func_check_prog_result=$_G_path_prog + _G_path_prog_max=$_G_count + fi + # 10*(2^10) chars as input seems more than enough + test 10 -lt "$_G_count" && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out + } + + func_path_progs "grep ggrep" func_check_prog_grep $PATH:/usr/xpg4/bin + GREP=$func_path_progs_result +} + + +## ------------------------------- ## +## User overridable command paths. ## +## ------------------------------- ## + +# All uppercase variable names are used for environment variables. These +# variables can be overridden by the user before calling a script that +# uses them if a suitable command of that name is not already available +# in the command search PATH. + +: ${CP="cp -f"} +: ${ECHO="printf %s\n"} +: ${EGREP="$GREP -E"} +: ${FGREP="$GREP -F"} +: ${LN_S="ln -s"} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} + + +## -------------------- ## +## Useful sed snippets. ## +## -------------------- ## + +sed_dirname='s|/[^/]*$||' +sed_basename='s|^.*/||' + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s|\([`"$\\]\)|\\\1|g' + +# Same as above, but do not quote variable references. +sed_double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Sed substitution that turns a string into a regex matching for the +# string literally. +sed_make_literal_regex='s|[].[^$\\*\/]|\\&|g' + +# Sed substitution that converts a w32 file name or path +# that contains forward slashes, into one that contains +# (escaped) backslashes. A very naive implementation. +sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + +# Re-'\' parameter expansions in output of sed_double_quote_subst that +# were '\'-ed in input to the same. If an odd number of '\' preceded a +# '$' in input to sed_double_quote_subst, that '$' was protected from +# expansion. Since each input '\' is now two '\'s, look for any number +# of runs of four '\'s followed by two '\'s and then a '$'. '\' that '$'. +_G_bs='\\' +_G_bs2='\\\\' +_G_bs4='\\\\\\\\' +_G_dollar='\$' +sed_double_backslash="\ + s/$_G_bs4/&\\ +/g + s/^$_G_bs2$_G_dollar/$_G_bs&/ + s/\\([^$_G_bs]\\)$_G_bs2$_G_dollar/\\1$_G_bs2$_G_bs$_G_dollar/g + s/\n//g" + + +## ----------------- ## +## Global variables. ## +## ----------------- ## + +# Except for the global variables explicitly listed below, the following +# functions in the '^func_' namespace, and the '^require_' namespace +# variables initialised in the 'Resource management' section, sourcing +# this file will not pollute your global namespace with anything +# else. There's no portable way to scope variables in Bourne shell +# though, so actually running these functions will sometimes place +# results into a variable named after the function, and often use +# temporary variables in the '^_G_' namespace. If you are careful to +# avoid using those namespaces casually in your sourcing script, things +# should continue to work as you expect. And, of course, you can freely +# overwrite any of the functions or variables defined here before +# calling anything to customize them. + +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +# Allow overriding, eg assuming that you follow the convention of +# putting '$debug_cmd' at the start of all your functions, you can get +# bash to show function call trace with: +# +# debug_cmd='echo "${FUNCNAME[0]} $*" >&2' bash your-script-name +debug_cmd=${debug_cmd-":"} +exit_cmd=: + +# By convention, finish your script with: +# +# exit $exit_status +# +# so that you can set exit_status to non-zero if you want to indicate +# something went wrong during execution without actually bailing out at +# the point of failure. +exit_status=$EXIT_SUCCESS # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of # the _XPG environment variable is not equal to 1 (one), the special # positional parameter $0, within a function call, is the name of the # function. -progpath="$0" +progpath=$0 + +# The name of this program. +progname=`$ECHO "$progpath" |$SED "$sed_basename"` + +# Make sure we have an absolute progpath for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=`$ECHO "$progpath" |$SED "$sed_dirname"` + progdir=`cd "$progdir" && pwd` + progpath=$progdir/$progname + ;; + *) + _G_IFS=$IFS + IFS=${PATH_SEPARATOR-:} + for progdir in $PATH; do + IFS=$_G_IFS + test -x "$progdir/$progname" && break + done + IFS=$_G_IFS + test -n "$progdir" || progdir=`pwd` + progpath=$progdir/$progname + ;; +esac +## ----------------- ## +## Standard options. ## +## ----------------- ## -: ${CP="cp -f"} -test "${ECHO+set}" = set || ECHO=${as_echo-'printf %s\n'} -: ${EGREP="/bin/grep -E"} -: ${FGREP="/bin/grep -F"} -: ${GREP="/bin/grep"} -: ${LN_S="ln -s"} -: ${MAKE="make"} -: ${MKDIR="mkdir"} -: ${MV="mv -f"} -: ${RM="rm -f"} -: ${SED="/bin/sed"} -: ${SHELL="${CONFIG_SHELL-/bin/sh}"} -: ${Xsed="$SED -e 1s/^X//"} +# The following options affect the operation of the functions defined +# below, and should be set appropriately depending on run-time para- +# meters passed on the command line. -# Global variables: -EXIT_SUCCESS=0 -EXIT_FAILURE=1 -EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. -EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. +opt_dry_run=false +opt_quiet=false +opt_verbose=false -exit_status=$EXIT_SUCCESS +# Categories 'all' and 'none' are always available. Append any others +# you will pass as the first argument to func_warning from your own +# code. +warning_categories= -# Make sure IFS has a sensible default -lt_nl=' -' -IFS=" $lt_nl" +# By default, display warnings according to 'opt_warning_types'. Set +# 'warning_func' to ':' to elide all warnings, or func_fatal_error to +# treat the next displayed warning as a fatal error. +warning_func=func_warn_and_continue -dirname="s,/[^/]*$,," -basename="s,^.*/,," +# Set to 'all' to display all warnings, 'none' to suppress all +# warnings, or a space delimited list of some subset of +# 'warning_categories' to display only the listed warnings. +opt_warning_types=all -# func_dirname file append nondir_replacement + +## -------------------- ## +## Resource management. ## +## -------------------- ## + +# This section contains definitions for functions that each ensure a +# particular resource (a file, or a non-empty configuration variable for +# example) is available, and if appropriate to extract default values +# from pertinent package files. Call them using their associated +# 'require_*' variable to ensure that they are executed, at most, once. +# +# It's entirely deliberate that calling these functions can set +# variables that don't obey the namespace limitations obeyed by the rest +# of this file, in order that that they be as useful as possible to +# callers. + + +# require_term_colors +# ------------------- +# Allow display of bold text on terminals that support it. +require_term_colors=func_require_term_colors +func_require_term_colors () +{ + $debug_cmd + + test -t 1 && { + # COLORTERM and USE_ANSI_COLORS environment variables take + # precedence, because most terminfo databases neglect to describe + # whether color sequences are supported. + test -n "${COLORTERM+set}" && : ${USE_ANSI_COLORS="1"} + + if test 1 = "$USE_ANSI_COLORS"; then + # Standard ANSI escape sequences + tc_reset='' + tc_bold=''; tc_standout='' + tc_red=''; tc_green='' + tc_blue=''; tc_cyan='' + else + # Otherwise trust the terminfo database after all. + test -n "`tput sgr0 2>/dev/null`" && { + tc_reset=`tput sgr0` + test -n "`tput bold 2>/dev/null`" && tc_bold=`tput bold` + tc_standout=$tc_bold + test -n "`tput smso 2>/dev/null`" && tc_standout=`tput smso` + test -n "`tput setaf 1 2>/dev/null`" && tc_red=`tput setaf 1` + test -n "`tput setaf 2 2>/dev/null`" && tc_green=`tput setaf 2` + test -n "`tput setaf 4 2>/dev/null`" && tc_blue=`tput setaf 4` + test -n "`tput setaf 5 2>/dev/null`" && tc_cyan=`tput setaf 5` + } + fi + } + + require_term_colors=: +} + + +## ----------------- ## +## Function library. ## +## ----------------- ## + +# This section contains a variety of useful functions to call in your +# scripts. Take note of the portable wrappers for features provided by +# some modern shells, which will fall back to slower equivalents on +# less featureful shells. + + +# func_append VAR VALUE +# --------------------- +# Append VALUE onto the existing contents of VAR. + + # We should try to minimise forks, especially on Windows where they are + # unreasonably slow, so skip the feature probes when bash or zsh are + # being used: + if test set = "${BASH_VERSION+set}${ZSH_VERSION+set}"; then + : ${_G_HAVE_ARITH_OP="yes"} + : ${_G_HAVE_XSI_OPS="yes"} + # The += operator was introduced in bash 3.1 + case $BASH_VERSION in + [12].* | 3.0 | 3.0*) ;; + *) + : ${_G_HAVE_PLUSEQ_OP="yes"} + ;; + esac + fi + + # _G_HAVE_PLUSEQ_OP + # Can be empty, in which case the shell is probed, "yes" if += is + # useable or anything else if it does not work. + test -z "$_G_HAVE_PLUSEQ_OP" \ + && (eval 'x=a; x+=" b"; test "a b" = "$x"') 2>/dev/null \ + && _G_HAVE_PLUSEQ_OP=yes + +if test yes = "$_G_HAVE_PLUSEQ_OP" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_append () + { + $debug_cmd + + eval "$1+=\$2" + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_append () + { + $debug_cmd + + eval "$1=\$$1\$2" + } +fi + + +# func_append_quoted VAR VALUE +# ---------------------------- +# Quote VALUE and append to the end of shell variable VAR, separated +# by a space. +if test yes = "$_G_HAVE_PLUSEQ_OP"; then + eval 'func_append_quoted () + { + $debug_cmd + + func_quote_for_eval "$2" + eval "$1+=\\ \$func_quote_for_eval_result" + }' +else + func_append_quoted () + { + $debug_cmd + + func_quote_for_eval "$2" + eval "$1=\$$1\\ \$func_quote_for_eval_result" + } +fi + + +# func_append_uniq VAR VALUE +# -------------------------- +# Append unique VALUE onto the existing contents of VAR, assuming +# entries are delimited by the first character of VALUE. For example: +# +# func_append_uniq options " --another-option option-argument" +# +# will only append to $options if " --another-option option-argument " +# is not already present somewhere in $options already (note spaces at +# each end implied by leading space in second argument). +func_append_uniq () +{ + $debug_cmd + + eval _G_current_value='`$ECHO $'$1'`' + _G_delim=`expr "$2" : '\(.\)'` + + case $_G_delim$_G_current_value$_G_delim in + *"$2$_G_delim"*) ;; + *) func_append "$@" ;; + esac +} + + +# func_arith TERM... +# ------------------ +# Set func_arith_result to the result of evaluating TERMs. + test -z "$_G_HAVE_ARITH_OP" \ + && (eval 'test 2 = $(( 1 + 1 ))') 2>/dev/null \ + && _G_HAVE_ARITH_OP=yes + +if test yes = "$_G_HAVE_ARITH_OP"; then + eval 'func_arith () + { + $debug_cmd + + func_arith_result=$(( $* )) + }' +else + func_arith () + { + $debug_cmd + + func_arith_result=`expr "$@"` + } +fi + + +# func_basename FILE +# ------------------ +# Set func_basename_result to FILE with everything up to and including +# the last / stripped. +if test yes = "$_G_HAVE_XSI_OPS"; then + # If this shell supports suffix pattern removal, then use it to avoid + # forking. Hide the definitions single quotes in case the shell chokes + # on unsupported syntax... + _b='func_basename_result=${1##*/}' + _d='case $1 in + */*) func_dirname_result=${1%/*}$2 ;; + * ) func_dirname_result=$3 ;; + esac' + +else + # ...otherwise fall back to using sed. + _b='func_basename_result=`$ECHO "$1" |$SED "$sed_basename"`' + _d='func_dirname_result=`$ECHO "$1" |$SED "$sed_dirname"` + if test "X$func_dirname_result" = "X$1"; then + func_dirname_result=$3 + else + func_append func_dirname_result "$2" + fi' +fi + +eval 'func_basename () +{ + $debug_cmd + + '"$_b"' +}' + + +# func_dirname FILE APPEND NONDIR_REPLACEMENT +# ------------------------------------------- # Compute the dirname of FILE. If nonempty, add APPEND to the result, # otherwise set result to NONDIR_REPLACEMENT. -func_dirname () +eval 'func_dirname () { - func_dirname_result=`$ECHO "${1}" | $SED "$dirname"` - if test "X$func_dirname_result" = "X${1}"; then - func_dirname_result="${3}" - else - func_dirname_result="$func_dirname_result${2}" - fi -} # func_dirname may be replaced by extended shell implementation + $debug_cmd + + '"$_d"' +}' -# func_basename file -func_basename () -{ - func_basename_result=`$ECHO "${1}" | $SED "$basename"` -} # func_basename may be replaced by extended shell implementation - - -# func_dirname_and_basename file append nondir_replacement -# perform func_basename and func_dirname in a single function +# func_dirname_and_basename FILE APPEND NONDIR_REPLACEMENT +# -------------------------------------------------------- +# Perform func_basename and func_dirname in a single function # call: # dirname: Compute the dirname of FILE. If nonempty, # add APPEND to the result, otherwise set result @@ -194,263 +697,327 @@ func_basename () # value returned in "$func_dirname_result" # basename: Compute filename of FILE. # value retuned in "$func_basename_result" -# Implementation must be kept synchronized with func_dirname -# and func_basename. For efficiency, we do not delegate to -# those functions but instead duplicate the functionality here. -func_dirname_and_basename () +# For efficiency, we do not delegate to the functions above but instead +# duplicate the functionality here. +eval 'func_dirname_and_basename () { - # Extract subdirectory from the argument. - func_dirname_result=`$ECHO "${1}" | $SED -e "$dirname"` - if test "X$func_dirname_result" = "X${1}"; then - func_dirname_result="${3}" - else - func_dirname_result="$func_dirname_result${2}" + $debug_cmd + + '"$_b"' + '"$_d"' +}' + + +# func_echo ARG... +# ---------------- +# Echo program name prefixed message. +func_echo () +{ + $debug_cmd + + _G_message=$* + + func_echo_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_IFS + $ECHO "$progname: $_G_line" + done + IFS=$func_echo_IFS +} + + +# func_echo_all ARG... +# -------------------- +# Invoke $ECHO with all args, space-separated. +func_echo_all () +{ + $ECHO "$*" +} + + +# func_echo_infix_1 INFIX ARG... +# ------------------------------ +# Echo program name, followed by INFIX on the first line, with any +# additional lines not showing INFIX. +func_echo_infix_1 () +{ + $debug_cmd + + $require_term_colors + + _G_infix=$1; shift + _G_indent=$_G_infix + _G_prefix="$progname: $_G_infix: " + _G_message=$* + + # Strip color escape sequences before counting printable length + for _G_tc in "$tc_reset" "$tc_bold" "$tc_standout" "$tc_red" "$tc_green" "$tc_blue" "$tc_cyan" + do + test -n "$_G_tc" && { + _G_esc_tc=`$ECHO "$_G_tc" | $SED "$sed_make_literal_regex"` + _G_indent=`$ECHO "$_G_indent" | $SED "s|$_G_esc_tc||g"` + } + done + _G_indent="$progname: "`echo "$_G_indent" | $SED 's|.| |g'`" " ## exclude from sc_prohibit_nested_quotes + + func_echo_infix_1_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_infix_1_IFS + $ECHO "$_G_prefix$tc_bold$_G_line$tc_reset" >&2 + _G_prefix=$_G_indent + done + IFS=$func_echo_infix_1_IFS +} + + +# func_error ARG... +# ----------------- +# Echo program name prefixed message to standard error. +func_error () +{ + $debug_cmd + + $require_term_colors + + func_echo_infix_1 " $tc_standout${tc_red}error$tc_reset" "$*" >&2 +} + + +# func_fatal_error ARG... +# ----------------------- +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + $debug_cmd + + func_error "$*" + exit $EXIT_FAILURE +} + + +# func_grep EXPRESSION FILENAME +# ----------------------------- +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $debug_cmd + + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_len STRING +# --------------- +# Set func_len_result to the length of STRING. STRING may not +# start with a hyphen. + test -z "$_G_HAVE_XSI_OPS" \ + && (eval 'x=a/b/c; + test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ + && _G_HAVE_XSI_OPS=yes + +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_len () + { + $debug_cmd + + func_len_result=${#1} + }' +else + func_len () + { + $debug_cmd + + func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` + } +fi + + +# func_mkdir_p DIRECTORY-PATH +# --------------------------- +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + $debug_cmd + + _G_directory_path=$1 + _G_dir_list= + + if test -n "$_G_directory_path" && test : != "$opt_dry_run"; then + + # Protect directory names starting with '-' + case $_G_directory_path in + -*) _G_directory_path=./$_G_directory_path ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$_G_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + _G_dir_list=$_G_directory_path:$_G_dir_list + + # If the last portion added has no slash in it, the list is done + case $_G_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + _G_directory_path=`$ECHO "$_G_directory_path" | $SED -e "$sed_dirname"` + done + _G_dir_list=`$ECHO "$_G_dir_list" | $SED 's|:*$||'` + + func_mkdir_p_IFS=$IFS; IFS=: + for _G_dir in $_G_dir_list; do + IFS=$func_mkdir_p_IFS + # mkdir can fail with a 'File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$_G_dir" 2>/dev/null || : + done + IFS=$func_mkdir_p_IFS + + # Bail out if we (or some other process) failed to create a directory. + test -d "$_G_directory_path" || \ + func_fatal_error "Failed to create '$1'" fi - func_basename_result=`$ECHO "${1}" | $SED -e "$basename"` -} # func_dirname_and_basename may be replaced by extended shell implementation +} -# func_stripname prefix suffix name -# strip PREFIX and SUFFIX off of NAME. -# PREFIX and SUFFIX must not contain globbing or regex special -# characters, hashes, percent signs, but SUFFIX may contain a leading -# dot (in which case that matches only a dot). -# func_strip_suffix prefix name -func_stripname () +# func_mktempdir [BASENAME] +# ------------------------- +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, BASENAME is the basename for that directory. +func_mktempdir () { - case ${2} in - .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; - *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; - esac -} # func_stripname may be replaced by extended shell implementation + $debug_cmd + _G_template=${TMPDIR-/tmp}/${1-$progname} + + if test : = "$opt_dry_run"; then + # Return a directory name, but don't create it in dry-run mode + _G_tmpdir=$_G_template-$$ + else + + # If mktemp works, use that first and foremost + _G_tmpdir=`mktemp -d "$_G_template-XXXXXXXX" 2>/dev/null` + + if test ! -d "$_G_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + _G_tmpdir=$_G_template-${RANDOM-0}$$ + + func_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$_G_tmpdir" + umask $func_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$_G_tmpdir" || \ + func_fatal_error "cannot create temporary directory '$_G_tmpdir'" + fi + + $ECHO "$_G_tmpdir" +} -# These SED scripts presuppose an absolute path with a trailing slash. -pathcar='s,^/\([^/]*\).*$,\1,' -pathcdr='s,^/[^/]*,,' -removedotparts=':dotsl - s@/\./@/@g - t dotsl - s,/\.$,/,' -collapseslashes='s@/\{1,\}@/@g' -finalslash='s,/*$,/,' # func_normal_abspath PATH +# ------------------------ # Remove doubled-up and trailing slashes, "." path components, # and cancel out any ".." path components in PATH after making # it an absolute path. -# value returned in "$func_normal_abspath_result" func_normal_abspath () { - # Start from root dir and reassemble the path. - func_normal_abspath_result= - func_normal_abspath_tpath=$1 - func_normal_abspath_altnamespace= - case $func_normal_abspath_tpath in - "") - # Empty path, that just means $cwd. - func_stripname '' '/' "`pwd`" - func_normal_abspath_result=$func_stripname_result - return - ;; - # The next three entries are used to spot a run of precisely - # two leading slashes without using negated character classes; - # we take advantage of case's first-match behaviour. - ///*) - # Unusual form of absolute path, do nothing. - ;; - //*) - # Not necessarily an ordinary path; POSIX reserves leading '//' - # and for example Cygwin uses it to access remote file shares - # over CIFS/SMB, so we conserve a leading double slash if found. - func_normal_abspath_altnamespace=/ - ;; - /*) - # Absolute path, do nothing. - ;; - *) - # Relative path, prepend $cwd. - func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath - ;; - esac - # Cancel out all the simple stuff to save iterations. We also want - # the path to end with a slash for ease of parsing, so make sure - # there is one (and only one) here. - func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ - -e "$removedotparts" -e "$collapseslashes" -e "$finalslash"` - while :; do - # Processed it all yet? - if test "$func_normal_abspath_tpath" = / ; then - # If we ascended to the root using ".." the result may be empty now. - if test -z "$func_normal_abspath_result" ; then - func_normal_abspath_result=/ - fi - break - fi - func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ - -e "$pathcar"` - func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ - -e "$pathcdr"` - # Figure out what to do with it - case $func_normal_abspath_tcomponent in + $debug_cmd + + # These SED scripts presuppose an absolute path with a trailing slash. + _G_pathcar='s|^/\([^/]*\).*$|\1|' + _G_pathcdr='s|^/[^/]*||' + _G_removedotparts=':dotsl + s|/\./|/|g + t dotsl + s|/\.$|/|' + _G_collapseslashes='s|/\{1,\}|/|g' + _G_finalslash='s|/*$|/|' + + # Start from root dir and reassemble the path. + func_normal_abspath_result= + func_normal_abspath_tpath=$1 + func_normal_abspath_altnamespace= + case $func_normal_abspath_tpath in "") - # Trailing empty path component, ignore it. - ;; - ..) - # Parent dir; strip last assembled component from result. - func_dirname "$func_normal_abspath_result" - func_normal_abspath_result=$func_dirname_result - ;; - *) - # Actual path component, append it. - func_normal_abspath_result=$func_normal_abspath_result/$func_normal_abspath_tcomponent - ;; - esac - done - # Restore leading double-slash if one was found on entry. - func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result -} - -# func_relative_path SRCDIR DSTDIR -# generates a relative path from SRCDIR to DSTDIR, with a trailing -# slash if non-empty, suitable for immediately appending a filename -# without needing to append a separator. -# value returned in "$func_relative_path_result" -func_relative_path () -{ - func_relative_path_result= - func_normal_abspath "$1" - func_relative_path_tlibdir=$func_normal_abspath_result - func_normal_abspath "$2" - func_relative_path_tbindir=$func_normal_abspath_result - - # Ascend the tree starting from libdir - while :; do - # check if we have found a prefix of bindir - case $func_relative_path_tbindir in - $func_relative_path_tlibdir) - # found an exact match - func_relative_path_tcancelled= - break + # Empty path, that just means $cwd. + func_stripname '' '/' "`pwd`" + func_normal_abspath_result=$func_stripname_result + return ;; - $func_relative_path_tlibdir*) - # found a matching prefix - func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" - func_relative_path_tcancelled=$func_stripname_result - if test -z "$func_relative_path_result"; then - func_relative_path_result=. - fi - break + # The next three entries are used to spot a run of precisely + # two leading slashes without using negated character classes; + # we take advantage of case's first-match behaviour. + ///*) + # Unusual form of absolute path, do nothing. + ;; + //*) + # Not necessarily an ordinary path; POSIX reserves leading '//' + # and for example Cygwin uses it to access remote file shares + # over CIFS/SMB, so we conserve a leading double slash if found. + func_normal_abspath_altnamespace=/ + ;; + /*) + # Absolute path, do nothing. ;; *) - func_dirname $func_relative_path_tlibdir - func_relative_path_tlibdir=${func_dirname_result} - if test "x$func_relative_path_tlibdir" = x ; then - # Have to descend all the way to the root! - func_relative_path_result=../$func_relative_path_result - func_relative_path_tcancelled=$func_relative_path_tbindir - break - fi - func_relative_path_result=../$func_relative_path_result + # Relative path, prepend $cwd. + func_normal_abspath_tpath=`pwd`/$func_normal_abspath_tpath ;; esac - done - # Now calculate path; take care to avoid doubling-up slashes. - func_stripname '' '/' "$func_relative_path_result" - func_relative_path_result=$func_stripname_result - func_stripname '/' '/' "$func_relative_path_tcancelled" - if test "x$func_stripname_result" != x ; then - func_relative_path_result=${func_relative_path_result}/${func_stripname_result} - fi - - # Normalisation. If bindir is libdir, return empty string, - # else relative path ending with a slash; either way, target - # file name can be directly appended. - if test ! -z "$func_relative_path_result"; then - func_stripname './' '' "$func_relative_path_result/" - func_relative_path_result=$func_stripname_result - fi + # Cancel out all the simple stuff to save iterations. We also want + # the path to end with a slash for ease of parsing, so make sure + # there is one (and only one) here. + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_removedotparts" -e "$_G_collapseslashes" -e "$_G_finalslash"` + while :; do + # Processed it all yet? + if test / = "$func_normal_abspath_tpath"; then + # If we ascended to the root using ".." the result may be empty now. + if test -z "$func_normal_abspath_result"; then + func_normal_abspath_result=/ + fi + break + fi + func_normal_abspath_tcomponent=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_pathcar"` + func_normal_abspath_tpath=`$ECHO "$func_normal_abspath_tpath" | $SED \ + -e "$_G_pathcdr"` + # Figure out what to do with it + case $func_normal_abspath_tcomponent in + "") + # Trailing empty path component, ignore it. + ;; + ..) + # Parent dir; strip last assembled component from result. + func_dirname "$func_normal_abspath_result" + func_normal_abspath_result=$func_dirname_result + ;; + *) + # Actual path component, append it. + func_append func_normal_abspath_result "/$func_normal_abspath_tcomponent" + ;; + esac + done + # Restore leading double-slash if one was found on entry. + func_normal_abspath_result=$func_normal_abspath_altnamespace$func_normal_abspath_result } -# The name of this program: -func_dirname_and_basename "$progpath" -progname=$func_basename_result -# Make sure we have an absolute path for reexecution: -case $progpath in - [\\/]*|[A-Za-z]:\\*) ;; - *[\\/]*) - progdir=$func_dirname_result - progdir=`cd "$progdir" && pwd` - progpath="$progdir/$progname" - ;; - *) - save_IFS="$IFS" - IFS=: - for progdir in $PATH; do - IFS="$save_IFS" - test -x "$progdir/$progname" && break - done - IFS="$save_IFS" - test -n "$progdir" || progdir=`pwd` - progpath="$progdir/$progname" - ;; -esac - -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -Xsed="${SED}"' -e 1s/^X//' -sed_quote_subst='s/\([`"$\\]\)/\\\1/g' - -# Same as above, but do not quote variable references. -double_quote_subst='s/\(["`\\]\)/\\\1/g' - -# Sed substitution that turns a string into a regex matching for the -# string literally. -sed_make_literal_regex='s,[].[^$\\*\/],\\&,g' - -# Sed substitution that converts a w32 file name or path -# which contains forward slashes, into one that contains -# (escaped) backslashes. A very naive implementation. -lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' - -# Re-`\' parameter expansions in output of double_quote_subst that were -# `\'-ed in input to the same. If an odd number of `\' preceded a '$' -# in input to double_quote_subst, that '$' was protected from expansion. -# Since each input `\' is now two `\'s, look for any number of runs of -# four `\'s followed by two `\'s and then a '$'. `\' that '$'. -bs='\\' -bs2='\\\\' -bs4='\\\\\\\\' -dollar='\$' -sed_double_backslash="\ - s/$bs4/&\\ -/g - s/^$bs2$dollar/$bs&/ - s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g - s/\n//g" - -# Standard options: -opt_dry_run=false -opt_help=false -opt_quiet=false -opt_verbose=false -opt_warning=: - -# func_echo arg... -# Echo program name prefixed message, along with the current mode -# name if it has been set yet. -func_echo () +# func_notquiet ARG... +# -------------------- +# Echo program name prefixed message only when not in quiet mode. +func_notquiet () { - $ECHO "$progname: ${opt_mode+$opt_mode: }$*" -} + $debug_cmd -# func_verbose arg... -# Echo program name prefixed message in verbose mode only. -func_verbose () -{ - $opt_verbose && func_echo ${1+"$@"} + $opt_quiet || func_echo ${1+"$@"} # A bug in bash halts the script if the last line of a function # fails when set -e is in force, so we need another command to @@ -458,450 +1025,1186 @@ func_verbose () : } -# func_echo_all arg... -# Invoke $ECHO with all args, space-separated. -func_echo_all () -{ - $ECHO "$*" -} -# func_error arg... -# Echo program name prefixed message to standard error. -func_error () +# func_relative_path SRCDIR DSTDIR +# -------------------------------- +# Set func_relative_path_result to the relative path from SRCDIR to DSTDIR. +func_relative_path () { - $ECHO "$progname: ${opt_mode+$opt_mode: }"${1+"$@"} 1>&2 -} + $debug_cmd -# func_warning arg... -# Echo program name prefixed warning message to standard error. -func_warning () -{ - $opt_warning && $ECHO "$progname: ${opt_mode+$opt_mode: }warning: "${1+"$@"} 1>&2 + func_relative_path_result= + func_normal_abspath "$1" + func_relative_path_tlibdir=$func_normal_abspath_result + func_normal_abspath "$2" + func_relative_path_tbindir=$func_normal_abspath_result + + # Ascend the tree starting from libdir + while :; do + # check if we have found a prefix of bindir + case $func_relative_path_tbindir in + $func_relative_path_tlibdir) + # found an exact match + func_relative_path_tcancelled= + break + ;; + $func_relative_path_tlibdir*) + # found a matching prefix + func_stripname "$func_relative_path_tlibdir" '' "$func_relative_path_tbindir" + func_relative_path_tcancelled=$func_stripname_result + if test -z "$func_relative_path_result"; then + func_relative_path_result=. + fi + break + ;; + *) + func_dirname $func_relative_path_tlibdir + func_relative_path_tlibdir=$func_dirname_result + if test -z "$func_relative_path_tlibdir"; then + # Have to descend all the way to the root! + func_relative_path_result=../$func_relative_path_result + func_relative_path_tcancelled=$func_relative_path_tbindir + break + fi + func_relative_path_result=../$func_relative_path_result + ;; + esac + done + + # Now calculate path; take care to avoid doubling-up slashes. + func_stripname '' '/' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + func_stripname '/' '/' "$func_relative_path_tcancelled" + if test -n "$func_stripname_result"; then + func_append func_relative_path_result "/$func_stripname_result" + fi + + # Normalisation. If bindir is libdir, return '.' else relative path. + if test -n "$func_relative_path_result"; then + func_stripname './' '' "$func_relative_path_result" + func_relative_path_result=$func_stripname_result + fi + + test -n "$func_relative_path_result" || func_relative_path_result=. - # bash bug again: : } -# func_fatal_error arg... -# Echo program name prefixed message to standard error, and exit. -func_fatal_error () -{ - func_error ${1+"$@"} - exit $EXIT_FAILURE -} -# func_fatal_help arg... -# Echo program name prefixed message to standard error, followed by -# a help hint, and exit. -func_fatal_help () -{ - func_error ${1+"$@"} - func_fatal_error "$help" -} -help="Try \`$progname --help' for more information." ## default - - -# func_grep expression filename -# Check whether EXPRESSION matches any line of FILENAME, without output. -func_grep () -{ - $GREP "$1" "$2" >/dev/null 2>&1 -} - - -# func_mkdir_p directory-path -# Make sure the entire path to DIRECTORY-PATH is available. -func_mkdir_p () -{ - my_directory_path="$1" - my_dir_list= - - if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then - - # Protect directory names starting with `-' - case $my_directory_path in - -*) my_directory_path="./$my_directory_path" ;; - esac - - # While some portion of DIR does not yet exist... - while test ! -d "$my_directory_path"; do - # ...make a list in topmost first order. Use a colon delimited - # list incase some portion of path contains whitespace. - my_dir_list="$my_directory_path:$my_dir_list" - - # If the last portion added has no slash in it, the list is done - case $my_directory_path in */*) ;; *) break ;; esac - - # ...otherwise throw away the child directory and loop - my_directory_path=`$ECHO "$my_directory_path" | $SED -e "$dirname"` - done - my_dir_list=`$ECHO "$my_dir_list" | $SED 's,:*$,,'` - - save_mkdir_p_IFS="$IFS"; IFS=':' - for my_dir in $my_dir_list; do - IFS="$save_mkdir_p_IFS" - # mkdir can fail with a `File exist' error if two processes - # try to create one of the directories concurrently. Don't - # stop in that case! - $MKDIR "$my_dir" 2>/dev/null || : - done - IFS="$save_mkdir_p_IFS" - - # Bail out if we (or some other process) failed to create a directory. - test -d "$my_directory_path" || \ - func_fatal_error "Failed to create \`$1'" - fi -} - - -# func_mktempdir [string] -# Make a temporary directory that won't clash with other running -# libtool processes, and avoids race conditions if possible. If -# given, STRING is the basename for that directory. -func_mktempdir () -{ - my_template="${TMPDIR-/tmp}/${1-$progname}" - - if test "$opt_dry_run" = ":"; then - # Return a directory name, but don't create it in dry-run mode - my_tmpdir="${my_template}-$$" - else - - # If mktemp works, use that first and foremost - my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` - - if test ! -d "$my_tmpdir"; then - # Failing that, at least try and use $RANDOM to avoid a race - my_tmpdir="${my_template}-${RANDOM-0}$$" - - save_mktempdir_umask=`umask` - umask 0077 - $MKDIR "$my_tmpdir" - umask $save_mktempdir_umask - fi - - # If we're not in dry-run mode, bomb out on failure - test -d "$my_tmpdir" || \ - func_fatal_error "cannot create temporary directory \`$my_tmpdir'" - fi - - $ECHO "$my_tmpdir" -} - - -# func_quote_for_eval arg -# Aesthetically quote ARG to be evaled later. -# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT -# is double-quoted, suitable for a subsequent eval, whereas -# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters -# which are still active within double quotes backslashified. +# func_quote_for_eval ARG... +# -------------------------- +# Aesthetically quote ARGs to be evaled later. +# This function returns two values: +# i) func_quote_for_eval_result +# double-quoted, suitable for a subsequent eval +# ii) func_quote_for_eval_unquoted_result +# has all characters that are still active within double +# quotes backslashified. func_quote_for_eval () { - case $1 in - *[\\\`\"\$]*) - func_quote_for_eval_unquoted_result=`$ECHO "$1" | $SED "$sed_quote_subst"` ;; - *) - func_quote_for_eval_unquoted_result="$1" ;; - esac + $debug_cmd - case $func_quote_for_eval_unquoted_result in - # Double-quote args containing shell metacharacters to delay - # word splitting, command substitution and and variable - # expansion for a subsequent eval. - # Many Bourne shells cannot handle close brackets correctly - # in scan sets, so we specify it separately. - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" - ;; - *) - func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" - esac + func_quote_for_eval_unquoted_result= + func_quote_for_eval_result= + while test 0 -lt $#; do + case $1 in + *[\\\`\"\$]*) + _G_unquoted_arg=`printf '%s\n' "$1" |$SED "$sed_quote_subst"` ;; + *) + _G_unquoted_arg=$1 ;; + esac + if test -n "$func_quote_for_eval_unquoted_result"; then + func_append func_quote_for_eval_unquoted_result " $_G_unquoted_arg" + else + func_append func_quote_for_eval_unquoted_result "$_G_unquoted_arg" + fi + + case $_G_unquoted_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and variable expansion + # for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + _G_quoted_arg=\"$_G_unquoted_arg\" + ;; + *) + _G_quoted_arg=$_G_unquoted_arg + ;; + esac + + if test -n "$func_quote_for_eval_result"; then + func_append func_quote_for_eval_result " $_G_quoted_arg" + else + func_append func_quote_for_eval_result "$_G_quoted_arg" + fi + shift + done } -# func_quote_for_expand arg +# func_quote_for_expand ARG +# ------------------------- # Aesthetically quote ARG to be evaled later; same as above, # but do not quote variable references. func_quote_for_expand () { + $debug_cmd + case $1 in *[\\\`\"]*) - my_arg=`$ECHO "$1" | $SED \ - -e "$double_quote_subst" -e "$sed_double_backslash"` ;; + _G_arg=`$ECHO "$1" | $SED \ + -e "$sed_double_quote_subst" -e "$sed_double_backslash"` ;; *) - my_arg="$1" ;; + _G_arg=$1 ;; esac - case $my_arg in + case $_G_arg in # Double-quote args containing shell metacharacters to delay # word splitting and command substitution for a subsequent eval. # Many Bourne shells cannot handle close brackets correctly # in scan sets, so we specify it separately. *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - my_arg="\"$my_arg\"" + _G_arg=\"$_G_arg\" ;; esac - func_quote_for_expand_result="$my_arg" + func_quote_for_expand_result=$_G_arg } -# func_show_eval cmd [fail_exp] -# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# func_stripname PREFIX SUFFIX NAME +# --------------------------------- +# strip PREFIX and SUFFIX from NAME, and store in func_stripname_result. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_stripname () + { + $debug_cmd + + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary variable first. + func_stripname_result=$3 + func_stripname_result=${func_stripname_result#"$1"} + func_stripname_result=${func_stripname_result%"$2"} + }' +else + func_stripname () + { + $debug_cmd + + case $2 in + .*) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%\\\\$2\$%%"`;; + *) func_stripname_result=`$ECHO "$3" | $SED -e "s%^$1%%" -e "s%$2\$%%"`;; + esac + } +fi + + +# func_show_eval CMD [FAIL_EXP] +# ----------------------------- +# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. func_show_eval () { - my_cmd="$1" - my_fail_exp="${2-:}" + $debug_cmd - ${opt_silent-false} || { - func_quote_for_expand "$my_cmd" - eval "func_echo $func_quote_for_expand_result" - } + _G_cmd=$1 + _G_fail_exp=${2-':'} - if ${opt_dry_run-false}; then :; else - eval "$my_cmd" - my_status=$? - if test "$my_status" -eq 0; then :; else - eval "(exit $my_status); $my_fail_exp" + func_quote_for_expand "$_G_cmd" + eval "func_notquiet $func_quote_for_expand_result" + + $opt_dry_run || { + eval "$_G_cmd" + _G_status=$? + if test 0 -ne "$_G_status"; then + eval "(exit $_G_status); $_G_fail_exp" fi - fi + } } -# func_show_eval_locale cmd [fail_exp] -# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# func_show_eval_locale CMD [FAIL_EXP] +# ------------------------------------ +# Unless opt_quiet is true, then output CMD. Then, if opt_dryrun is # not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP # is given, then evaluate it. Use the saved locale for evaluation. func_show_eval_locale () { - my_cmd="$1" - my_fail_exp="${2-:}" + $debug_cmd - ${opt_silent-false} || { - func_quote_for_expand "$my_cmd" + _G_cmd=$1 + _G_fail_exp=${2-':'} + + $opt_quiet || { + func_quote_for_expand "$_G_cmd" eval "func_echo $func_quote_for_expand_result" } - if ${opt_dry_run-false}; then :; else - eval "$lt_user_locale - $my_cmd" - my_status=$? - eval "$lt_safe_locale" - if test "$my_status" -eq 0; then :; else - eval "(exit $my_status); $my_fail_exp" + $opt_dry_run || { + eval "$_G_user_locale + $_G_cmd" + _G_status=$? + eval "$_G_safe_locale" + if test 0 -ne "$_G_status"; then + eval "(exit $_G_status); $_G_fail_exp" fi - fi + } } + # func_tr_sh +# ---------- # Turn $1 into a string suitable for a shell variable name. # Result is stored in $func_tr_sh_result. All characters # not in the set a-zA-Z0-9_ are replaced with '_'. Further, # if $1 begins with a digit, a '_' is prepended as well. func_tr_sh () { - case $1 in - [0-9]* | *[!a-zA-Z0-9_]*) - func_tr_sh_result=`$ECHO "$1" | $SED 's/^\([0-9]\)/_\1/; s/[^a-zA-Z0-9_]/_/g'` - ;; - * ) - func_tr_sh_result=$1 - ;; - esac + $debug_cmd + + case $1 in + [0-9]* | *[!a-zA-Z0-9_]*) + func_tr_sh_result=`$ECHO "$1" | $SED -e 's/^\([0-9]\)/_\1/' -e 's/[^a-zA-Z0-9_]/_/g'` + ;; + * ) + func_tr_sh_result=$1 + ;; + esac } -# func_version -# Echo version message to standard output and exit. -func_version () +# func_verbose ARG... +# ------------------- +# Echo program name prefixed message in verbose mode only. +func_verbose () { - $opt_debug + $debug_cmd - $SED -n '/(C)/!b go - :more - /\./!{ - N - s/\n# / / - b more - } - :go - /^# '$PROGRAM' (GNU /,/# warranty; / { - s/^# // - s/^# *$// - s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ - p - }' < "$progpath" - exit $? + $opt_verbose && func_echo "$*" + + : } -# func_usage -# Echo short help message to standard output and exit. -func_usage () + +# func_warn_and_continue ARG... +# ----------------------------- +# Echo program name prefixed warning message to standard error. +func_warn_and_continue () { - $opt_debug + $debug_cmd - $SED -n '/^# Usage:/,/^# *.*--help/ { - s/^# // - s/^# *$// - s/\$progname/'$progname'/ - p - }' < "$progpath" - echo - $ECHO "run \`$progname --help | more' for full usage" - exit $? + $require_term_colors + + func_echo_infix_1 "${tc_red}warning$tc_reset" "$*" >&2 } -# func_help [NOEXIT] -# Echo long help message to standard output and exit, -# unless 'noexit' is passed as argument. + +# func_warning CATEGORY ARG... +# ---------------------------- +# Echo program name prefixed warning message to standard error. Warning +# messages can be filtered according to CATEGORY, where this function +# elides messages where CATEGORY is not listed in the global variable +# 'opt_warning_types'. +func_warning () +{ + $debug_cmd + + # CATEGORY must be in the warning_categories list! + case " $warning_categories " in + *" $1 "*) ;; + *) func_internal_error "invalid warning category '$1'" ;; + esac + + _G_category=$1 + shift + + case " $opt_warning_types " in + *" $_G_category "*) $warning_func ${1+"$@"} ;; + esac +} + + +# func_sort_ver VER1 VER2 +# ----------------------- +# 'sort -V' is not generally available. +# Note this deviates from the version comparison in automake +# in that it treats 1.5 < 1.5.0, and treats 1.4.4a < 1.4-p3a +# but this should suffice as we won't be specifying old +# version formats or redundant trailing .0 in bootstrap.conf. +# If we did want full compatibility then we should probably +# use m4_version_compare from autoconf. +func_sort_ver () +{ + $debug_cmd + + printf '%s\n%s\n' "$1" "$2" \ + | sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n -k 5,5n -k 6,6n -k 7,7n -k 8,8n -k 9,9n +} + +# func_lt_ver PREV CURR +# --------------------- +# Return true if PREV and CURR are in the correct order according to +# func_sort_ver, otherwise false. Use it like this: +# +# func_lt_ver "$prev_ver" "$proposed_ver" || func_fatal_error "..." +func_lt_ver () +{ + $debug_cmd + + test "x$1" = x`func_sort_ver "$1" "$2" | $SED 1q` +} + + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-time-zone: "UTC" +# End: +#! /bin/sh + +# Set a version string for this script. +scriptversion=2015-10-07.11; # UTC + +# A portable, pluggable option parser for Bourne shell. +# Written by Gary V. Vaughan, 2010 + +# Copyright (C) 2010-2015 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Please report bugs or propose patches to gary@gnu.org. + + +## ------ ## +## Usage. ## +## ------ ## + +# This file is a library for parsing options in your shell scripts along +# with assorted other useful supporting features that you can make use +# of too. +# +# For the simplest scripts you might need only: +# +# #!/bin/sh +# . relative/path/to/funclib.sh +# . relative/path/to/options-parser +# scriptversion=1.0 +# func_options ${1+"$@"} +# eval set dummy "$func_options_result"; shift +# ...rest of your script... +# +# In order for the '--version' option to work, you will need to have a +# suitably formatted comment like the one at the top of this file +# starting with '# Written by ' and ending with '# warranty; '. +# +# For '-h' and '--help' to work, you will also need a one line +# description of your script's purpose in a comment directly above the +# '# Written by ' line, like the one at the top of this file. +# +# The default options also support '--debug', which will turn on shell +# execution tracing (see the comment above debug_cmd below for another +# use), and '--verbose' and the func_verbose function to allow your script +# to display verbose messages only when your user has specified +# '--verbose'. +# +# After sourcing this file, you can plug processing for additional +# options by amending the variables from the 'Configuration' section +# below, and following the instructions in the 'Option parsing' +# section further down. + +## -------------- ## +## Configuration. ## +## -------------- ## + +# You should override these variables in your script after sourcing this +# file so that they reflect the customisations you have added to the +# option parser. + +# The usage line for option parsing errors and the start of '-h' and +# '--help' output messages. You can embed shell variables for delayed +# expansion at the time the message is displayed, but you will need to +# quote other shell meta-characters carefully to prevent them being +# expanded when the contents are evaled. +usage='$progpath [OPTION]...' + +# Short help message in response to '-h' and '--help'. Add to this or +# override it after sourcing this library to reflect the full set of +# options your script accepts. +usage_message="\ + --debug enable verbose shell tracing + -W, --warnings=CATEGORY + report the warnings falling in CATEGORY [all] + -v, --verbose verbosely report processing + --version print version information and exit + -h, --help print short or long help message and exit +" + +# Additional text appended to 'usage_message' in response to '--help'. +long_help_message=" +Warning categories include: + 'all' show all warnings + 'none' turn off all the warnings + 'error' warnings are treated as fatal errors" + +# Help message printed before fatal option parsing errors. +fatal_help="Try '\$progname --help' for more information." + + + +## ------------------------- ## +## Hook function management. ## +## ------------------------- ## + +# This section contains functions for adding, removing, and running hooks +# to the main code. A hook is just a named list of of function, that can +# be run in order later on. + +# func_hookable FUNC_NAME +# ----------------------- +# Declare that FUNC_NAME will run hooks added with +# 'func_add_hook FUNC_NAME ...'. +func_hookable () +{ + $debug_cmd + + func_append hookable_fns " $1" +} + + +# func_add_hook FUNC_NAME HOOK_FUNC +# --------------------------------- +# Request that FUNC_NAME call HOOK_FUNC before it returns. FUNC_NAME must +# first have been declared "hookable" by a call to 'func_hookable'. +func_add_hook () +{ + $debug_cmd + + case " $hookable_fns " in + *" $1 "*) ;; + *) func_fatal_error "'$1' does not accept hook functions." ;; + esac + + eval func_append ${1}_hooks '" $2"' +} + + +# func_remove_hook FUNC_NAME HOOK_FUNC +# ------------------------------------ +# Remove HOOK_FUNC from the list of functions called by FUNC_NAME. +func_remove_hook () +{ + $debug_cmd + + eval ${1}_hooks='`$ECHO "\$'$1'_hooks" |$SED "s| '$2'||"`' +} + + +# func_run_hooks FUNC_NAME [ARG]... +# --------------------------------- +# Run all hook functions registered to FUNC_NAME. +# It is assumed that the list of hook functions contains nothing more +# than a whitespace-delimited list of legal shell function names, and +# no effort is wasted trying to catch shell meta-characters or preserve +# whitespace. +func_run_hooks () +{ + $debug_cmd + + _G_rc_run_hooks=false + + case " $hookable_fns " in + *" $1 "*) ;; + *) func_fatal_error "'$1' does not support hook funcions.n" ;; + esac + + eval _G_hook_fns=\$$1_hooks; shift + + for _G_hook in $_G_hook_fns; do + if eval $_G_hook '"$@"'; then + # store returned options list back into positional + # parameters for next 'cmd' execution. + eval _G_hook_result=\$${_G_hook}_result + eval set dummy "$_G_hook_result"; shift + _G_rc_run_hooks=: + fi + done + + $_G_rc_run_hooks && func_run_hooks_result=$_G_hook_result +} + + + +## --------------- ## +## Option parsing. ## +## --------------- ## + +# In order to add your own option parsing hooks, you must accept the +# full positional parameter list in your hook function, you may remove/edit +# any options that you action, and then pass back the remaining unprocessed +# options in '_result', escaped suitably for +# 'eval'. In this case you also must return $EXIT_SUCCESS to let the +# hook's caller know that it should pay attention to +# '_result'. Returning $EXIT_FAILURE signalizes that +# arguments are left untouched by the hook and therefore caller will ignore the +# result variable. +# +# Like this: +# +# my_options_prep () +# { +# $debug_cmd +# +# # Extend the existing usage message. +# usage_message=$usage_message' +# -s, --silent don'\''t print informational messages +# ' +# # No change in '$@' (ignored completely by this hook). There is +# # no need to do the equivalent (but slower) action: +# # func_quote_for_eval ${1+"$@"} +# # my_options_prep_result=$func_quote_for_eval_result +# false +# } +# func_add_hook func_options_prep my_options_prep +# +# +# my_silent_option () +# { +# $debug_cmd +# +# args_changed=false +# +# # Note that for efficiency, we parse as many options as we can +# # recognise in a loop before passing the remainder back to the +# # caller on the first unrecognised argument we encounter. +# while test $# -gt 0; do +# opt=$1; shift +# case $opt in +# --silent|-s) opt_silent=: +# args_changed=: +# ;; +# # Separate non-argument short options: +# -s*) func_split_short_opt "$_G_opt" +# set dummy "$func_split_short_opt_name" \ +# "-$func_split_short_opt_arg" ${1+"$@"} +# shift +# args_changed=: +# ;; +# *) # Make sure the first unrecognised option "$_G_opt" +# # is added back to "$@", we could need that later +# # if $args_changed is true. +# set dummy "$_G_opt" ${1+"$@"}; shift; break ;; +# esac +# done +# +# if $args_changed; then +# func_quote_for_eval ${1+"$@"} +# my_silent_option_result=$func_quote_for_eval_result +# fi +# +# $args_changed +# } +# func_add_hook func_parse_options my_silent_option +# +# +# my_option_validation () +# { +# $debug_cmd +# +# $opt_silent && $opt_verbose && func_fatal_help "\ +# '--silent' and '--verbose' options are mutually exclusive." +# +# false +# } +# func_add_hook func_validate_options my_option_validation +# +# You'll also need to manually amend $usage_message to reflect the extra +# options you parse. It's preferable to append if you can, so that +# multiple option parsing hooks can be added safely. + + +# func_options_finish [ARG]... +# ---------------------------- +# Finishing the option parse loop (call 'func_options' hooks ATM). +func_options_finish () +{ + $debug_cmd + + _G_func_options_finish_exit=false + if func_run_hooks func_options ${1+"$@"}; then + func_options_finish_result=$func_run_hooks_result + _G_func_options_finish_exit=: + fi + + $_G_func_options_finish_exit +} + + +# func_options [ARG]... +# --------------------- +# All the functions called inside func_options are hookable. See the +# individual implementations for details. +func_hookable func_options +func_options () +{ + $debug_cmd + + _G_rc_options=false + + for my_func in options_prep parse_options validate_options options_finish + do + if eval func_$my_func '${1+"$@"}'; then + eval _G_res_var='$'"func_${my_func}_result" + eval set dummy "$_G_res_var" ; shift + _G_rc_options=: + fi + done + + # Save modified positional parameters for caller. As a top-level + # options-parser function we always need to set the 'func_options_result' + # variable (regardless the $_G_rc_options value). + if $_G_rc_options; then + func_options_result=$_G_res_var + else + func_quote_for_eval ${1+"$@"} + func_options_result=$func_quote_for_eval_result + fi + + $_G_rc_options +} + + +# func_options_prep [ARG]... +# -------------------------- +# All initialisations required before starting the option parse loop. +# Note that when calling hook functions, we pass through the list of +# positional parameters. If a hook function modifies that list, and +# needs to propagate that back to rest of this script, then the complete +# modified list must be put in 'func_run_hooks_result' before +# returning $EXIT_SUCCESS (otherwise $EXIT_FAILURE is returned). +func_hookable func_options_prep +func_options_prep () +{ + $debug_cmd + + # Option defaults: + opt_verbose=false + opt_warning_types= + + _G_rc_options_prep=false + if func_run_hooks func_options_prep ${1+"$@"}; then + _G_rc_options_prep=: + # save modified positional parameters for caller + func_options_prep_result=$func_run_hooks_result + fi + + $_G_rc_options_prep +} + + +# func_parse_options [ARG]... +# --------------------------- +# The main option parsing loop. +func_hookable func_parse_options +func_parse_options () +{ + $debug_cmd + + func_parse_options_result= + + _G_rc_parse_options=false + # this just eases exit handling + while test $# -gt 0; do + # Defer to hook functions for initial option parsing, so they + # get priority in the event of reusing an option name. + if func_run_hooks func_parse_options ${1+"$@"}; then + eval set dummy "$func_run_hooks_result"; shift + _G_rc_parse_options=: + fi + + # Break out of the loop if we already parsed every option. + test $# -gt 0 || break + + _G_match_parse_options=: + _G_opt=$1 + shift + case $_G_opt in + --debug|-x) debug_cmd='set -x' + func_echo "enabling shell trace mode" + $debug_cmd + ;; + + --no-warnings|--no-warning|--no-warn) + set dummy --warnings none ${1+"$@"} + shift + ;; + + --warnings|--warning|-W) + if test $# = 0 && func_missing_arg $_G_opt; then + _G_rc_parse_options=: + break + fi + case " $warning_categories $1" in + *" $1 "*) + # trailing space prevents matching last $1 above + func_append_uniq opt_warning_types " $1" + ;; + *all) + opt_warning_types=$warning_categories + ;; + *none) + opt_warning_types=none + warning_func=: + ;; + *error) + opt_warning_types=$warning_categories + warning_func=func_fatal_error + ;; + *) + func_fatal_error \ + "unsupported warning category: '$1'" + ;; + esac + shift + ;; + + --verbose|-v) opt_verbose=: ;; + --version) func_version ;; + -\?|-h) func_usage ;; + --help) func_help ;; + + # Separate optargs to long options (plugins may need this): + --*=*) func_split_equals "$_G_opt" + set dummy "$func_split_equals_lhs" \ + "$func_split_equals_rhs" ${1+"$@"} + shift + ;; + + # Separate optargs to short options: + -W*) + func_split_short_opt "$_G_opt" + set dummy "$func_split_short_opt_name" \ + "$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + # Separate non-argument short options: + -\?*|-h*|-v*|-x*) + func_split_short_opt "$_G_opt" + set dummy "$func_split_short_opt_name" \ + "-$func_split_short_opt_arg" ${1+"$@"} + shift + ;; + + --) _G_rc_parse_options=: ; break ;; + -*) func_fatal_help "unrecognised option: '$_G_opt'" ;; + *) set dummy "$_G_opt" ${1+"$@"}; shift + _G_match_parse_options=false + break + ;; + esac + + $_G_match_parse_options && _G_rc_parse_options=: + done + + + if $_G_rc_parse_options; then + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + func_parse_options_result=$func_quote_for_eval_result + fi + + $_G_rc_parse_options +} + + +# func_validate_options [ARG]... +# ------------------------------ +# Perform any sanity checks on option settings and/or unconsumed +# arguments. +func_hookable func_validate_options +func_validate_options () +{ + $debug_cmd + + _G_rc_validate_options=false + + # Display all warnings if -W was not given. + test -n "$opt_warning_types" || opt_warning_types=" $warning_categories" + + if func_run_hooks func_validate_options ${1+"$@"}; then + # save modified positional parameters for caller + func_validate_options_result=$func_run_hooks_result + _G_rc_validate_options=: + fi + + # Bail if the options were screwed! + $exit_cmd $EXIT_FAILURE + + $_G_rc_validate_options +} + + + +## ----------------- ## +## Helper functions. ## +## ----------------- ## + +# This section contains the helper functions used by the rest of the +# hookable option parser framework in ascii-betical order. + + +# func_fatal_help ARG... +# ---------------------- +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + $debug_cmd + + eval \$ECHO \""Usage: $usage"\" + eval \$ECHO \""$fatal_help"\" + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + + +# func_help +# --------- +# Echo long help message to standard output and exit. func_help () { - $opt_debug + $debug_cmd - $SED -n '/^# Usage:/,/# Report bugs to/ { - :print - s/^# // - s/^# *$// - s*\$progname*'$progname'* - s*\$host*'"$host"'* - s*\$SHELL*'"$SHELL"'* - s*\$LTCC*'"$LTCC"'* - s*\$LTCFLAGS*'"$LTCFLAGS"'* - s*\$LD*'"$LD"'* - s/\$with_gnu_ld/'"$with_gnu_ld"'/ - s/\$automake_version/'"`(automake --version) 2>/dev/null |$SED 1q`"'/ - s/\$autoconf_version/'"`(autoconf --version) 2>/dev/null |$SED 1q`"'/ - p - d - } - /^# .* home page:/b print - /^# General help using/b print - ' < "$progpath" - ret=$? - if test -z "$1"; then - exit $ret - fi + func_usage_message + $ECHO "$long_help_message" + exit 0 } -# func_missing_arg argname + +# func_missing_arg ARGNAME +# ------------------------ # Echo program name prefixed message to standard error and set global # exit_cmd. func_missing_arg () { - $opt_debug + $debug_cmd - func_error "missing argument for $1." + func_error "Missing argument for '$1'." exit_cmd=exit } -# func_split_short_opt shortopt +# func_split_equals STRING +# ------------------------ +# Set func_split_equals_lhs and func_split_equals_rhs shell variables after +# splitting STRING at the '=' sign. +test -z "$_G_HAVE_XSI_OPS" \ + && (eval 'x=a/b/c; + test 5aa/bb/cc = "${#x}${x%%/*}${x%/*}${x#*/}${x##*/}"') 2>/dev/null \ + && _G_HAVE_XSI_OPS=yes + +if test yes = "$_G_HAVE_XSI_OPS" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_split_equals () + { + $debug_cmd + + func_split_equals_lhs=${1%%=*} + func_split_equals_rhs=${1#*=} + test "x$func_split_equals_lhs" = "x$1" \ + && func_split_equals_rhs= + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_split_equals () + { + $debug_cmd + + func_split_equals_lhs=`expr "x$1" : 'x\([^=]*\)'` + func_split_equals_rhs= + test "x$func_split_equals_lhs" = "x$1" \ + || func_split_equals_rhs=`expr "x$1" : 'x[^=]*=\(.*\)$'` + } +fi #func_split_equals + + +# func_split_short_opt SHORTOPT +# ----------------------------- # Set func_split_short_opt_name and func_split_short_opt_arg shell # variables after splitting SHORTOPT after the 2nd character. -func_split_short_opt () +if test yes = "$_G_HAVE_XSI_OPS" +then + # This is an XSI compatible shell, allowing a faster implementation... + eval 'func_split_short_opt () + { + $debug_cmd + + func_split_short_opt_arg=${1#??} + func_split_short_opt_name=${1%"$func_split_short_opt_arg"} + }' +else + # ...otherwise fall back to using expr, which is often a shell builtin. + func_split_short_opt () + { + $debug_cmd + + func_split_short_opt_name=`expr "x$1" : 'x-\(.\)'` + func_split_short_opt_arg=`expr "x$1" : 'x-.\(.*\)$'` + } +fi #func_split_short_opt + + +# func_usage +# ---------- +# Echo short help message to standard output and exit. +func_usage () { - my_sed_short_opt='1s/^\(..\).*$/\1/;q' - my_sed_short_rest='1s/^..\(.*\)$/\1/;q' + $debug_cmd - func_split_short_opt_name=`$ECHO "$1" | $SED "$my_sed_short_opt"` - func_split_short_opt_arg=`$ECHO "$1" | $SED "$my_sed_short_rest"` -} # func_split_short_opt may be replaced by extended shell implementation + func_usage_message + $ECHO "Run '$progname --help |${PAGER-more}' for full usage" + exit 0 +} -# func_split_long_opt longopt -# Set func_split_long_opt_name and func_split_long_opt_arg shell -# variables after splitting LONGOPT at the `=' sign. -func_split_long_opt () +# func_usage_message +# ------------------ +# Echo short help message to standard output. +func_usage_message () { - my_sed_long_opt='1s/^\(--[^=]*\)=.*/\1/;q' - my_sed_long_arg='1s/^--[^=]*=//' + $debug_cmd - func_split_long_opt_name=`$ECHO "$1" | $SED "$my_sed_long_opt"` - func_split_long_opt_arg=`$ECHO "$1" | $SED "$my_sed_long_arg"` -} # func_split_long_opt may be replaced by extended shell implementation - -exit_cmd=: + eval \$ECHO \""Usage: $usage"\" + echo + $SED -n 's|^# || + /^Written by/{ + x;p;x + } + h + /^Written by/q' < "$progpath" + echo + eval \$ECHO \""$usage_message"\" +} - - - -magic="%%%MAGIC variable%%%" -magic_exe="%%%MAGIC EXE variable%%%" - -# Global variables. -nonopt= -preserve_args= -lo2o="s/\\.lo\$/.${objext}/" -o2lo="s/\\.${objext}\$/.lo/" -extracted_archives= -extracted_serial=0 - -# If this variable is set in any of the actions, the command in it -# will be execed at the end. This prevents here-documents from being -# left over by shells. -exec_cmd= - -# func_append var value -# Append VALUE to the end of shell variable VAR. -func_append () +# func_version +# ------------ +# Echo version message to standard output and exit. +func_version () { - eval "${1}=\$${1}\${2}" -} # func_append may be replaced by extended shell implementation + $debug_cmd -# func_append_quoted var value -# Quote VALUE and append to the end of shell variable VAR, separated -# by a space. -func_append_quoted () + printf '%s\n' "$progname $scriptversion" + $SED -n ' + /(C)/!b go + :more + /\./!{ + N + s|\n# | | + b more + } + :go + /^# Written by /,/# warranty; / { + s|^# || + s|^# *$|| + s|\((C)\)[ 0-9,-]*[ ,-]\([1-9][0-9]* \)|\1 \2| + p + } + /^# Written by / { + s|^# || + p + } + /^warranty; /q' < "$progpath" + + exit $? +} + + +# Local variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-pattern: "10/scriptversion=%:y-%02m-%02d.%02H; # UTC" +# time-stamp-time-zone: "UTC" +# End: + +# Set a version string. +scriptversion='(GNU libtool) 2.4.6' + + +# func_echo ARG... +# ---------------- +# Libtool also displays the current mode in messages, so override +# funclib.sh func_echo with this custom definition. +func_echo () { - func_quote_for_eval "${2}" - eval "${1}=\$${1}\\ \$func_quote_for_eval_result" -} # func_append_quoted may be replaced by extended shell implementation + $debug_cmd + + _G_message=$* + + func_echo_IFS=$IFS + IFS=$nl + for _G_line in $_G_message; do + IFS=$func_echo_IFS + $ECHO "$progname${opt_mode+: $opt_mode}: $_G_line" + done + IFS=$func_echo_IFS +} -# func_arith arithmetic-term... -func_arith () +# func_warning ARG... +# ------------------- +# Libtool warnings are not categorized, so override funclib.sh +# func_warning with this simpler definition. +func_warning () { - func_arith_result=`expr "${@}"` -} # func_arith may be replaced by extended shell implementation + $debug_cmd + + $warning_func ${1+"$@"} +} -# func_len string -# STRING may not start with a hyphen. -func_len () +## ---------------- ## +## Options parsing. ## +## ---------------- ## + +# Hook in the functions to make sure our own options are parsed during +# the option parsing loop. + +usage='$progpath [OPTION]... [MODE-ARG]...' + +# Short help message in response to '-h'. +usage_message="Options: + --config show all configuration variables + --debug enable verbose shell tracing + -n, --dry-run display commands without modifying any files + --features display basic configuration information and exit + --mode=MODE use operation mode MODE + --no-warnings equivalent to '-Wnone' + --preserve-dup-deps don't remove duplicate dependency libraries + --quiet, --silent don't print informational messages + --tag=TAG use configuration variables from tag TAG + -v, --verbose print more informational messages than default + --version print version information + -W, --warnings=CATEGORY report the warnings falling in CATEGORY [all] + -h, --help, --help-all print short, long, or detailed help message +" + +# Additional text appended to 'usage_message' in response to '--help'. +func_help () { - func_len_result=`expr "${1}" : ".*" 2>/dev/null || echo $max_cmd_len` -} # func_len may be replaced by extended shell implementation + $debug_cmd + + func_usage_message + $ECHO "$long_help_message + +MODE must be one of the following: + + clean remove files from the build directory + compile compile a source file into a libtool object + execute automatically set library path, then run a program + finish complete the installation of libtool libraries + install install libraries or executables + link create a library or an executable + uninstall remove libraries from an installed directory + +MODE-ARGS vary depending on the MODE. When passed as first option, +'--mode=MODE' may be abbreviated as 'MODE' or a unique abbreviation of that. +Try '$progname --help --mode=MODE' for a more detailed description of MODE. + +When reporting a bug, please describe a test case to reproduce it and +include the following information: + + host-triplet: $host + shell: $SHELL + compiler: $LTCC + compiler flags: $LTCFLAGS + linker: $LD (gnu? $with_gnu_ld) + version: $progname $scriptversion Debian-2.4.6-15 + automake: `($AUTOMAKE --version) 2>/dev/null |$SED 1q` + autoconf: `($AUTOCONF --version) 2>/dev/null |$SED 1q` + +Report bugs to . +GNU libtool home page: . +General help using GNU software: ." + exit 0 +} -# func_lo2o object -func_lo2o () -{ - func_lo2o_result=`$ECHO "${1}" | $SED "$lo2o"` -} # func_lo2o may be replaced by extended shell implementation +# func_lo2o OBJECT-NAME +# --------------------- +# Transform OBJECT-NAME from a '.lo' suffix to the platform specific +# object suffix. + +lo2o=s/\\.lo\$/.$objext/ +o2lo=s/\\.$objext\$/.lo/ + +if test yes = "$_G_HAVE_XSI_OPS"; then + eval 'func_lo2o () + { + case $1 in + *.lo) func_lo2o_result=${1%.lo}.$objext ;; + * ) func_lo2o_result=$1 ;; + esac + }' + + # func_xform LIBOBJ-OR-SOURCE + # --------------------------- + # Transform LIBOBJ-OR-SOURCE from a '.o' or '.c' (or otherwise) + # suffix to a '.lo' libtool-object suffix. + eval 'func_xform () + { + func_xform_result=${1%.*}.lo + }' +else + # ...otherwise fall back to using sed. + func_lo2o () + { + func_lo2o_result=`$ECHO "$1" | $SED "$lo2o"` + } + + func_xform () + { + func_xform_result=`$ECHO "$1" | $SED 's|\.[^.]*$|.lo|'` + } +fi -# func_xform libobj-or-source -func_xform () -{ - func_xform_result=`$ECHO "${1}" | $SED 's/\.[^.]*$/.lo/'` -} # func_xform may be replaced by extended shell implementation - - -# func_fatal_configuration arg... +# func_fatal_configuration ARG... +# ------------------------------- # Echo program name prefixed message to standard error, followed by # a configuration failure hint, and exit. func_fatal_configuration () { - func_error ${1+"$@"} - func_error "See the $PACKAGE documentation for more information." - func_fatal_error "Fatal configuration error." + func__fatal_error ${1+"$@"} \ + "See the $PACKAGE documentation for more information." \ + "Fatal configuration error." } # func_config +# ----------- # Display the configuration for all the tags in this script. func_config () { @@ -919,17 +2222,19 @@ func_config () exit $? } + # func_features +# ------------- # Display the features supported by this script. func_features () { echo "host: $host" - if test "$build_libtool_libs" = yes; then + if test yes = "$build_libtool_libs"; then echo "enable shared libraries" else echo "disable shared libraries" fi - if test "$build_old_libs" = yes; then + if test yes = "$build_old_libs"; then echo "enable static libraries" else echo "disable static libraries" @@ -938,309 +2243,369 @@ func_features () exit $? } -# func_enable_tag tagname + +# func_enable_tag TAGNAME +# ----------------------- # Verify that TAGNAME is valid, and either flag an error and exit, or # enable the TAGNAME tag. We also add TAGNAME to the global $taglist # variable here. func_enable_tag () { - # Global variable: - tagname="$1" + # Global variable: + tagname=$1 - re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" - re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" - sed_extractcf="/$re_begincf/,/$re_endcf/p" + re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" + re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" + sed_extractcf=/$re_begincf/,/$re_endcf/p - # Validate tagname. - case $tagname in - *[!-_A-Za-z0-9,/]*) - func_fatal_error "invalid tag name: $tagname" - ;; - esac + # Validate tagname. + case $tagname in + *[!-_A-Za-z0-9,/]*) + func_fatal_error "invalid tag name: $tagname" + ;; + esac - # Don't test for the "default" C tag, as we know it's - # there but not specially marked. - case $tagname in - CC) ;; + # Don't test for the "default" C tag, as we know it's + # there but not specially marked. + case $tagname in + CC) ;; *) - if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then - taglist="$taglist $tagname" + if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then + taglist="$taglist $tagname" - # Evaluate the configuration. Be careful to quote the path - # and the sed script, to avoid splitting on whitespace, but - # also don't use non-portable quotes within backquotes within - # quotes we have to do it in 2 steps: - extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` - eval "$extractedcf" - else - func_error "ignoring unknown tag $tagname" - fi - ;; - esac + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac } + # func_check_version_match +# ------------------------ # Ensure that we are using m4 macros, and libtool script from the same # release of libtool. func_check_version_match () { - if test "$package_revision" != "$macro_revision"; then - if test "$VERSION" != "$macro_version"; then - if test -z "$macro_version"; then - cat >&2 <<_LT_EOF + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from an older release. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF - else - cat >&2 <<_LT_EOF + else + cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, but the $progname: definition of this LT_INIT comes from $PACKAGE $macro_version. $progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION $progname: and run autoconf again. _LT_EOF - fi - else - cat >&2 <<_LT_EOF + fi + else + cat >&2 <<_LT_EOF $progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, $progname: but the definition of this LT_INIT comes from revision $macro_revision. $progname: You should recreate aclocal.m4 with macros from revision $package_revision $progname: of $PACKAGE $VERSION and run autoconf again. _LT_EOF - fi + fi - exit $EXIT_MISMATCH - fi + exit $EXIT_MISMATCH + fi } -# Shorthand for --mode=foo, only valid as the first argument -case $1 in -clean|clea|cle|cl) - shift; set dummy --mode clean ${1+"$@"}; shift - ;; -compile|compil|compi|comp|com|co|c) - shift; set dummy --mode compile ${1+"$@"}; shift - ;; -execute|execut|execu|exec|exe|ex|e) - shift; set dummy --mode execute ${1+"$@"}; shift - ;; -finish|finis|fini|fin|fi|f) - shift; set dummy --mode finish ${1+"$@"}; shift - ;; -install|instal|insta|inst|ins|in|i) - shift; set dummy --mode install ${1+"$@"}; shift - ;; -link|lin|li|l) - shift; set dummy --mode link ${1+"$@"}; shift - ;; -uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) - shift; set dummy --mode uninstall ${1+"$@"}; shift - ;; -esac - - - -# Option defaults: -opt_debug=: -opt_dry_run=false -opt_config=false -opt_preserve_dup_deps=false -opt_features=false -opt_finish=false -opt_help=false -opt_help_all=false -opt_silent=: -opt_verbose=: -opt_silent=false -opt_verbose=false - - -# Parse options once, thoroughly. This comes as soon as possible in the -# script to make things like `--version' happen as quickly as we can. +# libtool_options_prep [ARG]... +# ----------------------------- +# Preparation for options parsed by libtool. +libtool_options_prep () { - # this just eases exit handling - while test $# -gt 0; do - opt="$1" - shift - case $opt in - --debug|-x) opt_debug='set -x' - func_echo "enabling shell trace mode" - $opt_debug - ;; - --dry-run|--dryrun|-n) - opt_dry_run=: - ;; - --config) - opt_config=: -func_config - ;; - --dlopen|-dlopen) - optarg="$1" - opt_dlopen="${opt_dlopen+$opt_dlopen -}$optarg" - shift - ;; - --preserve-dup-deps) - opt_preserve_dup_deps=: - ;; - --features) - opt_features=: -func_features - ;; - --finish) - opt_finish=: -set dummy --mode finish ${1+"$@"}; shift - ;; - --help) - opt_help=: - ;; - --help-all) - opt_help_all=: -opt_help=': help-all' - ;; - --mode) - test $# = 0 && func_missing_arg $opt && break - optarg="$1" - opt_mode="$optarg" -case $optarg in - # Valid mode arguments: - clean|compile|execute|finish|install|link|relink|uninstall) ;; + $debug_mode - # Catch anything else as an error - *) func_error "invalid argument for $opt" - exit_cmd=exit - break - ;; -esac - shift - ;; - --no-silent|--no-quiet) - opt_silent=false -func_append preserve_args " $opt" - ;; - --no-verbose) - opt_verbose=false -func_append preserve_args " $opt" - ;; - --silent|--quiet) - opt_silent=: -func_append preserve_args " $opt" - opt_verbose=false - ;; - --verbose|-v) - opt_verbose=: -func_append preserve_args " $opt" -opt_silent=false - ;; - --tag) - test $# = 0 && func_missing_arg $opt && break - optarg="$1" - opt_tag="$optarg" -func_append preserve_args " $opt $optarg" -func_enable_tag "$optarg" - shift - ;; + # Option defaults: + opt_config=false + opt_dlopen= + opt_dry_run=false + opt_help=false + opt_mode= + opt_preserve_dup_deps=false + opt_quiet=false - -\?|-h) func_usage ;; - --help) func_help ;; - --version) func_version ;; + nonopt= + preserve_args= - # Separate optargs to long options: - --*=*) - func_split_long_opt "$opt" - set dummy "$func_split_long_opt_name" "$func_split_long_opt_arg" ${1+"$@"} - shift - ;; + _G_rc_lt_options_prep=: - # Separate non-argument short options: - -\?*|-h*|-n*|-v*) - func_split_short_opt "$opt" - set dummy "$func_split_short_opt_name" "-$func_split_short_opt_arg" ${1+"$@"} - shift - ;; - - --) break ;; - -*) func_fatal_help "unrecognized option \`$opt'" ;; - *) set dummy "$opt" ${1+"$@"}; shift; break ;; - esac - done - - # Validate options: - - # save first non-option argument - if test "$#" -gt 0; then - nonopt="$opt" - shift - fi - - # preserve --debug - test "$opt_debug" = : || func_append preserve_args " --debug" - - case $host in - *cygwin* | *mingw* | *pw32* | *cegcc*) - # don't eliminate duplications in $postdeps and $predeps - opt_duplicate_compiler_generated_deps=: + # Shorthand for --mode=foo, only valid as the first argument + case $1 in + clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift + ;; + compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift + ;; + execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift + ;; + finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift + ;; + install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; + link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; + uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift ;; *) - opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps + _G_rc_lt_options_prep=false ;; - esac + esac - $opt_help || { - # Sanity checks first: - func_check_version_match - - if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then - func_fatal_configuration "not configured to build any kind of library" + if $_G_rc_lt_options_prep; then + # Pass back the list of options. + func_quote_for_eval ${1+"$@"} + libtool_options_prep_result=$func_quote_for_eval_result fi - # Darwin sucks - eval std_shrext=\"$shrext_cmds\" - - # Only execute mode is allowed to have -dlopen flags. - if test -n "$opt_dlopen" && test "$opt_mode" != execute; then - func_error "unrecognized option \`-dlopen'" - $ECHO "$help" 1>&2 - exit $EXIT_FAILURE - fi - - # Change the help message to a mode-specific one. - generic_help="$help" - help="Try \`$progname --help --mode=$opt_mode' for more information." - } - - - # Bail if the options were screwed - $exit_cmd $EXIT_FAILURE + $_G_rc_lt_options_prep } +func_add_hook func_options_prep libtool_options_prep +# libtool_parse_options [ARG]... +# --------------------------------- +# Provide handling for libtool specific options. +libtool_parse_options () +{ + $debug_cmd + + _G_rc_lt_parse_options=false + + # Perform our own loop to consume as many options as possible in + # each iteration. + while test $# -gt 0; do + _G_match_lt_parse_options=: + _G_opt=$1 + shift + case $_G_opt in + --dry-run|--dryrun|-n) + opt_dry_run=: + ;; + + --config) func_config ;; + + --dlopen|-dlopen) + opt_dlopen="${opt_dlopen+$opt_dlopen +}$1" + shift + ;; + + --preserve-dup-deps) + opt_preserve_dup_deps=: ;; + + --features) func_features ;; + + --finish) set dummy --mode finish ${1+"$@"}; shift ;; + + --help) opt_help=: ;; + + --help-all) opt_help=': help-all' ;; + + --mode) test $# = 0 && func_missing_arg $_G_opt && break + opt_mode=$1 + case $1 in + # Valid mode arguments: + clean|compile|execute|finish|install|link|relink|uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $_G_opt" + exit_cmd=exit + break + ;; + esac + shift + ;; + + --no-silent|--no-quiet) + opt_quiet=false + func_append preserve_args " $_G_opt" + ;; + + --no-warnings|--no-warning|--no-warn) + opt_warning=false + func_append preserve_args " $_G_opt" + ;; + + --no-verbose) + opt_verbose=false + func_append preserve_args " $_G_opt" + ;; + + --silent|--quiet) + opt_quiet=: + opt_verbose=false + func_append preserve_args " $_G_opt" + ;; + + --tag) test $# = 0 && func_missing_arg $_G_opt && break + opt_tag=$1 + func_append preserve_args " $_G_opt $1" + func_enable_tag "$1" + shift + ;; + + --verbose|-v) opt_quiet=false + opt_verbose=: + func_append preserve_args " $_G_opt" + ;; + + # An option not handled by this hook function: + *) set dummy "$_G_opt" ${1+"$@"} ; shift + _G_match_lt_parse_options=false + break + ;; + esac + $_G_match_lt_parse_options && _G_rc_lt_parse_options=: + done + + if $_G_rc_lt_parse_options; then + # save modified positional parameters for caller + func_quote_for_eval ${1+"$@"} + libtool_parse_options_result=$func_quote_for_eval_result + fi + + $_G_rc_lt_parse_options +} +func_add_hook func_parse_options libtool_parse_options + + + +# libtool_validate_options [ARG]... +# --------------------------------- +# Perform any sanity checks on option settings and/or unconsumed +# arguments. +libtool_validate_options () +{ + # save first non-option argument + if test 0 -lt $#; then + nonopt=$1 + shift + fi + + # preserve --debug + test : = "$debug_cmd" || func_append preserve_args " --debug" + + case $host in + # Solaris2 added to fix http://debbugs.gnu.org/cgi/bugreport.cgi?bug=16452 + # see also: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=59788 + *cygwin* | *mingw* | *pw32* | *cegcc* | *solaris2* | *os2*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_preserve_dup_deps + ;; + esac + + $opt_help || { + # Sanity checks first: + func_check_version_match + + test yes != "$build_libtool_libs" \ + && test yes != "$build_old_libs" \ + && func_fatal_configuration "not configured to build any kind of library" + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$opt_dlopen" && test execute != "$opt_mode"; then + func_error "unrecognized option '-dlopen'" + $ECHO "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help=$help + help="Try '$progname --help --mode=$opt_mode' for more information." + } + + # Pass back the unparsed argument list + func_quote_for_eval ${1+"$@"} + libtool_validate_options_result=$func_quote_for_eval_result +} +func_add_hook func_validate_options libtool_validate_options + + +# Process options as early as possible so that --help and --version +# can return quickly. +func_options ${1+"$@"} +eval set dummy "$func_options_result"; shift + ## ----------- ## ## Main. ## ## ----------- ## +magic='%%%MAGIC variable%%%' +magic_exe='%%%MAGIC EXE variable%%%' + +# Global variables. +extracted_archives= +extracted_serial=0 + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + + +# A function that is used when there is no print builtin or printf. +func_fallback_echo () +{ + eval 'cat <<_LTECHO_EOF +$1 +_LTECHO_EOF' +} + +# func_generated_by_libtool +# True iff stdin has been generated by Libtool. This function is only +# a basic sanity check; it will hardly flush out determined imposters. +func_generated_by_libtool_p () +{ + $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} + # func_lalib_p file -# True iff FILE is a libtool `.la' library or `.lo' object file. +# True iff FILE is a libtool '.la' library or '.lo' object file. # This function is only a basic sanity check; it will hardly flush out # determined imposters. func_lalib_p () { test -f "$1" && - $SED -e 4q "$1" 2>/dev/null \ - | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 + $SED -e 4q "$1" 2>/dev/null | func_generated_by_libtool_p } # func_lalib_unsafe_p file -# True iff FILE is a libtool `.la' library or `.lo' object file. +# True iff FILE is a libtool '.la' library or '.lo' object file. # This function implements the same check as func_lalib_p without # resorting to external programs. To this end, it redirects stdin and # closes it afterwards, without saving the original file descriptor. # As a safety measure, use it only where a negative result would be -# fatal anyway. Works if `file' does not exist. +# fatal anyway. Works if 'file' does not exist. func_lalib_unsafe_p () { lalib_p=no @@ -1248,13 +2613,13 @@ func_lalib_unsafe_p () for lalib_p_l in 1 2 3 4 do read lalib_p_line - case "$lalib_p_line" in + case $lalib_p_line in \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; esac done exec 0<&5 5<&- fi - test "$lalib_p" = yes + test yes = "$lalib_p" } # func_ltwrapper_script_p file @@ -1263,7 +2628,8 @@ func_lalib_unsafe_p () # determined imposters. func_ltwrapper_script_p () { - func_lalib_p "$1" + test -f "$1" && + $lt_truncate_bin < "$1" 2>/dev/null | func_generated_by_libtool_p } # func_ltwrapper_executable_p file @@ -1288,7 +2654,7 @@ func_ltwrapper_scriptname () { func_dirname_and_basename "$1" "" "." func_stripname '' '.exe' "$func_basename_result" - func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" + func_ltwrapper_scriptname_result=$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper } # func_ltwrapper_p file @@ -1307,11 +2673,13 @@ func_ltwrapper_p () # FAIL_CMD may read-access the current command in variable CMD! func_execute_cmds () { - $opt_debug + $debug_cmd + save_ifs=$IFS; IFS='~' for cmd in $1; do - IFS=$save_ifs + IFS=$sp$nl eval cmd=\"$cmd\" + IFS=$save_ifs func_show_eval "$cmd" "${2-:}" done IFS=$save_ifs @@ -1323,10 +2691,11 @@ func_execute_cmds () # Note that it is not necessary on cygwin/mingw to append a dot to # FILE even if both FILE and FILE.exe exist: automatic-append-.exe # behavior happens only for exec(3), not for open(2)! Also, sourcing -# `FILE.' does not work on cygwin managed mounts. +# 'FILE.' does not work on cygwin managed mounts. func_source () { - $opt_debug + $debug_cmd + case $1 in */* | *\\*) . "$1" ;; *) . "./$1" ;; @@ -1353,10 +2722,10 @@ func_resolve_sysroot () # store the result into func_replace_sysroot_result. func_replace_sysroot () { - case "$lt_sysroot:$1" in + case $lt_sysroot:$1 in ?*:"$lt_sysroot"*) func_stripname "$lt_sysroot" '' "$1" - func_replace_sysroot_result="=$func_stripname_result" + func_replace_sysroot_result='='$func_stripname_result ;; *) # Including no sysroot. @@ -1373,7 +2742,8 @@ func_replace_sysroot () # arg is usually of the form 'gcc ...' func_infer_tag () { - $opt_debug + $debug_cmd + if test -n "$available_tags" && test -z "$tagname"; then CC_quoted= for arg in $CC; do @@ -1392,7 +2762,7 @@ func_infer_tag () for z in $available_tags; do if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then # Evaluate the configuration. - eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + eval "`$SED -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" CC_quoted= for arg in $CC; do # Double-quote args containing other shell metacharacters. @@ -1417,7 +2787,7 @@ func_infer_tag () # line option must be used. if test -z "$tagname"; then func_echo "unable to infer tagged configuration" - func_fatal_error "specify a tag with \`--tag'" + func_fatal_error "specify a tag with '--tag'" # else # func_verbose "using $tagname tagged configuration" fi @@ -1433,15 +2803,15 @@ func_infer_tag () # but don't create it if we're doing a dry run. func_write_libtool_object () { - write_libobj=${1} - if test "$build_libtool_libs" = yes; then - write_lobj=\'${2}\' + write_libobj=$1 + if test yes = "$build_libtool_libs"; then + write_lobj=\'$2\' else write_lobj=none fi - if test "$build_old_libs" = yes; then - write_oldobj=\'${3}\' + if test yes = "$build_old_libs"; then + write_oldobj=\'$3\' else write_oldobj=none fi @@ -1449,7 +2819,7 @@ func_write_libtool_object () $opt_dry_run || { cat >${write_libobj}T </dev/null` - if test "$?" -eq 0 && test -n "${func_convert_core_file_wine_to_w32_tmp}"; then + if test "$?" -eq 0 && test -n "$func_convert_core_file_wine_to_w32_tmp"; then func_convert_core_file_wine_to_w32_result=`$ECHO "$func_convert_core_file_wine_to_w32_tmp" | - $SED -e "$lt_sed_naive_backslashify"` + $SED -e "$sed_naive_backslashify"` else func_convert_core_file_wine_to_w32_result= fi @@ -1513,18 +2884,19 @@ func_convert_core_file_wine_to_w32 () # are convertible, then the result may be empty. func_convert_core_path_wine_to_w32 () { - $opt_debug + $debug_cmd + # unfortunately, winepath doesn't convert paths, only file names - func_convert_core_path_wine_to_w32_result="" + func_convert_core_path_wine_to_w32_result= if test -n "$1"; then oldIFS=$IFS IFS=: for func_convert_core_path_wine_to_w32_f in $1; do IFS=$oldIFS func_convert_core_file_wine_to_w32 "$func_convert_core_path_wine_to_w32_f" - if test -n "$func_convert_core_file_wine_to_w32_result" ; then + if test -n "$func_convert_core_file_wine_to_w32_result"; then if test -z "$func_convert_core_path_wine_to_w32_result"; then - func_convert_core_path_wine_to_w32_result="$func_convert_core_file_wine_to_w32_result" + func_convert_core_path_wine_to_w32_result=$func_convert_core_file_wine_to_w32_result else func_append func_convert_core_path_wine_to_w32_result ";$func_convert_core_file_wine_to_w32_result" fi @@ -1553,7 +2925,8 @@ func_convert_core_path_wine_to_w32 () # environment variable; do not put it in $PATH. func_cygpath () { - $opt_debug + $debug_cmd + if test -n "$LT_CYGPATH" && test -f "$LT_CYGPATH"; then func_cygpath_result=`$LT_CYGPATH "$@" 2>/dev/null` if test "$?" -ne 0; then @@ -1562,7 +2935,7 @@ func_cygpath () fi else func_cygpath_result= - func_error "LT_CYGPATH is empty or specifies non-existent file: \`$LT_CYGPATH'" + func_error "LT_CYGPATH is empty or specifies non-existent file: '$LT_CYGPATH'" fi } #end: func_cygpath @@ -1573,10 +2946,11 @@ func_cygpath () # result in func_convert_core_msys_to_w32_result. func_convert_core_msys_to_w32 () { - $opt_debug + $debug_cmd + # awkward: cmd appends spaces to result func_convert_core_msys_to_w32_result=`( cmd //c echo "$1" ) 2>/dev/null | - $SED -e 's/[ ]*$//' -e "$lt_sed_naive_backslashify"` + $SED -e 's/[ ]*$//' -e "$sed_naive_backslashify"` } #end: func_convert_core_msys_to_w32 @@ -1587,13 +2961,14 @@ func_convert_core_msys_to_w32 () # func_to_host_file_result to ARG1). func_convert_file_check () { - $opt_debug - if test -z "$2" && test -n "$1" ; then + $debug_cmd + + if test -z "$2" && test -n "$1"; then func_error "Could not determine host file name corresponding to" - func_error " \`$1'" + func_error " '$1'" func_error "Continuing, but uninstalled executables may not work." # Fallback: - func_to_host_file_result="$1" + func_to_host_file_result=$1 fi } # end func_convert_file_check @@ -1605,10 +2980,11 @@ func_convert_file_check () # func_to_host_file_result to a simplistic fallback value (see below). func_convert_path_check () { - $opt_debug + $debug_cmd + if test -z "$4" && test -n "$3"; then func_error "Could not determine the host path corresponding to" - func_error " \`$3'" + func_error " '$3'" func_error "Continuing, but uninstalled executables may not work." # Fallback. This is a deliberately simplistic "conversion" and # should not be "improved". See libtool.info. @@ -1617,7 +2993,7 @@ func_convert_path_check () func_to_host_path_result=`echo "$3" | $SED -e "$lt_replace_pathsep_chars"` else - func_to_host_path_result="$3" + func_to_host_path_result=$3 fi fi } @@ -1629,9 +3005,10 @@ func_convert_path_check () # and appending REPL if ORIG matches BACKPAT. func_convert_path_front_back_pathsep () { - $opt_debug + $debug_cmd + case $4 in - $1 ) func_to_host_path_result="$3$func_to_host_path_result" + $1 ) func_to_host_path_result=$3$func_to_host_path_result ;; esac case $4 in @@ -1645,7 +3022,7 @@ func_convert_path_front_back_pathsep () ################################################## # $build to $host FILE NAME CONVERSION FUNCTIONS # ################################################## -# invoked via `$to_host_file_cmd ARG' +# invoked via '$to_host_file_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # Result will be available in $func_to_host_file_result. @@ -1656,7 +3033,8 @@ func_convert_path_front_back_pathsep () # in func_to_host_file_result. func_to_host_file () { - $opt_debug + $debug_cmd + $to_host_file_cmd "$1" } # end func_to_host_file @@ -1668,7 +3046,8 @@ func_to_host_file () # in (the comma separated) LAZY, no conversion takes place. func_to_tool_file () { - $opt_debug + $debug_cmd + case ,$2, in *,"$to_tool_file_cmd",*) func_to_tool_file_result=$1 @@ -1686,7 +3065,7 @@ func_to_tool_file () # Copy ARG to func_to_host_file_result. func_convert_file_noop () { - func_to_host_file_result="$1" + func_to_host_file_result=$1 } # end func_convert_file_noop @@ -1697,11 +3076,12 @@ func_convert_file_noop () # func_to_host_file_result. func_convert_file_msys_to_w32 () { - $opt_debug - func_to_host_file_result="$1" + $debug_cmd + + func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" - func_to_host_file_result="$func_convert_core_msys_to_w32_result" + func_to_host_file_result=$func_convert_core_msys_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } @@ -1713,8 +3093,9 @@ func_convert_file_msys_to_w32 () # func_to_host_file_result. func_convert_file_cygwin_to_w32 () { - $opt_debug - func_to_host_file_result="$1" + $debug_cmd + + func_to_host_file_result=$1 if test -n "$1"; then # because $build is cygwin, we call "the" cygpath in $PATH; no need to use # LT_CYGPATH in this case. @@ -1730,11 +3111,12 @@ func_convert_file_cygwin_to_w32 () # and a working winepath. Returns result in func_to_host_file_result. func_convert_file_nix_to_w32 () { - $opt_debug - func_to_host_file_result="$1" + $debug_cmd + + func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_file_wine_to_w32 "$1" - func_to_host_file_result="$func_convert_core_file_wine_to_w32_result" + func_to_host_file_result=$func_convert_core_file_wine_to_w32_result fi func_convert_file_check "$1" "$func_to_host_file_result" } @@ -1746,12 +3128,13 @@ func_convert_file_nix_to_w32 () # Returns result in func_to_host_file_result. func_convert_file_msys_to_cygwin () { - $opt_debug - func_to_host_file_result="$1" + $debug_cmd + + func_to_host_file_result=$1 if test -n "$1"; then func_convert_core_msys_to_w32 "$1" func_cygpath -u "$func_convert_core_msys_to_w32_result" - func_to_host_file_result="$func_cygpath_result" + func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } @@ -1764,13 +3147,14 @@ func_convert_file_msys_to_cygwin () # in func_to_host_file_result. func_convert_file_nix_to_cygwin () { - $opt_debug - func_to_host_file_result="$1" + $debug_cmd + + func_to_host_file_result=$1 if test -n "$1"; then # convert from *nix to w32, then use cygpath to convert from w32 to cygwin. func_convert_core_file_wine_to_w32 "$1" func_cygpath -u "$func_convert_core_file_wine_to_w32_result" - func_to_host_file_result="$func_cygpath_result" + func_to_host_file_result=$func_cygpath_result fi func_convert_file_check "$1" "$func_to_host_file_result" } @@ -1780,7 +3164,7 @@ func_convert_file_nix_to_cygwin () ############################################# # $build to $host PATH CONVERSION FUNCTIONS # ############################################# -# invoked via `$to_host_path_cmd ARG' +# invoked via '$to_host_path_cmd ARG' # # In each case, ARG is the path to be converted from $build to $host format. # The result will be available in $func_to_host_path_result. @@ -1804,10 +3188,11 @@ func_convert_file_nix_to_cygwin () to_host_path_cmd= func_init_to_host_path_cmd () { - $opt_debug + $debug_cmd + if test -z "$to_host_path_cmd"; then func_stripname 'func_convert_file_' '' "$to_host_file_cmd" - to_host_path_cmd="func_convert_path_${func_stripname_result}" + to_host_path_cmd=func_convert_path_$func_stripname_result fi } @@ -1817,7 +3202,8 @@ func_init_to_host_path_cmd () # in func_to_host_path_result. func_to_host_path () { - $opt_debug + $debug_cmd + func_init_to_host_path_cmd $to_host_path_cmd "$1" } @@ -1828,7 +3214,7 @@ func_to_host_path () # Copy ARG to func_to_host_path_result. func_convert_path_noop () { - func_to_host_path_result="$1" + func_to_host_path_result=$1 } # end func_convert_path_noop @@ -1839,8 +3225,9 @@ func_convert_path_noop () # func_to_host_path_result. func_convert_path_msys_to_w32 () { - $opt_debug - func_to_host_path_result="$1" + $debug_cmd + + func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from ARG. MSYS # behavior is inconsistent here; cygpath turns them into '.;' and ';.'; @@ -1848,7 +3235,7 @@ func_convert_path_msys_to_w32 () func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" - func_to_host_path_result="$func_convert_core_msys_to_w32_result" + func_to_host_path_result=$func_convert_core_msys_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" @@ -1862,8 +3249,9 @@ func_convert_path_msys_to_w32 () # func_to_host_file_result. func_convert_path_cygwin_to_w32 () { - $opt_debug - func_to_host_path_result="$1" + $debug_cmd + + func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" @@ -1882,14 +3270,15 @@ func_convert_path_cygwin_to_w32 () # a working winepath. Returns result in func_to_host_file_result. func_convert_path_nix_to_w32 () { - $opt_debug - func_to_host_path_result="$1" + $debug_cmd + + func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" - func_to_host_path_result="$func_convert_core_path_wine_to_w32_result" + func_to_host_path_result=$func_convert_core_path_wine_to_w32_result func_convert_path_check : ";" \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" ";" "$1" @@ -1903,15 +3292,16 @@ func_convert_path_nix_to_w32 () # Returns result in func_to_host_file_result. func_convert_path_msys_to_cygwin () { - $opt_debug - func_to_host_path_result="$1" + $debug_cmd + + func_to_host_path_result=$1 if test -n "$1"; then # See func_convert_path_msys_to_w32: func_stripname : : "$1" func_to_host_path_tmp1=$func_stripname_result func_convert_core_msys_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_msys_to_w32_result" - func_to_host_path_result="$func_cygpath_result" + func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" @@ -1926,8 +3316,9 @@ func_convert_path_msys_to_cygwin () # func_to_host_file_result. func_convert_path_nix_to_cygwin () { - $opt_debug - func_to_host_path_result="$1" + $debug_cmd + + func_to_host_path_result=$1 if test -n "$1"; then # Remove leading and trailing path separator characters from # ARG. msys behavior is inconsistent here, cygpath turns them @@ -1936,7 +3327,7 @@ func_convert_path_nix_to_cygwin () func_to_host_path_tmp1=$func_stripname_result func_convert_core_path_wine_to_w32 "$func_to_host_path_tmp1" func_cygpath -u -p "$func_convert_core_path_wine_to_w32_result" - func_to_host_path_result="$func_cygpath_result" + func_to_host_path_result=$func_cygpath_result func_convert_path_check : : \ "$func_to_host_path_tmp1" "$func_to_host_path_result" func_convert_path_front_back_pathsep ":*" "*:" : "$1" @@ -1945,13 +3336,31 @@ func_convert_path_nix_to_cygwin () # end func_convert_path_nix_to_cygwin +# func_dll_def_p FILE +# True iff FILE is a Windows DLL '.def' file. +# Keep in sync with _LT_DLL_DEF_P in libtool.m4 +func_dll_def_p () +{ + $debug_cmd + + func_dll_def_p_tmp=`$SED -n \ + -e 's/^[ ]*//' \ + -e '/^\(;.*\)*$/d' \ + -e 's/^\(EXPORTS\|LIBRARY\)\([ ].*\)*$/DEF/p' \ + -e q \ + "$1"` + test DEF = "$func_dll_def_p_tmp" +} + + # func_mode_compile arg... func_mode_compile () { - $opt_debug + $debug_cmd + # Get the compilation command and the source file. base_compile= - srcfile="$nonopt" # always keep a non-empty value in "srcfile" + srcfile=$nonopt # always keep a non-empty value in "srcfile" suppress_opt=yes suppress_output= arg_mode=normal @@ -1964,12 +3373,12 @@ func_mode_compile () case $arg_mode in arg ) # do not "continue". Instead, add this to base_compile - lastarg="$arg" + lastarg=$arg arg_mode=normal ;; target ) - libobj="$arg" + libobj=$arg arg_mode=normal continue ;; @@ -1979,7 +3388,7 @@ func_mode_compile () case $arg in -o) test -n "$libobj" && \ - func_fatal_error "you cannot specify \`-o' more than once" + func_fatal_error "you cannot specify '-o' more than once" arg_mode=target continue ;; @@ -2008,12 +3417,12 @@ func_mode_compile () func_stripname '-Wc,' '' "$arg" args=$func_stripname_result lastarg= - save_ifs="$IFS"; IFS=',' + save_ifs=$IFS; IFS=, for arg in $args; do - IFS="$save_ifs" + IFS=$save_ifs func_append_quoted lastarg "$arg" done - IFS="$save_ifs" + IFS=$save_ifs func_stripname ' ' '' "$lastarg" lastarg=$func_stripname_result @@ -2026,8 +3435,8 @@ func_mode_compile () # Accept the current argument as the source file. # The previous "srcfile" becomes the current argument. # - lastarg="$srcfile" - srcfile="$arg" + lastarg=$srcfile + srcfile=$arg ;; esac # case $arg ;; @@ -2042,13 +3451,13 @@ func_mode_compile () func_fatal_error "you must specify an argument for -Xcompile" ;; target) - func_fatal_error "you must specify a target with \`-o'" + func_fatal_error "you must specify a target with '-o'" ;; *) # Get the name of the library object. test -z "$libobj" && { func_basename "$srcfile" - libobj="$func_basename_result" + libobj=$func_basename_result } ;; esac @@ -2059,7 +3468,7 @@ func_mode_compile () *.[cCFSifmso] | \ *.ada | *.adb | *.ads | *.asm | \ *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ - *.[fF][09]? | *.for | *.java | *.obj | *.sx | *.cu | *.cup) + *.[fF][09]? | *.for | *.java | *.go | *.obj | *.sx | *.cu | *.cup) func_xform "$libobj" libobj=$func_xform_result ;; @@ -2068,7 +3477,7 @@ func_mode_compile () case $libobj in *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) - func_fatal_error "cannot determine name of library object from \`$libobj'" + func_fatal_error "cannot determine name of library object from '$libobj'" ;; esac @@ -2077,8 +3486,8 @@ func_mode_compile () for arg in $later; do case $arg in -shared) - test "$build_libtool_libs" != yes && \ - func_fatal_configuration "can not build a shared library" + test yes = "$build_libtool_libs" \ + || func_fatal_configuration "cannot build a shared library" build_old_libs=no continue ;; @@ -2104,17 +3513,17 @@ func_mode_compile () func_quote_for_eval "$libobj" test "X$libobj" != "X$func_quote_for_eval_result" \ && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ - && func_warning "libobj name \`$libobj' may not contain shell special characters." + && func_warning "libobj name '$libobj' may not contain shell special characters." func_dirname_and_basename "$obj" "/" "" - objname="$func_basename_result" - xdir="$func_dirname_result" - lobj=${xdir}$objdir/$objname + objname=$func_basename_result + xdir=$func_dirname_result + lobj=$xdir$objdir/$objname test -z "$base_compile" && \ func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. - if test "$build_old_libs" = yes; then + if test yes = "$build_old_libs"; then removelist="$obj $lobj $libobj ${libobj}T" else removelist="$lobj $libobj ${libobj}T" @@ -2126,16 +3535,16 @@ func_mode_compile () pic_mode=default ;; esac - if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + if test no = "$pic_mode" && test pass_all != "$deplibs_check_method"; then # non-PIC code in shared libraries is not supported pic_mode=default fi # Calculate the filename of the output object if compiler does # not support -o with -c - if test "$compiler_c_o" = no; then - output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.${objext} - lockfile="$output_obj.lock" + if test no = "$compiler_c_o"; then + output_obj=`$ECHO "$srcfile" | $SED 's%^.*/%%; s%\.[^.]*$%%'`.$objext + lockfile=$output_obj.lock else output_obj= need_locks=no @@ -2144,12 +3553,12 @@ func_mode_compile () # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file - if test "$need_locks" = yes; then + if test yes = "$need_locks"; then until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" sleep 2 done - elif test "$need_locks" = warn; then + elif test warn = "$need_locks"; then if test -f "$lockfile"; then $ECHO "\ *** ERROR, $lockfile exists and contains: @@ -2157,7 +3566,7 @@ func_mode_compile () This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you +your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." @@ -2179,11 +3588,11 @@ compiler." qsrcfile=$func_quote_for_eval_result # Only build a PIC object if we are building libtool libraries. - if test "$build_libtool_libs" = yes; then + if test yes = "$build_libtool_libs"; then # Without this assignment, base_compile gets emptied. fbsd_hideous_sh_bug=$base_compile - if test "$pic_mode" != no; then + if test no != "$pic_mode"; then command="$base_compile $qsrcfile $pic_flag" else # Don't build PIC code @@ -2200,7 +3609,7 @@ compiler." func_show_eval_locale "$command" \ 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' - if test "$need_locks" = warn && + if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: @@ -2211,7 +3620,7 @@ $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you +your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." @@ -2227,20 +3636,20 @@ compiler." fi # Allow error messages only from the first compilation. - if test "$suppress_opt" = yes; then + if test yes = "$suppress_opt"; then suppress_output=' >/dev/null 2>&1' fi fi # Only build a position-dependent object if we build old libraries. - if test "$build_old_libs" = yes; then - if test "$pic_mode" != yes; then + if test yes = "$build_old_libs"; then + if test yes != "$pic_mode"; then # Don't build PIC code command="$base_compile $qsrcfile$pie_flag" else command="$base_compile $qsrcfile $pic_flag" fi - if test "$compiler_c_o" = yes; then + if test yes = "$compiler_c_o"; then func_append command " -o $obj" fi @@ -2249,7 +3658,7 @@ compiler." func_show_eval_locale "$command" \ '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' - if test "$need_locks" = warn && + if test warn = "$need_locks" && test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then $ECHO "\ *** ERROR, $lockfile contains: @@ -2260,7 +3669,7 @@ $srcfile This indicates that another process is trying to use the same temporary object file, and libtool could not work around it because -your compiler does not support \`-c' and \`-o' together. If you +your compiler does not support '-c' and '-o' together. If you repeat this compilation, it may succeed, by chance, but you had better avoid parallel builds (make -j) in this platform, or get a better compiler." @@ -2280,7 +3689,7 @@ compiler." func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" # Unlock the critical section if it was locked - if test "$need_locks" != no; then + if test no != "$need_locks"; then removelist=$lockfile $RM "$lockfile" fi @@ -2290,7 +3699,7 @@ compiler." } $opt_help || { - test "$opt_mode" = compile && func_mode_compile ${1+"$@"} + test compile = "$opt_mode" && func_mode_compile ${1+"$@"} } func_mode_help () @@ -2310,7 +3719,7 @@ func_mode_help () Remove files from the build directory. RM is the name of the program to use to delete files associated with each FILE -(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, object or program, all the files associated @@ -2329,16 +3738,16 @@ This mode accepts the following additional options: -no-suppress do not suppress compiler output for multiple passes -prefer-pic try to build PIC objects only -prefer-non-pic try to build non-PIC objects only - -shared do not build a \`.o' file suitable for static linking - -static only build a \`.o' file suitable for static linking + -shared do not build a '.o' file suitable for static linking + -static only build a '.o' file suitable for static linking -Wc,FLAG pass FLAG directly to the compiler -COMPILE-COMMAND is a command to be used in creating a \`standard' object file +COMPILE-COMMAND is a command to be used in creating a 'standard' object file from the given SOURCEFILE. The output file name is determined by removing the directory component from -SOURCEFILE, then substituting the C source code suffix \`.c' with the -library object suffix, \`.lo'." +SOURCEFILE, then substituting the C source code suffix '.c' with the +library object suffix, '.lo'." ;; execute) @@ -2351,7 +3760,7 @@ This mode accepts the following additional options: -dlopen FILE add the directory containing FILE to the library path -This mode sets the library path environment variable according to \`-dlopen' +This mode sets the library path environment variable according to '-dlopen' flags. If any of the ARGS are libtool executable wrappers, then they are translated @@ -2370,7 +3779,7 @@ Complete the installation of libtool libraries. Each LIBDIR is a directory that contains libtool libraries. The commands that this mode executes may require superuser privileges. Use -the \`--dry-run' option if you just want to see what would be executed." +the '--dry-run' option if you just want to see what would be executed." ;; install) @@ -2380,7 +3789,7 @@ the \`--dry-run' option if you just want to see what would be executed." Install executables or libraries. INSTALL-COMMAND is the installation command. The first component should be -either the \`install' or \`cp' program. +either the 'install' or 'cp' program. The following components of INSTALL-COMMAND are treated specially: @@ -2406,7 +3815,7 @@ The following components of LINK-COMMAND are treated specially: -avoid-version do not add a version suffix if possible -bindir BINDIR specify path to binaries directory (for systems where libraries must be found in the PATH setting at runtime) - -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlopen FILE '-dlpreopen' FILE if it cannot be dlopened at runtime -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) -export-symbols SYMFILE @@ -2420,7 +3829,8 @@ The following components of LINK-COMMAND are treated specially: -no-install link a not-installable executable -no-undefined declare that a library does not refer to external symbols -o OUTPUT-FILE create OUTPUT-FILE from the specified objects - -objectlist FILE Use a list of object files found in FILE to specify objects + -objectlist FILE use a list of object files found in FILE to specify objects + -os2dllname NAME force a short DLL name on OS/2 (no effect on other OSes) -precious-files-regex REGEX don't remove output files matching REGEX -release RELEASE specify package release information @@ -2440,20 +3850,20 @@ The following components of LINK-COMMAND are treated specially: -Xlinker FLAG pass linker-specific FLAG directly to the linker -XCClinker FLAG pass link-specific FLAG to the compiler driver (CC) -All other options (arguments beginning with \`-') are ignored. +All other options (arguments beginning with '-') are ignored. -Every other argument is treated as a filename. Files ending in \`.la' are +Every other argument is treated as a filename. Files ending in '.la' are treated as uninstalled libtool libraries, other files are standard or library object files. -If the OUTPUT-FILE ends in \`.la', then a libtool library is created, -only library objects (\`.lo' files) may be specified, and \`-rpath' is +If the OUTPUT-FILE ends in '.la', then a libtool library is created, +only library objects ('.lo' files) may be specified, and '-rpath' is required, except when creating a convenience library. -If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created -using \`ar' and \`ranlib', or on Windows using \`lib'. +If OUTPUT-FILE ends in '.a' or '.lib', then a standard library is created +using 'ar' and 'ranlib', or on Windows using 'lib'. -If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +If OUTPUT-FILE ends in '.lo' or '.$objext', then a reloadable object file is created, otherwise an executable program is created." ;; @@ -2464,7 +3874,7 @@ is created, otherwise an executable program is created." Remove libraries from an installation directory. RM is the name of the program to use to delete files associated with each FILE -(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +(typically '/bin/rm'). RM-OPTIONS are options (such as '-f') to be passed to RM. If FILE is a libtool library, all the files associated with it are deleted. @@ -2472,17 +3882,17 @@ Otherwise, only FILE itself is deleted using RM." ;; *) - func_fatal_help "invalid operation mode \`$opt_mode'" + func_fatal_help "invalid operation mode '$opt_mode'" ;; esac echo - $ECHO "Try \`$progname --help' for more information about other modes." + $ECHO "Try '$progname --help' for more information about other modes." } # Now that we've collected a possible --mode arg, show help if necessary if $opt_help; then - if test "$opt_help" = :; then + if test : = "$opt_help"; then func_mode_help else { @@ -2490,7 +3900,7 @@ if $opt_help; then for opt_mode in compile link execute install finish uninstall clean; do func_mode_help done - } | sed -n '1p; 2,$s/^Usage:/ or: /p' + } | $SED -n '1p; 2,$s/^Usage:/ or: /p' { func_help noexit for opt_mode in compile link execute install finish uninstall clean; do @@ -2498,7 +3908,7 @@ if $opt_help; then func_mode_help done } | - sed '1d + $SED '1d /^When reporting/,/^Report/{ H d @@ -2515,16 +3925,17 @@ fi # func_mode_execute arg... func_mode_execute () { - $opt_debug + $debug_cmd + # The first argument is the command name. - cmd="$nonopt" + cmd=$nonopt test -z "$cmd" && \ func_fatal_help "you must specify a COMMAND" # Handle -dlopen flags immediately. for file in $opt_dlopen; do test -f "$file" \ - || func_fatal_help "\`$file' is not a file" + || func_fatal_help "'$file' is not a file" dir= case $file in @@ -2534,7 +3945,7 @@ func_mode_execute () # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ - || func_fatal_help "\`$lib' is not a valid libtool archive" + || func_fatal_help "'$lib' is not a valid libtool archive" # Read the libtool library. dlname= @@ -2545,18 +3956,18 @@ func_mode_execute () if test -z "$dlname"; then # Warn if it was a shared library. test -n "$library_names" && \ - func_warning "\`$file' was not linked with \`-export-dynamic'" + func_warning "'$file' was not linked with '-export-dynamic'" continue fi func_dirname "$file" "" "." - dir="$func_dirname_result" + dir=$func_dirname_result if test -f "$dir/$objdir/$dlname"; then func_append dir "/$objdir" else if test ! -f "$dir/$dlname"; then - func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" + func_fatal_error "cannot find '$dlname' in '$dir' or '$dir/$objdir'" fi fi ;; @@ -2564,18 +3975,18 @@ func_mode_execute () *.lo) # Just add the directory containing the .lo file. func_dirname "$file" "" "." - dir="$func_dirname_result" + dir=$func_dirname_result ;; *) - func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" + func_warning "'-dlopen' is ignored for non-libtool libraries and objects" continue ;; esac # Get the absolute pathname. absdir=`cd "$dir" && pwd` - test -n "$absdir" && dir="$absdir" + test -n "$absdir" && dir=$absdir # Now add the directory to shlibpath_var. if eval "test -z \"\$$shlibpath_var\""; then @@ -2587,7 +3998,7 @@ func_mode_execute () # This variable tells wrapper scripts just to set shlibpath_var # rather than running their programs. - libtool_execute_magic="$magic" + libtool_execute_magic=$magic # Check if any of the arguments is a wrapper script. args= @@ -2600,12 +4011,12 @@ func_mode_execute () if func_ltwrapper_script_p "$file"; then func_source "$file" # Transform arg to wrapped name. - file="$progdir/$program" + file=$progdir/$program elif func_ltwrapper_executable_p "$file"; then func_ltwrapper_scriptname "$file" func_source "$func_ltwrapper_scriptname_result" # Transform arg to wrapped name. - file="$progdir/$program" + file=$progdir/$program fi ;; esac @@ -2613,7 +4024,15 @@ func_mode_execute () func_append_quoted args "$file" done - if test "X$opt_dry_run" = Xfalse; then + if $opt_dry_run; then + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" + echo "export $shlibpath_var" + fi + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + else if test -n "$shlibpath_var"; then # Export the shlibpath_var. eval "export $shlibpath_var" @@ -2630,25 +4049,18 @@ func_mode_execute () done # Now prepare to actually exec the command. - exec_cmd="\$cmd$args" - else - # Display what would be done. - if test -n "$shlibpath_var"; then - eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" - echo "export $shlibpath_var" - fi - $ECHO "$cmd$args" - exit $EXIT_SUCCESS + exec_cmd=\$cmd$args fi } -test "$opt_mode" = execute && func_mode_execute ${1+"$@"} +test execute = "$opt_mode" && func_mode_execute ${1+"$@"} # func_mode_finish arg... func_mode_finish () { - $opt_debug + $debug_cmd + libs= libdirs= admincmds= @@ -2662,11 +4074,11 @@ func_mode_finish () if func_lalib_unsafe_p "$opt"; then func_append libs " $opt" else - func_warning "\`$opt' is not a valid libtool archive" + func_warning "'$opt' is not a valid libtool archive" fi else - func_fatal_error "invalid argument \`$opt'" + func_fatal_error "invalid argument '$opt'" fi done @@ -2681,12 +4093,12 @@ func_mode_finish () # Remove sysroot references if $opt_dry_run; then for lib in $libs; do - echo "removing references to $lt_sysroot and \`=' prefixes from $lib" + echo "removing references to $lt_sysroot and '=' prefixes from $lib" done else tmpdir=`func_mktempdir` for lib in $libs; do - sed -e "${sysroot_cmd} s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ + $SED -e "$sysroot_cmd s/\([ ']-[LR]\)=/\1/g; s/\([ ']\)=/\1/g" $lib \ > $tmpdir/tmp-la mv -f $tmpdir/tmp-la $lib done @@ -2711,7 +4123,7 @@ func_mode_finish () fi # Exit here if they wanted silent mode. - $opt_silent && exit $EXIT_SUCCESS + $opt_quiet && exit $EXIT_SUCCESS if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then echo "----------------------------------------------------------------------" @@ -2722,27 +4134,27 @@ func_mode_finish () echo echo "If you ever happen to want to link against installed libraries" echo "in a given directory, LIBDIR, you must either use libtool, and" - echo "specify the full pathname of the library, or use the \`-LLIBDIR'" + echo "specify the full pathname of the library, or use the '-LLIBDIR'" echo "flag during linking and do at least one of the following:" if test -n "$shlibpath_var"; then - echo " - add LIBDIR to the \`$shlibpath_var' environment variable" + echo " - add LIBDIR to the '$shlibpath_var' environment variable" echo " during execution" fi if test -n "$runpath_var"; then - echo " - add LIBDIR to the \`$runpath_var' environment variable" + echo " - add LIBDIR to the '$runpath_var' environment variable" echo " during linking" fi if test -n "$hardcode_libdir_flag_spec"; then libdir=LIBDIR eval flag=\"$hardcode_libdir_flag_spec\" - $ECHO " - use the \`$flag' linker flag" + $ECHO " - use the '$flag' linker flag" fi if test -n "$admincmds"; then $ECHO " - have your system administrator run these commands:$admincmds" fi if test -f /etc/ld.so.conf; then - echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + echo " - have your system administrator add LIBDIR to '/etc/ld.so.conf'" fi echo @@ -2761,18 +4173,20 @@ func_mode_finish () exit $EXIT_SUCCESS } -test "$opt_mode" = finish && func_mode_finish ${1+"$@"} +test finish = "$opt_mode" && func_mode_finish ${1+"$@"} # func_mode_install arg... func_mode_install () { - $opt_debug + $debug_cmd + # There may be an optional sh(1) argument at the beginning of # install_prog (especially on Windows NT). - if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + if test "$SHELL" = "$nonopt" || test /bin/sh = "$nonopt" || # Allow the use of GNU shtool's install command. - case $nonopt in *shtool*) :;; *) false;; esac; then + case $nonopt in *shtool*) :;; *) false;; esac + then # Aesthetically quote it. func_quote_for_eval "$nonopt" install_prog="$func_quote_for_eval_result " @@ -2799,7 +4213,7 @@ func_mode_install () opts= prev= install_type= - isdir=no + isdir=false stripme= no_mode=: for arg @@ -2812,7 +4226,7 @@ func_mode_install () fi case $arg in - -d) isdir=yes ;; + -d) isdir=: ;; -f) if $install_cp; then :; else prev=$arg @@ -2830,7 +4244,7 @@ func_mode_install () *) # If the previous option needed an argument, then skip it. if test -n "$prev"; then - if test "x$prev" = x-m && test -n "$install_override_mode"; then + if test X-m = "X$prev" && test -n "$install_override_mode"; then arg2=$install_override_mode no_mode=false fi @@ -2855,7 +4269,7 @@ func_mode_install () func_fatal_help "you must specify an install program" test -n "$prev" && \ - func_fatal_help "the \`$prev' option requires an argument" + func_fatal_help "the '$prev' option requires an argument" if test -n "$install_override_mode" && $no_mode; then if $install_cp; then :; else @@ -2877,19 +4291,19 @@ func_mode_install () dest=$func_stripname_result # Check to see that the destination is a directory. - test -d "$dest" && isdir=yes - if test "$isdir" = yes; then - destdir="$dest" + test -d "$dest" && isdir=: + if $isdir; then + destdir=$dest destname= else func_dirname_and_basename "$dest" "" "." - destdir="$func_dirname_result" - destname="$func_basename_result" + destdir=$func_dirname_result + destname=$func_basename_result # Not a directory, so check to see that there is only one file specified. set dummy $files; shift test "$#" -gt 1 && \ - func_fatal_help "\`$dest' is not a directory" + func_fatal_help "'$dest' is not a directory" fi case $destdir in [\\/]* | [A-Za-z]:[\\/]*) ;; @@ -2898,7 +4312,7 @@ func_mode_install () case $file in *.lo) ;; *) - func_fatal_help "\`$destdir' must be an absolute directory name" + func_fatal_help "'$destdir' must be an absolute directory name" ;; esac done @@ -2907,7 +4321,7 @@ func_mode_install () # This variable tells wrapper scripts just to set variables rather # than running their programs. - libtool_install_magic="$magic" + libtool_install_magic=$magic staticlibs= future_libdirs= @@ -2927,7 +4341,7 @@ func_mode_install () # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$file" \ - || func_fatal_help "\`$file' is not a valid libtool archive" + || func_fatal_help "'$file' is not a valid libtool archive" library_names= old_library= @@ -2949,7 +4363,7 @@ func_mode_install () fi func_dirname "$file" "/" "" - dir="$func_dirname_result" + dir=$func_dirname_result func_append dir "$objdir" if test -n "$relink_command"; then @@ -2963,7 +4377,7 @@ func_mode_install () # are installed into $libdir/../bin (currently, that works fine) # but it's something to keep an eye on. test "$inst_prefix_dir" = "$destdir" && \ - func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" + func_fatal_error "error: cannot install '$file' to a directory not ending in $libdir" if test -n "$inst_prefix_dir"; then # Stick the inst_prefix_dir data into the link command. @@ -2972,29 +4386,36 @@ func_mode_install () relink_command=`$ECHO "$relink_command" | $SED "s%@inst_prefix_dir@%%"` fi - func_warning "relinking \`$file'" + func_warning "relinking '$file'" func_show_eval "$relink_command" \ - 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' + 'func_fatal_error "error: relink '\''$file'\'' with the above command before installing it"' fi # See the names of the shared library. set dummy $library_names; shift if test -n "$1"; then - realname="$1" + realname=$1 shift - srcname="$realname" - test -n "$relink_command" && srcname="$realname"T + srcname=$realname + test -n "$relink_command" && srcname=${realname}T # Install the shared library and build the symlinks. func_show_eval "$install_shared_prog $dir/$srcname $destdir/$realname" \ 'exit $?' - tstripme="$stripme" + tstripme=$stripme case $host_os in cygwin* | mingw* | pw32* | cegcc*) case $realname in *.dll.a) - tstripme="" + tstripme= + ;; + esac + ;; + os2*) + case $realname in + *_dll.a) + tstripme= ;; esac ;; @@ -3005,7 +4426,7 @@ func_mode_install () if test "$#" -gt 0; then # Delete the old symlinks, and create new ones. - # Try `ln -sf' first, because the `ln' binary might depend on + # Try 'ln -sf' first, because the 'ln' binary might depend on # the symlink we replace! Solaris /bin/ln does not understand -f, # so we also need to try rm && ln -s. for linkname @@ -3016,14 +4437,14 @@ func_mode_install () fi # Do each command in the postinstall commands. - lib="$destdir/$realname" + lib=$destdir/$realname func_execute_cmds "$postinstall_cmds" 'exit $?' fi # Install the pseudo-library for information purposes. func_basename "$file" - name="$func_basename_result" - instname="$dir/$name"i + name=$func_basename_result + instname=$dir/${name}i func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' # Maybe install the static library, too. @@ -3035,11 +4456,11 @@ func_mode_install () # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then - destfile="$destdir/$destname" + destfile=$destdir/$destname else func_basename "$file" - destfile="$func_basename_result" - destfile="$destdir/$destfile" + destfile=$func_basename_result + destfile=$destdir/$destfile fi # Deduce the name of the destination old-style object file. @@ -3049,11 +4470,11 @@ func_mode_install () staticdest=$func_lo2o_result ;; *.$objext) - staticdest="$destfile" + staticdest=$destfile destfile= ;; *) - func_fatal_help "cannot copy a libtool object to \`$destfile'" + func_fatal_help "cannot copy a libtool object to '$destfile'" ;; esac @@ -3062,7 +4483,7 @@ func_mode_install () func_show_eval "$install_prog $file $destfile" 'exit $?' # Install the old object if enabled. - if test "$build_old_libs" = yes; then + if test yes = "$build_old_libs"; then # Deduce the name of the old-style object file. func_lo2o "$file" staticobj=$func_lo2o_result @@ -3074,23 +4495,23 @@ func_mode_install () *) # Figure out destination file name, if it wasn't already specified. if test -n "$destname"; then - destfile="$destdir/$destname" + destfile=$destdir/$destname else func_basename "$file" - destfile="$func_basename_result" - destfile="$destdir/$destfile" + destfile=$func_basename_result + destfile=$destdir/$destfile fi # If the file is missing, and there is a .exe on the end, strip it # because it is most likely a libtool script we actually want to # install - stripped_ext="" + stripped_ext= case $file in *.exe) if test ! -f "$file"; then func_stripname '' '.exe' "$file" file=$func_stripname_result - stripped_ext=".exe" + stripped_ext=.exe fi ;; esac @@ -3118,19 +4539,19 @@ func_mode_install () # Check the variables that should have been set. test -z "$generated_by_libtool_version" && \ - func_fatal_error "invalid libtool wrapper script \`$wrapper'" + func_fatal_error "invalid libtool wrapper script '$wrapper'" - finalize=yes + finalize=: for lib in $notinst_deplibs; do # Check to see that each library is installed. libdir= if test -f "$lib"; then func_source "$lib" fi - libfile="$libdir/"`$ECHO "$lib" | $SED 's%^.*/%%g'` ### testsuite: skip nested quoting test + libfile=$libdir/`$ECHO "$lib" | $SED 's%^.*/%%g'` if test -n "$libdir" && test ! -f "$libfile"; then - func_warning "\`$lib' has not been installed in \`$libdir'" - finalize=no + func_warning "'$lib' has not been installed in '$libdir'" + finalize=false fi done @@ -3138,29 +4559,29 @@ func_mode_install () func_source "$wrapper" outputname= - if test "$fast_install" = no && test -n "$relink_command"; then + if test no = "$fast_install" && test -n "$relink_command"; then $opt_dry_run || { - if test "$finalize" = yes; then + if $finalize; then tmpdir=`func_mktempdir` func_basename "$file$stripped_ext" - file="$func_basename_result" - outputname="$tmpdir/$file" + file=$func_basename_result + outputname=$tmpdir/$file # Replace the output file specification. relink_command=`$ECHO "$relink_command" | $SED 's%@OUTPUT@%'"$outputname"'%g'` - $opt_silent || { + $opt_quiet || { func_quote_for_expand "$relink_command" eval "func_echo $func_quote_for_expand_result" } if eval "$relink_command"; then : else - func_error "error: relink \`$file' with the above command before installing it" + func_error "error: relink '$file' with the above command before installing it" $opt_dry_run || ${RM}r "$tmpdir" continue fi - file="$outputname" + file=$outputname else - func_warning "cannot relink \`$file'" + func_warning "cannot relink '$file'" fi } else @@ -3197,15 +4618,17 @@ func_mode_install () for file in $staticlibs; do func_basename "$file" - name="$func_basename_result" + name=$func_basename_result # Set up the ranlib parameters. - oldlib="$destdir/$name" + oldlib=$destdir/$name + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result func_show_eval "$install_prog \$file \$oldlib" 'exit $?' if test -n "$stripme" && test -n "$old_striplib"; then - func_show_eval "$old_striplib $oldlib" 'exit $?' + func_show_eval "$old_striplib $tool_oldlib" 'exit $?' fi # Do each command in the postinstall commands. @@ -3213,18 +4636,18 @@ func_mode_install () done test -n "$future_libdirs" && \ - func_warning "remember to run \`$progname --finish$future_libdirs'" + func_warning "remember to run '$progname --finish$future_libdirs'" if test -n "$current_libdirs"; then # Maybe just do a dry run. $opt_dry_run && current_libdirs=" -n$current_libdirs" - exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + exec_cmd='$SHELL "$progpath" $preserve_args --finish$current_libdirs' else exit $EXIT_SUCCESS fi } -test "$opt_mode" = install && func_mode_install ${1+"$@"} +test install = "$opt_mode" && func_mode_install ${1+"$@"} # func_generate_dlsyms outputname originator pic_p @@ -3232,16 +4655,17 @@ test "$opt_mode" = install && func_mode_install ${1+"$@"} # a dlpreopen symbol table. func_generate_dlsyms () { - $opt_debug - my_outputname="$1" - my_originator="$2" - my_pic_p="${3-no}" - my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` + $debug_cmd + + my_outputname=$1 + my_originator=$2 + my_pic_p=${3-false} + my_prefix=`$ECHO "$my_originator" | $SED 's%[^a-zA-Z0-9]%_%g'` my_dlsyms= - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then if test -n "$NM" && test -n "$global_symbol_pipe"; then - my_dlsyms="${my_outputname}S.c" + my_dlsyms=${my_outputname}S.c else func_error "not configured to extract global symbols from dlpreopened files" fi @@ -3252,7 +4676,7 @@ func_generate_dlsyms () "") ;; *.c) # Discover the nlist of each of the dlfiles. - nlist="$output_objdir/${my_outputname}.nm" + nlist=$output_objdir/$my_outputname.nm func_show_eval "$RM $nlist ${nlist}S ${nlist}T" @@ -3260,34 +4684,36 @@ func_generate_dlsyms () func_verbose "creating $output_objdir/$my_dlsyms" $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ -/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ -/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ +/* $my_dlsyms - symbol resolution table for '$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE) $VERSION */ #ifdef __cplusplus extern \"C\" { #endif -#if defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) +#if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored \"-Wstrict-prototypes\" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ -#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) -/* DATA imports from DLLs on WIN32 con't be const, because runtime +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST -#elif defined(__osf__) +#elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif +#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) + /* External symbol declarations for the compiler. */\ " - if test "$dlself" = yes; then - func_verbose "generating symbol list for \`$output'" + if test yes = "$dlself"; then + func_verbose "generating symbol list for '$output'" $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" @@ -3295,7 +4721,7 @@ extern \"C\" { progfiles=`$ECHO "$objs$old_deplibs" | $SP2NL | $SED "$lo2o" | $NL2SP` for progfile in $progfiles; do func_to_tool_file "$progfile" func_convert_file_msys_to_w32 - func_verbose "extracting global C symbols from \`$func_to_tool_file_result'" + func_verbose "extracting global C symbols from '$func_to_tool_file_result'" $opt_dry_run || eval "$NM $func_to_tool_file_result | $global_symbol_pipe >> '$nlist'" done @@ -3315,10 +4741,10 @@ extern \"C\" { # Prepare the list of exported symbols if test -z "$export_symbols"; then - export_symbols="$output_objdir/$outputname.exp" + export_symbols=$output_objdir/$outputname.exp $opt_dry_run || { $RM $export_symbols - eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + eval "$SED -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' case $host in *cygwin* | *mingw* | *cegcc* ) eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' @@ -3328,7 +4754,7 @@ extern \"C\" { } else $opt_dry_run || { - eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval "$SED -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' eval '$MV "$nlist"T "$nlist"' case $host in @@ -3342,22 +4768,22 @@ extern \"C\" { fi for dlprefile in $dlprefiles; do - func_verbose "extracting global C symbols from \`$dlprefile'" + func_verbose "extracting global C symbols from '$dlprefile'" func_basename "$dlprefile" - name="$func_basename_result" + name=$func_basename_result case $host in *cygwin* | *mingw* | *cegcc* ) # if an import library, we need to obtain dlname if func_win32_import_lib_p "$dlprefile"; then func_tr_sh "$dlprefile" eval "curr_lafile=\$libfile_$func_tr_sh_result" - dlprefile_dlbasename="" + dlprefile_dlbasename= if test -n "$curr_lafile" && func_lalib_p "$curr_lafile"; then # Use subshell, to avoid clobbering current variable values dlprefile_dlname=`source "$curr_lafile" && echo "$dlname"` - if test -n "$dlprefile_dlname" ; then + if test -n "$dlprefile_dlname"; then func_basename "$dlprefile_dlname" - dlprefile_dlbasename="$func_basename_result" + dlprefile_dlbasename=$func_basename_result else # no lafile. user explicitly requested -dlpreopen . $sharedlib_from_linklib_cmd "$dlprefile" @@ -3365,7 +4791,7 @@ extern \"C\" { fi fi $opt_dry_run || { - if test -n "$dlprefile_dlbasename" ; then + if test -n "$dlprefile_dlbasename"; then eval '$ECHO ": $dlprefile_dlbasename" >> "$nlist"' else func_warning "Could not compute DLL name from $name" @@ -3421,6 +4847,11 @@ extern \"C\" { echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi + func_show_eval '$RM "${nlist}I"' + if test -n "$global_symbol_to_import"; then + eval "$global_symbol_to_import"' < "$nlist"S > "$nlist"I' + fi + echo >> "$output_objdir/$my_dlsyms" "\ /* The mapping between symbol names and symbols. */ @@ -3429,11 +4860,30 @@ typedef struct { void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist -lt_${my_prefix}_LTX_preloaded_symbols[]; +lt_${my_prefix}_LTX_preloaded_symbols[];\ +" + + if test -s "$nlist"I; then + echo >> "$output_objdir/$my_dlsyms" "\ +static void lt_syminit(void) +{ + LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; + for (; symbol->name; ++symbol) + {" + $SED 's/.*/ if (STREQ (symbol->name, \"&\")) symbol->address = (void *) \&&;/' < "$nlist"I >> "$output_objdir/$my_dlsyms" + echo >> "$output_objdir/$my_dlsyms" "\ + } +}" + fi + echo >> "$output_objdir/$my_dlsyms" "\ LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = -{\ - { \"$my_originator\", (void *) 0 }," +{ {\"$my_originator\", (void *) 0}," + + if test -s "$nlist"I; then + echo >> "$output_objdir/$my_dlsyms" "\ + {\"@INIT@\", (void *) <_syminit}," + fi case $need_lib_prefix in no) @@ -3470,14 +4920,12 @@ static const void *lt_preloaded_setup() { # linked before any other PIC object. But we must not use # pic_flag when linking with -static. The problem exists in # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. - *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + *-*-freebsd2.*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; *-*-hpux*) pic_flag_for_symtable=" $pic_flag" ;; *) - if test "X$my_pic_p" != Xno; then - pic_flag_for_symtable=" $pic_flag" - fi + $my_pic_p && pic_flag_for_symtable=" $pic_flag" ;; esac ;; @@ -3494,10 +4942,10 @@ static const void *lt_preloaded_setup() { func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' # Clean up the generated files. - func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T" "${nlist}I"' # Transform the symbol file into the correct name. - symfileobj="$output_objdir/${my_outputname}S.$objext" + symfileobj=$output_objdir/${my_outputname}S.$objext case $host in *cygwin* | *mingw* | *cegcc* ) if test -f "$output_objdir/$my_outputname.def"; then @@ -3515,7 +4963,7 @@ static const void *lt_preloaded_setup() { esac ;; *) - func_fatal_error "unknown suffix for \`$my_dlsyms'" + func_fatal_error "unknown suffix for '$my_dlsyms'" ;; esac else @@ -3529,6 +4977,32 @@ static const void *lt_preloaded_setup() { fi } +# func_cygming_gnu_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is a GNU/binutils-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_gnu_implib_p () +{ + $debug_cmd + + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` + test -n "$func_cygming_gnu_implib_tmp" +} + +# func_cygming_ms_implib_p ARG +# This predicate returns with zero status (TRUE) if +# ARG is an MS-style import library. Returns +# with nonzero status (FALSE) otherwise. +func_cygming_ms_implib_p () +{ + $debug_cmd + + func_to_tool_file "$1" func_convert_file_msys_to_w32 + func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` + test -n "$func_cygming_ms_implib_tmp" +} + # func_win32_libid arg # return the library type of file 'arg' # @@ -3538,8 +5012,9 @@ static const void *lt_preloaded_setup() { # Despite the name, also deal with 64 bit binaries. func_win32_libid () { - $opt_debug - win32_libid_type="unknown" + $debug_cmd + + win32_libid_type=unknown win32_fileres=`file -L $1 2>/dev/null` case $win32_fileres in *ar\ archive\ import\ library*) # definitely import @@ -3549,16 +5024,29 @@ func_win32_libid () # Keep the egrep pattern in sync with the one in _LT_CHECK_MAGIC_METHOD. if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | $EGREP 'file format (pei*-i386(.*architecture: i386)?|pe-arm-wince|pe-x86-64)' >/dev/null; then - func_to_tool_file "$1" func_convert_file_msys_to_w32 - win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | - $SED -n -e ' + case $nm_interface in + "MS dumpbin") + if func_cygming_ms_implib_p "$1" || + func_cygming_gnu_implib_p "$1" + then + win32_nmres=import + else + win32_nmres= + fi + ;; + *) + func_to_tool_file "$1" func_convert_file_msys_to_w32 + win32_nmres=`eval $NM -f posix -A \"$func_to_tool_file_result\" | + $SED -n -e ' 1,100{ / I /{ - s,.*,import, + s|.*|import| p q } }'` + ;; + esac case $win32_nmres in import*) win32_libid_type="x86 archive import";; *) win32_libid_type="x86 archive static";; @@ -3590,7 +5078,8 @@ func_win32_libid () # $sharedlib_from_linklib_result func_cygming_dll_for_implib () { - $opt_debug + $debug_cmd + sharedlib_from_linklib_result=`$DLLTOOL --identify-strict --identify "$1"` } @@ -3607,7 +5096,8 @@ func_cygming_dll_for_implib () # specified import library. func_cygming_dll_for_implib_fallback_core () { - $opt_debug + $debug_cmd + match_literal=`$ECHO "$1" | $SED "$sed_make_literal_regex"` $OBJDUMP -s --section "$1" "$2" 2>/dev/null | $SED '/^Contents of section '"$match_literal"':/{ @@ -3643,8 +5133,8 @@ func_cygming_dll_for_implib_fallback_core () /./p' | # we now have a list, one entry per line, of the stringified # contents of the appropriate section of all members of the - # archive which possess that section. Heuristic: eliminate - # all those which have a first or second character that is + # archive that possess that section. Heuristic: eliminate + # all those that have a first or second character that is # a '.' (that is, objdump's representation of an unprintable # character.) This should work for all archives with less than # 0x302f exports -- but will fail for DLLs whose name actually @@ -3655,30 +5145,6 @@ func_cygming_dll_for_implib_fallback_core () $SED -e '/^\./d;/^.\./d;q' } -# func_cygming_gnu_implib_p ARG -# This predicate returns with zero status (TRUE) if -# ARG is a GNU/binutils-style import library. Returns -# with nonzero status (FALSE) otherwise. -func_cygming_gnu_implib_p () -{ - $opt_debug - func_to_tool_file "$1" func_convert_file_msys_to_w32 - func_cygming_gnu_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $EGREP ' (_head_[A-Za-z0-9_]+_[ad]l*|[A-Za-z0-9_]+_[ad]l*_iname)$'` - test -n "$func_cygming_gnu_implib_tmp" -} - -# func_cygming_ms_implib_p ARG -# This predicate returns with zero status (TRUE) if -# ARG is an MS-style import library. Returns -# with nonzero status (FALSE) otherwise. -func_cygming_ms_implib_p () -{ - $opt_debug - func_to_tool_file "$1" func_convert_file_msys_to_w32 - func_cygming_ms_implib_tmp=`$NM "$func_to_tool_file_result" | eval "$global_symbol_pipe" | $GREP '_NULL_IMPORT_DESCRIPTOR'` - test -n "$func_cygming_ms_implib_tmp" -} - # func_cygming_dll_for_implib_fallback ARG # Platform-specific function to extract the # name of the DLL associated with the specified @@ -3692,16 +5158,17 @@ func_cygming_ms_implib_p () # $sharedlib_from_linklib_result func_cygming_dll_for_implib_fallback () { - $opt_debug - if func_cygming_gnu_implib_p "$1" ; then + $debug_cmd + + if func_cygming_gnu_implib_p "$1"; then # binutils import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$7' "$1"` - elif func_cygming_ms_implib_p "$1" ; then + elif func_cygming_ms_implib_p "$1"; then # ms-generated import library sharedlib_from_linklib_result=`func_cygming_dll_for_implib_fallback_core '.idata$6' "$1"` else # unknown - sharedlib_from_linklib_result="" + sharedlib_from_linklib_result= fi } @@ -3709,10 +5176,11 @@ func_cygming_dll_for_implib_fallback () # func_extract_an_archive dir oldlib func_extract_an_archive () { - $opt_debug - f_ex_an_ar_dir="$1"; shift - f_ex_an_ar_oldlib="$1" - if test "$lock_old_archive_extraction" = yes; then + $debug_cmd + + f_ex_an_ar_dir=$1; shift + f_ex_an_ar_oldlib=$1 + if test yes = "$lock_old_archive_extraction"; then lockfile=$f_ex_an_ar_oldlib.lock until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do func_echo "Waiting for $lockfile to be removed" @@ -3721,7 +5189,7 @@ func_extract_an_archive () fi func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" \ 'stat=$?; rm -f "$lockfile"; exit $stat' - if test "$lock_old_archive_extraction" = yes; then + if test yes = "$lock_old_archive_extraction"; then $opt_dry_run || rm -f "$lockfile" fi if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then @@ -3735,22 +5203,23 @@ func_extract_an_archive () # func_extract_archives gentop oldlib ... func_extract_archives () { - $opt_debug - my_gentop="$1"; shift + $debug_cmd + + my_gentop=$1; shift my_oldlibs=${1+"$@"} - my_oldobjs="" - my_xlib="" - my_xabs="" - my_xdir="" + my_oldobjs= + my_xlib= + my_xabs= + my_xdir= for my_xlib in $my_oldlibs; do # Extract the objects. case $my_xlib in - [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; + [\\/]* | [A-Za-z]:[\\/]*) my_xabs=$my_xlib ;; *) my_xabs=`pwd`"/$my_xlib" ;; esac func_basename "$my_xlib" - my_xlib="$func_basename_result" + my_xlib=$func_basename_result my_xlib_u=$my_xlib while :; do case " $extracted_archives " in @@ -3762,7 +5231,7 @@ func_extract_archives () esac done extracted_archives="$extracted_archives $my_xlib_u" - my_xdir="$my_gentop/$my_xlib_u" + my_xdir=$my_gentop/$my_xlib_u func_mkdir_p "$my_xdir" @@ -3775,22 +5244,23 @@ func_extract_archives () cd $my_xdir || exit $? darwin_archive=$my_xabs darwin_curdir=`pwd` - darwin_base_archive=`basename "$darwin_archive"` + func_basename "$darwin_archive" + darwin_base_archive=$func_basename_result darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` if test -n "$darwin_arches"; then darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` darwin_arch= func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" - for darwin_arch in $darwin_arches ; do - func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" - $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" - cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" - func_extract_an_archive "`pwd`" "${darwin_base_archive}" + for darwin_arch in $darwin_arches; do + func_mkdir_p "unfat-$$/$darwin_base_archive-$darwin_arch" + $LIPO -thin $darwin_arch -output "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" "$darwin_archive" + cd "unfat-$$/$darwin_base_archive-$darwin_arch" + func_extract_an_archive "`pwd`" "$darwin_base_archive" cd "$darwin_curdir" - $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + $RM "unfat-$$/$darwin_base_archive-$darwin_arch/$darwin_base_archive" done # $darwin_arches ## Okay now we've a bunch of thin objects, gotta fatten them up :) - darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$sed_basename" | sort -u` darwin_file= darwin_files= for darwin_file in $darwin_filelist; do @@ -3812,7 +5282,7 @@ func_extract_archives () my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | sort | $NL2SP` done - func_extract_archives_result="$my_oldobjs" + func_extract_archives_result=$my_oldobjs } @@ -3827,7 +5297,7 @@ func_extract_archives () # # ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR # variable will take. If 'yes', then the emitted script -# will assume that the directory in which it is stored is +# will assume that the directory where it is stored is # the $objdir directory. This is a cygwin/mingw-specific # behavior. func_emit_wrapper () @@ -3838,7 +5308,7 @@ func_emit_wrapper () #! $SHELL # $output - temporary wrapper script for $objdir/$outputname -# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # The $output program cannot be directly executed until all the libtool # libraries that it depends on are installed. @@ -3895,9 +5365,9 @@ _LTECHO_EOF' # Very basic option parsing. These options are (a) specific to # the libtool wrapper, (b) are identical between the wrapper -# /script/ and the wrapper /executable/ which is used only on +# /script/ and the wrapper /executable/ that is used only on # windows platforms, and (c) all begin with the string "--lt-" -# (application programs are unlikely to have options which match +# (application programs are unlikely to have options that match # this pattern). # # There are only two supported options: --lt-debug and @@ -3930,7 +5400,7 @@ func_parse_lt_options () # Print the debug banner immediately: if test -n \"\$lt_option_debug\"; then - echo \"${outputname}:${output}:\${LINENO}: libtool wrapper (GNU $PACKAGE$TIMESTAMP) $VERSION\" 1>&2 + echo \"$outputname:$output:\$LINENO: libtool wrapper (GNU $PACKAGE) $VERSION\" 1>&2 fi } @@ -3941,7 +5411,7 @@ func_lt_dump_args () lt_dump_args_N=1; for lt_arg do - \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[\$lt_dump_args_N]: \$lt_arg\" + \$ECHO \"$outputname:$output:\$LINENO: newargv[\$lt_dump_args_N]: \$lt_arg\" lt_dump_args_N=\`expr \$lt_dump_args_N + 1\` done } @@ -3955,7 +5425,7 @@ func_exec_program_core () *-*-mingw | *-*-os2* | *-cegcc*) $ECHO "\ if test -n \"\$lt_option_debug\"; then - \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir\\\\\$program\" 1>&2 + \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir\\\\\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} @@ -3965,7 +5435,7 @@ func_exec_program_core () *) $ECHO "\ if test -n \"\$lt_option_debug\"; then - \$ECHO \"${outputname}:${output}:\${LINENO}: newargv[0]: \$progdir/\$program\" 1>&2 + \$ECHO \"$outputname:$output:\$LINENO: newargv[0]: \$progdir/\$program\" 1>&2 func_lt_dump_args \${1+\"\$@\"} 1>&2 fi exec \"\$progdir/\$program\" \${1+\"\$@\"} @@ -3982,14 +5452,17 @@ func_exec_program_core () # launches target application with the remaining arguments. func_exec_program () { - for lt_wr_arg - do - case \$lt_wr_arg in - --lt-*) ;; - *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; - esac - shift - done + case \" \$* \" in + *\\ --lt-*) + for lt_wr_arg + do + case \$lt_wr_arg in + --lt-*) ;; + *) set x \"\$@\" \"\$lt_wr_arg\"; shift;; + esac + shift + done ;; + esac func_exec_program_core \${1+\"\$@\"} } @@ -4037,13 +5510,13 @@ func_exec_program () test -n \"\$absdir\" && thisdir=\"\$absdir\" " - if test "$fast_install" = yes; then + if test yes = "$fast_install"; then $ECHO "\ program=lt-'$outputname'$exeext progdir=\"\$thisdir/$objdir\" if test ! -f \"\$progdir/\$program\" || - { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | $SED 1q\`; \\ test \"X\$file\" != \"X\$progdir/\$program\"; }; then file=\"\$\$-\$program\" @@ -4060,7 +5533,7 @@ func_exec_program () if test -n \"\$relink_command\"; then if relink_command_output=\`eval \$relink_command 2>&1\`; then : else - $ECHO \"\$relink_command_output\" >&2 + \$ECHO \"\$relink_command_output\" >&2 $RM \"\$progdir/\$file\" exit 1 fi @@ -4095,7 +5568,7 @@ func_exec_program () fi # Export our shlibpath_var if we have one. - if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + if test yes = "$shlibpath_overrides_runpath" && test -n "$shlibpath_var" && test -n "$temp_rpath"; then $ECHO "\ # Add our own library path to $shlibpath_var $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" @@ -4115,7 +5588,7 @@ func_exec_program () fi else # The program doesn't exist. - \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 + \$ECHO \"\$0: error: '\$progdir/\$program' does not exist\" 1>&2 \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 \$ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 exit 1 @@ -4134,7 +5607,7 @@ func_emit_cwrapperexe_src () cat < #include +#define STREQ(s1, s2) (strcmp ((s1), (s2)) == 0) + /* declarations of non-ANSI functions */ -#if defined(__MINGW32__) +#if defined __MINGW32__ # ifdef __STRICT_ANSI__ int _putenv (const char *); # endif -#elif defined(__CYGWIN__) +#elif defined __CYGWIN__ # ifdef __STRICT_ANSI__ char *realpath (const char *, char *); int putenv (char *); int setenv (const char *, const char *, int); # endif -/* #elif defined (other platforms) ... */ +/* #elif defined other_platform || defined ... */ #endif /* portability defines, excluding path handling macros */ -#if defined(_MSC_VER) +#if defined _MSC_VER # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv # define S_IXUSR _S_IEXEC -# ifndef _INTPTR_T_DEFINED -# define _INTPTR_T_DEFINED -# define intptr_t int -# endif -#elif defined(__MINGW32__) +#elif defined __MINGW32__ # define setmode _setmode # define stat _stat # define chmod _chmod # define getcwd _getcwd # define putenv _putenv -#elif defined(__CYGWIN__) +#elif defined __CYGWIN__ # define HAVE_SETENV # define FOPEN_WB "wb" -/* #elif defined (other platforms) ... */ +/* #elif defined other platforms ... */ #endif -#if defined(PATH_MAX) +#if defined PATH_MAX # define LT_PATHMAX PATH_MAX -#elif defined(MAXPATHLEN) +#elif defined MAXPATHLEN # define LT_PATHMAX MAXPATHLEN #else # define LT_PATHMAX 1024 @@ -4228,8 +5699,8 @@ int setenv (const char *, const char *, int); # define PATH_SEPARATOR ':' #endif -#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ - defined (__OS2__) +#if defined _WIN32 || defined __MSDOS__ || defined __DJGPP__ || \ + defined __OS2__ # define HAVE_DOS_BASED_FILE_SYSTEM # define FOPEN_WB "wb" # ifndef DIR_SEPARATOR_2 @@ -4262,10 +5733,10 @@ int setenv (const char *, const char *, int); #define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) #define XFREE(stale) do { \ - if (stale) { free ((void *) stale); stale = 0; } \ + if (stale) { free (stale); stale = 0; } \ } while (0) -#if defined(LT_DEBUGWRAPPER) +#if defined LT_DEBUGWRAPPER static int lt_debug = 1; #else static int lt_debug = 0; @@ -4294,11 +5765,16 @@ void lt_dump_script (FILE *f); EOF cat < 0) && IS_PATH_SEPARATOR (new_value[len-1])) + size_t len = strlen (new_value); + while ((len > 0) && IS_PATH_SEPARATOR (new_value[len-1])) { - new_value[len-1] = '\0'; + new_value[--len] = '\0'; } lt_setenv (name, new_value); XFREE (new_value); @@ -5057,9 +6533,15 @@ void lt_dump_script (FILE* f) { EOF func_emit_wrapper yes | - $SED -e 's/\([\\"]\)/\\\1/g' \ - -e 's/^/ fputs ("/' -e 's/$/\\n", f);/' - + $SED -n -e ' +s/^\(.\{79\}\)\(..*\)/\1\ +\2/ +h +s/\([\\"]\)/\\\1/g +s/$/\\n/ +s/\([^\n]*\).*/ fputs ("\1", f);/p +g +D' cat <<"EOF" } EOF @@ -5070,27 +6552,47 @@ EOF # True if ARG is an import lib, as indicated by $file_magic_cmd func_win32_import_lib_p () { - $opt_debug + $debug_cmd + case `eval $file_magic_cmd \"\$1\" 2>/dev/null | $SED -e 10q` in *import*) : ;; *) false ;; esac } +# func_suncc_cstd_abi +# !!ONLY CALL THIS FOR SUN CC AFTER $compile_command IS FULLY EXPANDED!! +# Several compiler flags select an ABI that is incompatible with the +# Cstd library. Avoid specifying it if any are in CXXFLAGS. +func_suncc_cstd_abi () +{ + $debug_cmd + + case " $compile_command " in + *" -compat=g "*|*\ -std=c++[0-9][0-9]\ *|*" -library=stdcxx4 "*|*" -library=stlport4 "*) + suncc_use_cstd_abi=no + ;; + *) + suncc_use_cstd_abi=yes + ;; + esac +} + # func_mode_link arg... func_mode_link () { - $opt_debug + $debug_cmd + case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) # It is impossible to link a dll without this setting, and # we shouldn't force the makefile maintainer to figure out - # which system we are compiling for in order to pass an extra + # what system we are compiling for in order to pass an extra # flag for every libtool invocation. # allow_undefined=no # FIXME: Unfortunately, there are problems with the above when trying - # to make a dll which has undefined symbols, in which case not + # to make a dll that has undefined symbols, in which case not # even a static library is built. For now, we need to specify # -no-undefined on the libtool link line when we can be certain # that all symbols are satisfied, otherwise we get a static library. @@ -5134,10 +6636,11 @@ func_mode_link () module=no no_install=no objs= + os2dllname= non_pic_objects= precious_files_regex= prefer_static_libs=no - preload=no + preload=false prev= prevarg= release= @@ -5149,7 +6652,7 @@ func_mode_link () vinfo= vinfo_number=no weak_libs= - single_module="${wl}-single_module" + single_module=$wl-single_module func_infer_tag $base_compile # We need to know -static, to get the right output filenames. @@ -5157,15 +6660,15 @@ func_mode_link () do case $arg in -shared) - test "$build_libtool_libs" != yes && \ - func_fatal_configuration "can not build a shared library" + test yes != "$build_libtool_libs" \ + && func_fatal_configuration "cannot build a shared library" build_old_libs=no break ;; -all-static | -static | -static-libtool-libs) case $arg in -all-static) - if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + if test yes = "$build_libtool_libs" && test -z "$link_static_flag"; then func_warning "complete static linking is impossible in this configuration" fi if test -n "$link_static_flag"; then @@ -5198,7 +6701,7 @@ func_mode_link () # Go through the arguments, transforming them on the way. while test "$#" -gt 0; do - arg="$1" + arg=$1 shift func_quote_for_eval "$arg" qarg=$func_quote_for_eval_unquoted_result @@ -5215,21 +6718,21 @@ func_mode_link () case $prev in bindir) - bindir="$arg" + bindir=$arg prev= continue ;; dlfiles|dlprefiles) - if test "$preload" = no; then + $preload || { # Add the symbol object into the linking commands. func_append compile_command " @SYMFILE@" func_append finalize_command " @SYMFILE@" - preload=yes - fi + preload=: + } case $arg in *.la | *.lo) ;; # We handle these cases below. force) - if test "$dlself" = no; then + if test no = "$dlself"; then dlself=needless export_dynamic=yes fi @@ -5237,9 +6740,9 @@ func_mode_link () continue ;; self) - if test "$prev" = dlprefiles; then + if test dlprefiles = "$prev"; then dlself=yes - elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + elif test dlfiles = "$prev" && test yes != "$dlopen_self"; then dlself=yes else dlself=needless @@ -5249,7 +6752,7 @@ func_mode_link () continue ;; *) - if test "$prev" = dlfiles; then + if test dlfiles = "$prev"; then func_append dlfiles " $arg" else func_append dlprefiles " $arg" @@ -5260,14 +6763,14 @@ func_mode_link () esac ;; expsyms) - export_symbols="$arg" + export_symbols=$arg test -f "$arg" \ - || func_fatal_error "symbol file \`$arg' does not exist" + || func_fatal_error "symbol file '$arg' does not exist" prev= continue ;; expsyms_regex) - export_symbols_regex="$arg" + export_symbols_regex=$arg prev= continue ;; @@ -5285,7 +6788,13 @@ func_mode_link () continue ;; inst_prefix) - inst_prefix_dir="$arg" + inst_prefix_dir=$arg + prev= + continue + ;; + mllvm) + # Clang does not use LLVM to link, so we can simply discard any + # '-mllvm $arg' options when doing the link step. prev= continue ;; @@ -5309,21 +6818,21 @@ func_mode_link () if test -z "$pic_object" || test -z "$non_pic_object" || - test "$pic_object" = none && - test "$non_pic_object" = none; then - func_fatal_error "cannot find name of object for \`$arg'" + test none = "$pic_object" && + test none = "$non_pic_object"; then + func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" - xdir="$func_dirname_result" + xdir=$func_dirname_result - if test "$pic_object" != none; then + if test none != "$pic_object"; then # Prepend the subdirectory the object is found in. - pic_object="$xdir$pic_object" + pic_object=$xdir$pic_object - if test "$prev" = dlfiles; then - if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + if test dlfiles = "$prev"; then + if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue @@ -5334,7 +6843,7 @@ func_mode_link () fi # CHECK ME: I think I busted this. -Ossama - if test "$prev" = dlprefiles; then + if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= @@ -5342,23 +6851,23 @@ func_mode_link () # A PIC object. func_append libobjs " $pic_object" - arg="$pic_object" + arg=$pic_object fi # Non-PIC object. - if test "$non_pic_object" != none; then + if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. - non_pic_object="$xdir$non_pic_object" + non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" - if test -z "$pic_object" || test "$pic_object" = none ; then - arg="$non_pic_object" + if test -z "$pic_object" || test none = "$pic_object"; then + arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. - non_pic_object="$pic_object" + non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else @@ -5366,7 +6875,7 @@ func_mode_link () if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" - xdir="$func_dirname_result" + xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result @@ -5374,24 +6883,29 @@ func_mode_link () func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else - func_fatal_error "\`$arg' is not a valid libtool object" + func_fatal_error "'$arg' is not a valid libtool object" fi fi done else - func_fatal_error "link input file \`$arg' does not exist" + func_fatal_error "link input file '$arg' does not exist" fi arg=$save_arg prev= continue ;; + os2dllname) + os2dllname=$arg + prev= + continue + ;; precious_regex) - precious_files_regex="$arg" + precious_files_regex=$arg prev= continue ;; release) - release="-$arg" + release=-$arg prev= continue ;; @@ -5403,7 +6917,7 @@ func_mode_link () func_fatal_error "only absolute run-paths are allowed" ;; esac - if test "$prev" = rpath; then + if test rpath = "$prev"; then case "$rpath " in *" $arg "*) ;; *) func_append rpath " $arg" ;; @@ -5418,7 +6932,7 @@ func_mode_link () continue ;; shrext) - shrext_cmds="$arg" + shrext_cmds=$arg prev= continue ;; @@ -5458,7 +6972,7 @@ func_mode_link () esac fi # test -n "$prev" - prevarg="$arg" + prevarg=$arg case $arg in -all-static) @@ -5472,7 +6986,7 @@ func_mode_link () -allow-undefined) # FIXME: remove this flag sometime in the future. - func_fatal_error "\`-allow-undefined' must not be used because it is the default" + func_fatal_error "'-allow-undefined' must not be used because it is the default" ;; -avoid-version) @@ -5504,7 +7018,7 @@ func_mode_link () if test -n "$export_symbols" || test -n "$export_symbols_regex"; then func_fatal_error "more than one -exported-symbols argument is not allowed" fi - if test "X$arg" = "X-export-symbols"; then + if test X-export-symbols = "X$arg"; then prev=expsyms else prev=expsyms_regex @@ -5538,9 +7052,9 @@ func_mode_link () func_stripname "-L" '' "$arg" if test -z "$func_stripname_result"; then if test "$#" -gt 0; then - func_fatal_error "require no space between \`-L' and \`$1'" + func_fatal_error "require no space between '-L' and '$1'" else - func_fatal_error "need path for \`-L' option" + func_fatal_error "need path for '-L' option" fi fi func_resolve_sysroot "$func_stripname_result" @@ -5551,8 +7065,8 @@ func_mode_link () *) absdir=`cd "$dir" && pwd` test -z "$absdir" && \ - func_fatal_error "cannot determine absolute directory name of \`$dir'" - dir="$absdir" + func_fatal_error "cannot determine absolute directory name of '$dir'" + dir=$absdir ;; esac case "$deplibs " in @@ -5587,7 +7101,7 @@ func_mode_link () ;; -l*) - if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + if test X-lc = "X$arg" || test X-lm = "X$arg"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc* | *-*-haiku*) # These systems don't actually have a C or math library (as such) @@ -5595,11 +7109,11 @@ func_mode_link () ;; *-*-os2*) # These systems don't actually have a C library (as such) - test "X$arg" = "X-lc" && continue + test X-lc = "X$arg" && continue ;; - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc due to us having libc/libc_r. - test "X$arg" = "X-lc" && continue + test X-lc = "X$arg" && continue ;; *-*-rhapsody* | *-*-darwin1.[012]) # Rhapsody C and math libraries are in the System framework @@ -5608,16 +7122,16 @@ func_mode_link () ;; *-*-sco3.2v5* | *-*-sco5v6*) # Causes problems with __ctype - test "X$arg" = "X-lc" && continue + test X-lc = "X$arg" && continue ;; *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) # Compiler inserts libc in the correct place for threads to work - test "X$arg" = "X-lc" && continue + test X-lc = "X$arg" && continue ;; esac - elif test "X$arg" = "X-lc_r"; then + elif test X-lc_r = "X$arg"; then case $host in - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly* | *-*-bitrig*) # Do not include libc_r directly, use -pthread flag. continue ;; @@ -5627,6 +7141,11 @@ func_mode_link () continue ;; + -mllvm) + prev=mllvm + continue + ;; + -module) module=yes continue @@ -5643,7 +7162,8 @@ func_mode_link () continue ;; - -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads) + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) func_append compiler_flags " $arg" func_append compile_command " $arg" func_append finalize_command " $arg" @@ -5655,7 +7175,7 @@ func_mode_link () ;; -multi_module) - single_module="${wl}-multi_module" + single_module=$wl-multi_module continue ;; @@ -5669,8 +7189,8 @@ func_mode_link () *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) # The PATH hackery in wrapper scripts is required on Windows # and Darwin in order for the loader to find any dlls it needs. - func_warning "\`-no-install' is ignored for $host" - func_warning "assuming \`-no-fast-install' instead" + func_warning "'-no-install' is ignored for $host" + func_warning "assuming '-no-fast-install' instead" fast_install=no ;; *) no_install=yes ;; @@ -5688,6 +7208,11 @@ func_mode_link () continue ;; + -os2dllname) + prev=os2dllname + continue + ;; + -o) prev=output ;; -precious-files-regex) @@ -5775,14 +7300,14 @@ func_mode_link () func_stripname '-Wc,' '' "$arg" args=$func_stripname_result arg= - save_ifs="$IFS"; IFS=',' + save_ifs=$IFS; IFS=, for flag in $args; do - IFS="$save_ifs" + IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $func_quote_for_eval_result" func_append compiler_flags " $func_quote_for_eval_result" done - IFS="$save_ifs" + IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; @@ -5791,15 +7316,15 @@ func_mode_link () func_stripname '-Wl,' '' "$arg" args=$func_stripname_result arg= - save_ifs="$IFS"; IFS=',' + save_ifs=$IFS; IFS=, for flag in $args; do - IFS="$save_ifs" + IFS=$save_ifs func_quote_for_eval "$flag" func_append arg " $wl$func_quote_for_eval_result" func_append compiler_flags " $wl$func_quote_for_eval_result" func_append linker_flags " $func_quote_for_eval_result" done - IFS="$save_ifs" + IFS=$save_ifs func_stripname ' ' '' "$arg" arg=$func_stripname_result ;; @@ -5822,7 +7347,7 @@ func_mode_link () # -msg_* for osf cc -msg_*) func_quote_for_eval "$arg" - arg="$func_quote_for_eval_result" + arg=$func_quote_for_eval_result ;; # Flags to be passed through unchanged, with rationale: @@ -5834,25 +7359,52 @@ func_mode_link () # -m*, -t[45]*, -txscale* architecture-specific flags for GCC # -F/path path to uninstalled frameworks, gcc on darwin # -p, -pg, --coverage, -fprofile-* profiling flags for GCC + # -fstack-protector* stack protector flags for GCC # @file GCC response files # -tp=* Portland pgcc target processor selection # --sysroot=* for sysroot support - # -O*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + # -O*, -g*, -flto*, -fwhopr*, -fuse-linker-plugin GCC link-time optimization + # -specs=* GCC specs files + # -stdlib=* select c++ std lib with clang + # -fsanitize=* Clang/GCC memory and address sanitizer + # -fuse-ld=* Linker select flags for GCC + # -static-* direct GCC to link specific libraries statically + # -fcilkplus Cilk Plus language extension features for C/C++ -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*|-tp=*|--sysroot=*| \ - -O*|-flto*|-fwhopr*|-fuse-linker-plugin) + -O*|-g*|-flto*|-fwhopr*|-fuse-linker-plugin|-fstack-protector*|-stdlib=*| \ + -specs=*|-fsanitize=*|-fuse-ld=*|-static-*|-fcilkplus) func_quote_for_eval "$arg" - arg="$func_quote_for_eval_result" + arg=$func_quote_for_eval_result func_append compile_command " $arg" func_append finalize_command " $arg" func_append compiler_flags " $arg" continue ;; + -Z*) + if test os2 = "`expr $host : '.*\(os2\)'`"; then + # OS/2 uses -Zxxx to specify OS/2-specific options + compiler_flags="$compiler_flags $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case $arg in + -Zlinker | -Zstack) + prev=xcompiler + ;; + esac + continue + else + # Otherwise treat like 'Some other compiler flag' below + func_quote_for_eval "$arg" + arg=$func_quote_for_eval_result + fi + ;; + # Some other compiler flag. -* | +*) func_quote_for_eval "$arg" - arg="$func_quote_for_eval_result" + arg=$func_quote_for_eval_result ;; *.$objext) @@ -5873,21 +7425,21 @@ func_mode_link () if test -z "$pic_object" || test -z "$non_pic_object" || - test "$pic_object" = none && - test "$non_pic_object" = none; then - func_fatal_error "cannot find name of object for \`$arg'" + test none = "$pic_object" && + test none = "$non_pic_object"; then + func_fatal_error "cannot find name of object for '$arg'" fi # Extract subdirectory from the argument. func_dirname "$arg" "/" "" - xdir="$func_dirname_result" + xdir=$func_dirname_result - if test "$pic_object" != none; then + test none = "$pic_object" || { # Prepend the subdirectory the object is found in. - pic_object="$xdir$pic_object" + pic_object=$xdir$pic_object - if test "$prev" = dlfiles; then - if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + if test dlfiles = "$prev"; then + if test yes = "$build_libtool_libs" && test yes = "$dlopen_support"; then func_append dlfiles " $pic_object" prev= continue @@ -5898,7 +7450,7 @@ func_mode_link () fi # CHECK ME: I think I busted this. -Ossama - if test "$prev" = dlprefiles; then + if test dlprefiles = "$prev"; then # Preload the old-style object. func_append dlprefiles " $pic_object" prev= @@ -5906,23 +7458,23 @@ func_mode_link () # A PIC object. func_append libobjs " $pic_object" - arg="$pic_object" - fi + arg=$pic_object + } # Non-PIC object. - if test "$non_pic_object" != none; then + if test none != "$non_pic_object"; then # Prepend the subdirectory the object is found in. - non_pic_object="$xdir$non_pic_object" + non_pic_object=$xdir$non_pic_object # A standard non-PIC object func_append non_pic_objects " $non_pic_object" - if test -z "$pic_object" || test "$pic_object" = none ; then - arg="$non_pic_object" + if test -z "$pic_object" || test none = "$pic_object"; then + arg=$non_pic_object fi else # If the PIC object exists, use it instead. # $xdir was prepended to $pic_object above. - non_pic_object="$pic_object" + non_pic_object=$pic_object func_append non_pic_objects " $non_pic_object" fi else @@ -5930,7 +7482,7 @@ func_mode_link () if $opt_dry_run; then # Extract subdirectory from the argument. func_dirname "$arg" "/" "" - xdir="$func_dirname_result" + xdir=$func_dirname_result func_lo2o "$arg" pic_object=$xdir$objdir/$func_lo2o_result @@ -5938,7 +7490,7 @@ func_mode_link () func_append libobjs " $pic_object" func_append non_pic_objects " $non_pic_object" else - func_fatal_error "\`$arg' is not a valid libtool object" + func_fatal_error "'$arg' is not a valid libtool object" fi fi ;; @@ -5954,11 +7506,11 @@ func_mode_link () # A libtool-controlled library. func_resolve_sysroot "$arg" - if test "$prev" = dlfiles; then + if test dlfiles = "$prev"; then # This library was specified with -dlopen. func_append dlfiles " $func_resolve_sysroot_result" prev= - elif test "$prev" = dlprefiles; then + elif test dlprefiles = "$prev"; then # The library was specified with -dlpreopen. func_append dlprefiles " $func_resolve_sysroot_result" prev= @@ -5973,7 +7525,7 @@ func_mode_link () # Unknown arguments in both finalize_command and compile_command need # to be aesthetically quoted because they are evaled later. func_quote_for_eval "$arg" - arg="$func_quote_for_eval_result" + arg=$func_quote_for_eval_result ;; esac # arg @@ -5985,9 +7537,9 @@ func_mode_link () done # argument parsing loop test -n "$prev" && \ - func_fatal_help "the \`$prevarg' option requires an argument" + func_fatal_help "the '$prevarg' option requires an argument" - if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + if test yes = "$export_dynamic" && test -n "$export_dynamic_flag_spec"; then eval arg=\"$export_dynamic_flag_spec\" func_append compile_command " $arg" func_append finalize_command " $arg" @@ -5996,20 +7548,23 @@ func_mode_link () oldlibs= # calculate the name of the file, without its directory func_basename "$output" - outputname="$func_basename_result" - libobjs_save="$libobjs" + outputname=$func_basename_result + libobjs_save=$libobjs if test -n "$shlibpath_var"; then # get the directories listed in $shlibpath_var - eval shlib_search_path=\`\$ECHO \"\${$shlibpath_var}\" \| \$SED \'s/:/ /g\'\` + eval shlib_search_path=\`\$ECHO \"\$$shlibpath_var\" \| \$SED \'s/:/ /g\'\` else shlib_search_path= fi eval sys_lib_search_path=\"$sys_lib_search_path_spec\" eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + # Definition is injected by LT_CONFIG during libtool generation. + func_munge_path_list sys_lib_dlsearch_path "$LT_SYS_LIBRARY_PATH" + func_dirname "$output" "/" "" - output_objdir="$func_dirname_result$objdir" + output_objdir=$func_dirname_result$objdir func_to_tool_file "$output_objdir/" tool_output_objdir=$func_to_tool_file_result # Create the object directory. @@ -6032,7 +7587,7 @@ func_mode_link () # Find all interdependent deplibs by searching for libraries # that are linked more than once (e.g. -la -lb -la) for deplib in $deplibs; do - if $opt_preserve_dup_deps ; then + if $opt_preserve_dup_deps; then case "$libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac @@ -6040,7 +7595,7 @@ func_mode_link () func_append libs " $deplib" done - if test "$linkmode" = lib; then + if test lib = "$linkmode"; then libs="$predeps $libs $compiler_lib_search_path $postdeps" # Compute libraries that are listed more than once in $predeps @@ -6072,7 +7627,7 @@ func_mode_link () case $file in *.la) ;; *) - func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" + func_fatal_help "libraries can '-dlopen' only libtool libraries: $file" ;; esac done @@ -6080,7 +7635,7 @@ func_mode_link () prog) compile_deplibs= finalize_deplibs= - alldeplibs=no + alldeplibs=false newdlfiles= newdlprefiles= passes="conv scan dlopen dlpreopen link" @@ -6092,32 +7647,32 @@ func_mode_link () for pass in $passes; do # The preopen pass in lib mode reverses $deplibs; put it back here # so that -L comes before libs that need it for instance... - if test "$linkmode,$pass" = "lib,link"; then + if test lib,link = "$linkmode,$pass"; then ## FIXME: Find the place where the list is rebuilt in the wrong ## order, and fix it there properly tmp_deplibs= for deplib in $deplibs; do tmp_deplibs="$deplib $tmp_deplibs" done - deplibs="$tmp_deplibs" + deplibs=$tmp_deplibs fi - if test "$linkmode,$pass" = "lib,link" || - test "$linkmode,$pass" = "prog,scan"; then - libs="$deplibs" + if test lib,link = "$linkmode,$pass" || + test prog,scan = "$linkmode,$pass"; then + libs=$deplibs deplibs= fi - if test "$linkmode" = prog; then + if test prog = "$linkmode"; then case $pass in - dlopen) libs="$dlfiles" ;; - dlpreopen) libs="$dlprefiles" ;; + dlopen) libs=$dlfiles ;; + dlpreopen) libs=$dlprefiles ;; link) libs="$deplibs %DEPLIBS%" test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" ;; esac fi - if test "$linkmode,$pass" = "lib,dlpreopen"; then + if test lib,dlpreopen = "$linkmode,$pass"; then # Collect and forward deplibs of preopened libtool libs for lib in $dlprefiles; do # Ignore non-libtool-libs @@ -6138,25 +7693,26 @@ func_mode_link () esac done done - libs="$dlprefiles" + libs=$dlprefiles fi - if test "$pass" = dlopen; then + if test dlopen = "$pass"; then # Collect dlpreopened libraries - save_deplibs="$deplibs" + save_deplibs=$deplibs deplibs= fi for deplib in $libs; do lib= - found=no + found=false case $deplib in - -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads) - if test "$linkmode,$pass" = "prog,link"; then + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe \ + |-threads|-fopenmp|-openmp|-mp|-xopenmp|-omp|-qsmp=*) + if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else func_append compiler_flags " $deplib" - if test "$linkmode" = lib ; then + if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; @@ -6166,13 +7722,13 @@ func_mode_link () continue ;; -l*) - if test "$linkmode" != lib && test "$linkmode" != prog; then - func_warning "\`-l' is ignored for archives/objects" + if test lib != "$linkmode" && test prog != "$linkmode"; then + func_warning "'-l' is ignored for archives/objects" continue fi func_stripname '-l' '' "$deplib" name=$func_stripname_result - if test "$linkmode" = lib; then + if test lib = "$linkmode"; then searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" else searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" @@ -6180,31 +7736,22 @@ func_mode_link () for searchdir in $searchdirs; do for search_ext in .la $std_shrext .so .a; do # Search the libtool library - lib="$searchdir/lib${name}${search_ext}" + lib=$searchdir/lib$name$search_ext if test -f "$lib"; then - if test "$search_ext" = ".la"; then - found=yes + if test .la = "$search_ext"; then + found=: else - found=no + found=false fi break 2 fi done done - if test "$found" != yes; then - # deplib doesn't seem to be a libtool library - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" - fi - continue - else # deplib is a libtool library + if $found; then + # deplib is a libtool library # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, # We need to do some special things here, and not later. - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $deplib "*) if func_lalib_p "$lib"; then @@ -6212,19 +7759,19 @@ func_mode_link () old_library= func_source "$lib" for l in $old_library $library_names; do - ll="$l" + ll=$l done - if test "X$ll" = "X$old_library" ; then # only static version available - found=no + if test "X$ll" = "X$old_library"; then # only static version available + found=false func_dirname "$lib" "" "." - ladir="$func_dirname_result" + ladir=$func_dirname_result lib=$ladir/$old_library - if test "$linkmode,$pass" = "prog,link"; then + if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" - test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" fi continue fi @@ -6233,15 +7780,25 @@ func_mode_link () *) ;; esac fi + else + # deplib doesn't seem to be a libtool library + if test prog,link = "$linkmode,$pass"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test lib = "$linkmode" && newdependency_libs="$deplib $newdependency_libs" + fi + continue fi ;; # -l *.ltframework) - if test "$linkmode,$pass" = "prog,link"; then + if test prog,link = "$linkmode,$pass"; then compile_deplibs="$deplib $compile_deplibs" finalize_deplibs="$deplib $finalize_deplibs" else deplibs="$deplib $deplibs" - if test "$linkmode" = lib ; then + if test lib = "$linkmode"; then case "$new_inherited_linker_flags " in *" $deplib "*) ;; * ) func_append new_inherited_linker_flags " $deplib" ;; @@ -6254,18 +7811,18 @@ func_mode_link () case $linkmode in lib) deplibs="$deplib $deplibs" - test "$pass" = conv && continue + test conv = "$pass" && continue newdependency_libs="$deplib $newdependency_libs" func_stripname '-L' '' "$deplib" func_resolve_sysroot "$func_stripname_result" func_append newlib_search_path " $func_resolve_sysroot_result" ;; prog) - if test "$pass" = conv; then + if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi - if test "$pass" = scan; then + if test scan = "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" @@ -6276,13 +7833,13 @@ func_mode_link () func_append newlib_search_path " $func_resolve_sysroot_result" ;; *) - func_warning "\`-L' is ignored for archives/objects" + func_warning "'-L' is ignored for archives/objects" ;; esac # linkmode continue ;; # -L -R*) - if test "$pass" = link; then + if test link = "$pass"; then func_stripname '-R' '' "$deplib" func_resolve_sysroot "$func_stripname_result" dir=$func_resolve_sysroot_result @@ -6300,7 +7857,7 @@ func_mode_link () lib=$func_resolve_sysroot_result ;; *.$libext) - if test "$pass" = conv; then + if test conv = "$pass"; then deplibs="$deplib $deplibs" continue fi @@ -6311,21 +7868,26 @@ func_mode_link () case " $dlpreconveniencelibs " in *" $deplib "*) ;; *) - valid_a_lib=no + valid_a_lib=false case $deplibs_check_method in match_pattern*) set dummy $deplibs_check_method; shift match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` if eval "\$ECHO \"$deplib\"" 2>/dev/null | $SED 10q \ | $EGREP "$match_pattern_regex" > /dev/null; then - valid_a_lib=yes + valid_a_lib=: fi ;; pass_all) - valid_a_lib=yes + valid_a_lib=: ;; esac - if test "$valid_a_lib" != yes; then + if $valid_a_lib; then + echo + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + else echo $ECHO "*** Warning: Trying to link with static lib archive $deplib." echo "*** I have the capability to make that library automatically link in when" @@ -6333,18 +7895,13 @@ func_mode_link () echo "*** shared version of the library, which you do not appear to have" echo "*** because the file extensions .$libext of this argument makes me believe" echo "*** that it is just a static archive that I should not use here." - else - echo - $ECHO "*** Warning: Linking the shared library $output against the" - $ECHO "*** static library $deplib is not portable!" - deplibs="$deplib $deplibs" fi ;; esac continue ;; prog) - if test "$pass" != link; then + if test link != "$pass"; then deplibs="$deplib $deplibs" else compile_deplibs="$deplib $compile_deplibs" @@ -6355,10 +7912,10 @@ func_mode_link () esac # linkmode ;; # *.$libext *.lo | *.$objext) - if test "$pass" = conv; then + if test conv = "$pass"; then deplibs="$deplib $deplibs" - elif test "$linkmode" = prog; then - if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + elif test prog = "$linkmode"; then + if test dlpreopen = "$pass" || test yes != "$dlopen_support" || test no = "$build_libtool_libs"; then # If there is no dlopen support or we're linking statically, # we need to preload. func_append newdlprefiles " $deplib" @@ -6371,22 +7928,20 @@ func_mode_link () continue ;; %DEPLIBS%) - alldeplibs=yes + alldeplibs=: continue ;; esac # case $deplib - if test "$found" = yes || test -f "$lib"; then : - else - func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" - fi + $found || test -f "$lib" \ + || func_fatal_error "cannot find the library '$lib' or unhandled argument '$deplib'" # Check to see that this really is a libtool archive. func_lalib_unsafe_p "$lib" \ - || func_fatal_error "\`$lib' is not a valid libtool archive" + || func_fatal_error "'$lib' is not a valid libtool archive" func_dirname "$lib" "" "." - ladir="$func_dirname_result" + ladir=$func_dirname_result dlname= dlopen= @@ -6416,19 +7971,19 @@ func_mode_link () done fi dependency_libs=`$ECHO " $dependency_libs" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` - if test "$linkmode,$pass" = "lib,link" || - test "$linkmode,$pass" = "prog,scan" || - { test "$linkmode" != prog && test "$linkmode" != lib; }; then + if test lib,link = "$linkmode,$pass" || + test prog,scan = "$linkmode,$pass" || + { test prog != "$linkmode" && test lib != "$linkmode"; }; then test -n "$dlopen" && func_append dlfiles " $dlopen" test -n "$dlpreopen" && func_append dlprefiles " $dlpreopen" fi - if test "$pass" = conv; then + if test conv = "$pass"; then # Only check for convenience libraries deplibs="$lib $deplibs" if test -z "$libdir"; then if test -z "$old_library"; then - func_fatal_error "cannot find name of link library for \`$lib'" + func_fatal_error "cannot find name of link library for '$lib'" fi # It is a libtool convenience library, so add in its objects. func_append convenience " $ladir/$objdir/$old_library" @@ -6436,15 +7991,15 @@ func_mode_link () tmp_libs= for deplib in $dependency_libs; do deplibs="$deplib $deplibs" - if $opt_preserve_dup_deps ; then + if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac fi func_append tmp_libs " $deplib" done - elif test "$linkmode" != prog && test "$linkmode" != lib; then - func_fatal_error "\`$lib' is not a convenience library" + elif test prog != "$linkmode" && test lib != "$linkmode"; then + func_fatal_error "'$lib' is not a convenience library" fi continue fi # $pass = conv @@ -6453,26 +8008,26 @@ func_mode_link () # Get the name of the library we link against. linklib= if test -n "$old_library" && - { test "$prefer_static_libs" = yes || - test "$prefer_static_libs,$installed" = "built,no"; }; then + { test yes = "$prefer_static_libs" || + test built,no = "$prefer_static_libs,$installed"; }; then linklib=$old_library else for l in $old_library $library_names; do - linklib="$l" + linklib=$l done fi if test -z "$linklib"; then - func_fatal_error "cannot find name of link library for \`$lib'" + func_fatal_error "cannot find name of link library for '$lib'" fi # This library was specified with -dlopen. - if test "$pass" = dlopen; then - if test -z "$libdir"; then - func_fatal_error "cannot -dlopen a convenience library: \`$lib'" - fi + if test dlopen = "$pass"; then + test -z "$libdir" \ + && func_fatal_error "cannot -dlopen a convenience library: '$lib'" if test -z "$dlname" || - test "$dlopen_support" != yes || - test "$build_libtool_libs" = no; then + test yes != "$dlopen_support" || + test no = "$build_libtool_libs" + then # If there is no dlname, no dlopen support or we're linking # statically, we need to preload. We also need to preload any # dependent libraries so libltdl's deplib preloader doesn't @@ -6486,40 +8041,40 @@ func_mode_link () # We need an absolute path. case $ladir in - [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir=$ladir ;; *) abs_ladir=`cd "$ladir" && pwd` if test -z "$abs_ladir"; then - func_warning "cannot determine absolute directory name of \`$ladir'" + func_warning "cannot determine absolute directory name of '$ladir'" func_warning "passing it literally to the linker, although it might fail" - abs_ladir="$ladir" + abs_ladir=$ladir fi ;; esac func_basename "$lib" - laname="$func_basename_result" + laname=$func_basename_result # Find the relevant object directory and library name. - if test "X$installed" = Xyes; then + if test yes = "$installed"; then if test ! -f "$lt_sysroot$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then - func_warning "library \`$lib' was moved." - dir="$ladir" - absdir="$abs_ladir" - libdir="$abs_ladir" + func_warning "library '$lib' was moved." + dir=$ladir + absdir=$abs_ladir + libdir=$abs_ladir else - dir="$lt_sysroot$libdir" - absdir="$lt_sysroot$libdir" + dir=$lt_sysroot$libdir + absdir=$lt_sysroot$libdir fi - test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + test yes = "$hardcode_automatic" && avoidtemprpath=yes else if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then - dir="$ladir" - absdir="$abs_ladir" + dir=$ladir + absdir=$abs_ladir # Remove this search path later func_append notinst_path " $abs_ladir" else - dir="$ladir/$objdir" - absdir="$abs_ladir/$objdir" + dir=$ladir/$objdir + absdir=$abs_ladir/$objdir # Remove this search path later func_append notinst_path " $abs_ladir" fi @@ -6528,11 +8083,11 @@ func_mode_link () name=$func_stripname_result # This library was specified with -dlpreopen. - if test "$pass" = dlpreopen; then - if test -z "$libdir" && test "$linkmode" = prog; then - func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" + if test dlpreopen = "$pass"; then + if test -z "$libdir" && test prog = "$linkmode"; then + func_fatal_error "only libraries may -dlpreopen a convenience library: '$lib'" fi - case "$host" in + case $host in # special handling for platforms with PE-DLLs. *cygwin* | *mingw* | *cegcc* ) # Linker will automatically link against shared library if both @@ -6576,9 +8131,9 @@ func_mode_link () if test -z "$libdir"; then # Link the convenience library - if test "$linkmode" = lib; then + if test lib = "$linkmode"; then deplibs="$dir/$old_library $deplibs" - elif test "$linkmode,$pass" = "prog,link"; then + elif test prog,link = "$linkmode,$pass"; then compile_deplibs="$dir/$old_library $compile_deplibs" finalize_deplibs="$dir/$old_library $finalize_deplibs" else @@ -6588,14 +8143,14 @@ func_mode_link () fi - if test "$linkmode" = prog && test "$pass" != link; then + if test prog = "$linkmode" && test link != "$pass"; then func_append newlib_search_path " $ladir" deplibs="$lib $deplibs" - linkalldeplibs=no - if test "$link_all_deplibs" != no || test -z "$library_names" || - test "$build_libtool_libs" = no; then - linkalldeplibs=yes + linkalldeplibs=false + if test no != "$link_all_deplibs" || test -z "$library_names" || + test no = "$build_libtool_libs"; then + linkalldeplibs=: fi tmp_libs= @@ -6607,14 +8162,14 @@ func_mode_link () ;; esac # Need to link against all dependency_libs? - if test "$linkalldeplibs" = yes; then + if $linkalldeplibs; then deplibs="$deplib $deplibs" else # Need to hardcode shared library paths # or/and link against static libraries newdependency_libs="$deplib $newdependency_libs" fi - if $opt_preserve_dup_deps ; then + if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $deplib "*) func_append specialdeplibs " $deplib" ;; esac @@ -6624,15 +8179,15 @@ func_mode_link () continue fi # $linkmode = prog... - if test "$linkmode,$pass" = "prog,link"; then + if test prog,link = "$linkmode,$pass"; then if test -n "$library_names" && - { { test "$prefer_static_libs" = no || - test "$prefer_static_libs,$installed" = "built,yes"; } || + { { test no = "$prefer_static_libs" || + test built,yes = "$prefer_static_libs,$installed"; } || test -z "$old_library"; }; then # We need to hardcode the library path - if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + if test -n "$shlibpath_var" && test -z "$avoidtemprpath"; then # Make sure the rpath contains only unique directories. - case "$temp_rpath:" in + case $temp_rpath: in *"$absdir:"*) ;; *) func_append temp_rpath "$absdir:" ;; esac @@ -6661,9 +8216,9 @@ func_mode_link () esac fi # $linkmode,$pass = prog,link... - if test "$alldeplibs" = yes && - { test "$deplibs_check_method" = pass_all || - { test "$build_libtool_libs" = yes && + if $alldeplibs && + { test pass_all = "$deplibs_check_method" || + { test yes = "$build_libtool_libs" && test -n "$library_names"; }; }; then # We only need to search for static libraries continue @@ -6672,19 +8227,19 @@ func_mode_link () link_static=no # Whether the deplib will be linked statically use_static_libs=$prefer_static_libs - if test "$use_static_libs" = built && test "$installed" = yes; then + if test built = "$use_static_libs" && test yes = "$installed"; then use_static_libs=no fi if test -n "$library_names" && - { test "$use_static_libs" = no || test -z "$old_library"; }; then + { test no = "$use_static_libs" || test -z "$old_library"; }; then case $host in - *cygwin* | *mingw* | *cegcc*) + *cygwin* | *mingw* | *cegcc* | *os2*) # No point in relinking DLLs because paths are not encoded func_append notinst_deplibs " $lib" need_relink=no ;; *) - if test "$installed" = no; then + if test no = "$installed"; then func_append notinst_deplibs " $lib" need_relink=yes fi @@ -6694,24 +8249,24 @@ func_mode_link () # Warn about portability, can't link against -module's on some # systems (darwin). Don't bleat about dlopened modules though! - dlopenmodule="" + dlopenmodule= for dlpremoduletest in $dlprefiles; do if test "X$dlpremoduletest" = "X$lib"; then - dlopenmodule="$dlpremoduletest" + dlopenmodule=$dlpremoduletest break fi done - if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then + if test -z "$dlopenmodule" && test yes = "$shouldnotlink" && test link = "$pass"; then echo - if test "$linkmode" = prog; then + if test prog = "$linkmode"; then $ECHO "*** Warning: Linking the executable $output against the loadable module" else $ECHO "*** Warning: Linking the shared library $output against the loadable module" fi $ECHO "*** $linklib is not portable!" fi - if test "$linkmode" = lib && - test "$hardcode_into_libs" = yes; then + if test lib = "$linkmode" && + test yes = "$hardcode_into_libs"; then # Hardcode the library path. # Skip directories that are in the system default run-time # search path. @@ -6739,43 +8294,43 @@ func_mode_link () # figure out the soname set dummy $library_names shift - realname="$1" + realname=$1 shift libname=`eval "\\$ECHO \"$libname_spec\""` # use dlname if we got it. it's perfectly good, no? if test -n "$dlname"; then - soname="$dlname" + soname=$dlname elif test -n "$soname_spec"; then # bleh windows case $host in - *cygwin* | mingw* | *cegcc*) + *cygwin* | mingw* | *cegcc* | *os2*) func_arith $current - $age major=$func_arith_result - versuffix="-$major" + versuffix=-$major ;; esac eval soname=\"$soname_spec\" else - soname="$realname" + soname=$realname fi # Make a new name for the extract_expsyms_cmds to use - soroot="$soname" + soroot=$soname func_basename "$soroot" - soname="$func_basename_result" + soname=$func_basename_result func_stripname 'lib' '.dll' "$soname" newlib=libimp-$func_stripname_result.a # If the library has no export list, then create one now if test -f "$output_objdir/$soname-def"; then : else - func_verbose "extracting exported symbol list from \`$soname'" + func_verbose "extracting exported symbol list from '$soname'" func_execute_cmds "$extract_expsyms_cmds" 'exit $?' fi # Create $newlib if test -f "$output_objdir/$newlib"; then :; else - func_verbose "generating import library for \`$soname'" + func_verbose "generating import library for '$soname'" func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' fi # make sure the library variables are pointing to the new library @@ -6783,58 +8338,58 @@ func_mode_link () linklib=$newlib fi # test -n "$old_archive_from_expsyms_cmds" - if test "$linkmode" = prog || test "$opt_mode" != relink; then + if test prog = "$linkmode" || test relink != "$opt_mode"; then add_shlibpath= add_dir= add= lib_linked=yes case $hardcode_action in immediate | unsupported) - if test "$hardcode_direct" = no; then - add="$dir/$linklib" + if test no = "$hardcode_direct"; then + add=$dir/$linklib case $host in - *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; - *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sco3.2v5.0.[024]*) add_dir=-L$dir ;; + *-*-sysv4*uw2*) add_dir=-L$dir ;; *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ - *-*-unixware7*) add_dir="-L$dir" ;; + *-*-unixware7*) add_dir=-L$dir ;; *-*-darwin* ) - # if the lib is a (non-dlopened) module then we can not + # if the lib is a (non-dlopened) module then we cannot # link against it, someone is ignoring the earlier warnings if /usr/bin/file -L $add 2> /dev/null | - $GREP ": [^:]* bundle" >/dev/null ; then + $GREP ": [^:]* bundle" >/dev/null; then if test "X$dlopenmodule" != "X$lib"; then $ECHO "*** Warning: lib $linklib is a module, not a shared library" - if test -z "$old_library" ; then + if test -z "$old_library"; then echo echo "*** And there doesn't seem to be a static archive available" echo "*** The link will probably fail, sorry" else - add="$dir/$old_library" + add=$dir/$old_library fi elif test -n "$old_library"; then - add="$dir/$old_library" + add=$dir/$old_library fi fi esac - elif test "$hardcode_minus_L" = no; then + elif test no = "$hardcode_minus_L"; then case $host in - *-*-sunos*) add_shlibpath="$dir" ;; + *-*-sunos*) add_shlibpath=$dir ;; esac - add_dir="-L$dir" - add="-l$name" - elif test "$hardcode_shlibpath_var" = no; then - add_shlibpath="$dir" - add="-l$name" + add_dir=-L$dir + add=-l$name + elif test no = "$hardcode_shlibpath_var"; then + add_shlibpath=$dir + add=-l$name else lib_linked=no fi ;; relink) - if test "$hardcode_direct" = yes && - test "$hardcode_direct_absolute" = no; then - add="$dir/$linklib" - elif test "$hardcode_minus_L" = yes; then - add_dir="-L$dir" + if test yes = "$hardcode_direct" && + test no = "$hardcode_direct_absolute"; then + add=$dir/$linklib + elif test yes = "$hardcode_minus_L"; then + add_dir=-L$absdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in @@ -6843,10 +8398,10 @@ func_mode_link () ;; esac fi - add="-l$name" - elif test "$hardcode_shlibpath_var" = yes; then - add_shlibpath="$dir" - add="-l$name" + add=-l$name + elif test yes = "$hardcode_shlibpath_var"; then + add_shlibpath=$dir + add=-l$name else lib_linked=no fi @@ -6854,7 +8409,7 @@ func_mode_link () *) lib_linked=no ;; esac - if test "$lib_linked" != yes; then + if test yes != "$lib_linked"; then func_fatal_configuration "unsupported hardcode properties" fi @@ -6864,15 +8419,15 @@ func_mode_link () *) func_append compile_shlibpath "$add_shlibpath:" ;; esac fi - if test "$linkmode" = prog; then + if test prog = "$linkmode"; then test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" test -n "$add" && compile_deplibs="$add $compile_deplibs" else test -n "$add_dir" && deplibs="$add_dir $deplibs" test -n "$add" && deplibs="$add $deplibs" - if test "$hardcode_direct" != yes && - test "$hardcode_minus_L" != yes && - test "$hardcode_shlibpath_var" = yes; then + if test yes != "$hardcode_direct" && + test yes != "$hardcode_minus_L" && + test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; @@ -6881,33 +8436,33 @@ func_mode_link () fi fi - if test "$linkmode" = prog || test "$opt_mode" = relink; then + if test prog = "$linkmode" || test relink = "$opt_mode"; then add_shlibpath= add_dir= add= # Finalize command for both is simple: just hardcode it. - if test "$hardcode_direct" = yes && - test "$hardcode_direct_absolute" = no; then - add="$libdir/$linklib" - elif test "$hardcode_minus_L" = yes; then - add_dir="-L$libdir" - add="-l$name" - elif test "$hardcode_shlibpath_var" = yes; then + if test yes = "$hardcode_direct" && + test no = "$hardcode_direct_absolute"; then + add=$libdir/$linklib + elif test yes = "$hardcode_minus_L"; then + add_dir=-L$libdir + add=-l$name + elif test yes = "$hardcode_shlibpath_var"; then case :$finalize_shlibpath: in *":$libdir:"*) ;; *) func_append finalize_shlibpath "$libdir:" ;; esac - add="-l$name" - elif test "$hardcode_automatic" = yes; then + add=-l$name + elif test yes = "$hardcode_automatic"; then if test -n "$inst_prefix_dir" && - test -f "$inst_prefix_dir$libdir/$linklib" ; then - add="$inst_prefix_dir$libdir/$linklib" + test -f "$inst_prefix_dir$libdir/$linklib"; then + add=$inst_prefix_dir$libdir/$linklib else - add="$libdir/$linklib" + add=$libdir/$linklib fi else # We cannot seem to hardcode it, guess we'll fake it. - add_dir="-L$libdir" + add_dir=-L$libdir # Try looking first in the location we're being installed to. if test -n "$inst_prefix_dir"; then case $libdir in @@ -6916,10 +8471,10 @@ func_mode_link () ;; esac fi - add="-l$name" + add=-l$name fi - if test "$linkmode" = prog; then + if test prog = "$linkmode"; then test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" test -n "$add" && finalize_deplibs="$add $finalize_deplibs" else @@ -6927,43 +8482,43 @@ func_mode_link () test -n "$add" && deplibs="$add $deplibs" fi fi - elif test "$linkmode" = prog; then + elif test prog = "$linkmode"; then # Here we assume that one of hardcode_direct or hardcode_minus_L # is not unsupported. This is valid on all known static and # shared platforms. - if test "$hardcode_direct" != unsupported; then - test -n "$old_library" && linklib="$old_library" + if test unsupported != "$hardcode_direct"; then + test -n "$old_library" && linklib=$old_library compile_deplibs="$dir/$linklib $compile_deplibs" finalize_deplibs="$dir/$linklib $finalize_deplibs" else compile_deplibs="-l$name -L$dir $compile_deplibs" finalize_deplibs="-l$name -L$dir $finalize_deplibs" fi - elif test "$build_libtool_libs" = yes; then + elif test yes = "$build_libtool_libs"; then # Not a shared library - if test "$deplibs_check_method" != pass_all; then + if test pass_all != "$deplibs_check_method"; then # We're trying link a shared library against a static one # but the system doesn't support it. # Just print a warning and add the library to dependency_libs so # that the program can be linked against the static library. echo - $ECHO "*** Warning: This system can not link to static lib archive $lib." + $ECHO "*** Warning: This system cannot link to static lib archive $lib." echo "*** I have the capability to make that library automatically link in when" echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have." - if test "$module" = yes; then + if test yes = "$module"; then echo "*** But as you try to build a module library, libtool will still create " echo "*** a static module, that should work as long as the dlopening application" echo "*** is linked with the -dlopen flag to resolve symbols at runtime." if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" - echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." - echo "*** \`nm' from GNU binutils and a full rebuild may help." + echo "*** 'nm' from GNU binutils and a full rebuild may help." fi - if test "$build_old_libs" = no; then + if test no = "$build_old_libs"; then build_libtool_libs=module build_old_libs=yes else @@ -6976,11 +8531,11 @@ func_mode_link () fi fi # link shared/static library? - if test "$linkmode" = lib; then + if test lib = "$linkmode"; then if test -n "$dependency_libs" && - { test "$hardcode_into_libs" != yes || - test "$build_old_libs" = yes || - test "$link_static" = yes; }; then + { test yes != "$hardcode_into_libs" || + test yes = "$build_old_libs" || + test yes = "$link_static"; }; then # Extract -R from dependency_libs temp_deplibs= for libdir in $dependency_libs; do @@ -6994,12 +8549,12 @@ func_mode_link () *) func_append temp_deplibs " $libdir";; esac done - dependency_libs="$temp_deplibs" + dependency_libs=$temp_deplibs fi func_append newlib_search_path " $absdir" # Link against this library - test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + test no = "$link_static" && newdependency_libs="$abs_ladir/$laname $newdependency_libs" # ... and its dependency_libs tmp_libs= for deplib in $dependency_libs; do @@ -7009,7 +8564,7 @@ func_mode_link () func_resolve_sysroot "$func_stripname_result";; *) func_resolve_sysroot "$deplib" ;; esac - if $opt_preserve_dup_deps ; then + if $opt_preserve_dup_deps; then case "$tmp_libs " in *" $func_resolve_sysroot_result "*) func_append specialdeplibs " $func_resolve_sysroot_result" ;; @@ -7018,12 +8573,12 @@ func_mode_link () func_append tmp_libs " $func_resolve_sysroot_result" done - if test "$link_all_deplibs" != no; then + if test no != "$link_all_deplibs"; then # Add the search paths of all dependency libraries for deplib in $dependency_libs; do path= case $deplib in - -L*) path="$deplib" ;; + -L*) path=$deplib ;; *.la) func_resolve_sysroot "$deplib" deplib=$func_resolve_sysroot_result @@ -7031,12 +8586,12 @@ func_mode_link () dir=$func_dirname_result # We need an absolute path. case $dir in - [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + [\\/]* | [A-Za-z]:[\\/]*) absdir=$dir ;; *) absdir=`cd "$dir" && pwd` if test -z "$absdir"; then - func_warning "cannot determine absolute directory name of \`$dir'" - absdir="$dir" + func_warning "cannot determine absolute directory name of '$dir'" + absdir=$dir fi ;; esac @@ -7044,35 +8599,35 @@ func_mode_link () case $host in *-*-darwin*) depdepl= - eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` - if test -n "$deplibrary_names" ; then - for tmp in $deplibrary_names ; do + eval deplibrary_names=`$SED -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names"; then + for tmp in $deplibrary_names; do depdepl=$tmp done - if test -f "$absdir/$objdir/$depdepl" ; then - depdepl="$absdir/$objdir/$depdepl" - darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -f "$absdir/$objdir/$depdepl"; then + depdepl=$absdir/$objdir/$depdepl + darwin_install_name=`$OTOOL -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` if test -z "$darwin_install_name"; then - darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + darwin_install_name=`$OTOOL64 -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` fi - func_append compiler_flags " ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" - func_append linker_flags " -dylib_file ${darwin_install_name}:${depdepl}" + func_append compiler_flags " $wl-dylib_file $wl$darwin_install_name:$depdepl" + func_append linker_flags " -dylib_file $darwin_install_name:$depdepl" path= fi fi ;; *) - path="-L$absdir/$objdir" + path=-L$absdir/$objdir ;; esac else - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` test -z "$libdir" && \ - func_fatal_error "\`$deplib' is not a valid libtool archive" + func_fatal_error "'$deplib' is not a valid libtool archive" test "$absdir" != "$libdir" && \ - func_warning "\`$deplib' seems to be moved" + func_warning "'$deplib' seems to be moved" - path="-L$absdir" + path=-L$absdir fi ;; esac @@ -7084,23 +8639,23 @@ func_mode_link () fi # link_all_deplibs != no fi # linkmode = lib done # for deplib in $libs - if test "$pass" = link; then - if test "$linkmode" = "prog"; then + if test link = "$pass"; then + if test prog = "$linkmode"; then compile_deplibs="$new_inherited_linker_flags $compile_deplibs" finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" else compiler_flags="$compiler_flags "`$ECHO " $new_inherited_linker_flags" | $SED 's% \([^ $]*\).ltframework% -framework \1%g'` fi fi - dependency_libs="$newdependency_libs" - if test "$pass" = dlpreopen; then + dependency_libs=$newdependency_libs + if test dlpreopen = "$pass"; then # Link the dlpreopened libraries before other libraries for deplib in $save_deplibs; do deplibs="$deplib $deplibs" done fi - if test "$pass" != dlopen; then - if test "$pass" != conv; then + if test dlopen != "$pass"; then + test conv = "$pass" || { # Make sure lib_search_path contains only unique directories. lib_search_path= for dir in $newlib_search_path; do @@ -7110,12 +8665,12 @@ func_mode_link () esac done newlib_search_path= - fi + } - if test "$linkmode,$pass" != "prog,link"; then - vars="deplibs" - else + if test prog,link = "$linkmode,$pass"; then vars="compile_deplibs finalize_deplibs" + else + vars=deplibs fi for var in $vars dependency_libs; do # Add libraries to $var in reverse order @@ -7173,62 +8728,93 @@ func_mode_link () eval $var=\"$tmp_libs\" done # for var fi + + # Add Sun CC postdeps if required: + test CXX = "$tagname" && { + case $host_os in + linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C++ 5.9 + func_suncc_cstd_abi + + if test no != "$suncc_use_cstd_abi"; then + func_append postdeps ' -library=Cstd -library=Crun' + fi + ;; + esac + ;; + + solaris*) + func_cc_basename "$CC" + case $func_cc_basename_result in + CC* | sunCC*) + func_suncc_cstd_abi + + if test no != "$suncc_use_cstd_abi"; then + func_append postdeps ' -library=Cstd -library=Crun' + fi + ;; + esac + ;; + esac + } + # Last step: remove runtime libs from dependency_libs # (they stay in deplibs) tmp_libs= - for i in $dependency_libs ; do + for i in $dependency_libs; do case " $predeps $postdeps $compiler_lib_search_path " in *" $i "*) - i="" + i= ;; esac - if test -n "$i" ; then + if test -n "$i"; then func_append tmp_libs " $i" fi done dependency_libs=$tmp_libs done # for pass - if test "$linkmode" = prog; then - dlfiles="$newdlfiles" + if test prog = "$linkmode"; then + dlfiles=$newdlfiles fi - if test "$linkmode" = prog || test "$linkmode" = lib; then - dlprefiles="$newdlprefiles" + if test prog = "$linkmode" || test lib = "$linkmode"; then + dlprefiles=$newdlprefiles fi case $linkmode in oldlib) - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - func_warning "\`-dlopen' is ignored for archives" + if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then + func_warning "'-dlopen' is ignored for archives" fi case " $deplibs" in *\ -l* | *\ -L*) - func_warning "\`-l' and \`-L' are ignored for archives" ;; + func_warning "'-l' and '-L' are ignored for archives" ;; esac test -n "$rpath" && \ - func_warning "\`-rpath' is ignored for archives" + func_warning "'-rpath' is ignored for archives" test -n "$xrpath" && \ - func_warning "\`-R' is ignored for archives" + func_warning "'-R' is ignored for archives" test -n "$vinfo" && \ - func_warning "\`-version-info/-version-number' is ignored for archives" + func_warning "'-version-info/-version-number' is ignored for archives" test -n "$release" && \ - func_warning "\`-release' is ignored for archives" + func_warning "'-release' is ignored for archives" test -n "$export_symbols$export_symbols_regex" && \ - func_warning "\`-export-symbols' is ignored for archives" + func_warning "'-export-symbols' is ignored for archives" # Now set the variables for building old libraries. build_libtool_libs=no - oldlibs="$output" + oldlibs=$output func_append objs "$old_deplibs" ;; lib) - # Make sure we only generate libraries of the form `libNAME.la'. + # Make sure we only generate libraries of the form 'libNAME.la'. case $outputname in lib*) func_stripname 'lib' '.la' "$outputname" @@ -7237,10 +8823,10 @@ func_mode_link () eval libname=\"$libname_spec\" ;; *) - test "$module" = no && \ - func_fatal_help "libtool library \`$output' must begin with \`lib'" + test no = "$module" \ + && func_fatal_help "libtool library '$output' must begin with 'lib'" - if test "$need_lib_prefix" != no; then + if test no != "$need_lib_prefix"; then # Add the "lib" prefix for modules if required func_stripname '' '.la' "$outputname" name=$func_stripname_result @@ -7254,8 +8840,8 @@ func_mode_link () esac if test -n "$objs"; then - if test "$deplibs_check_method" != pass_all; then - func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" + if test pass_all != "$deplibs_check_method"; then + func_fatal_error "cannot build libtool library '$output' from non-libtool objects on this host:$objs" else echo $ECHO "*** Warning: Linking the shared library $output against the non-libtool" @@ -7264,21 +8850,21 @@ func_mode_link () fi fi - test "$dlself" != no && \ - func_warning "\`-dlopen self' is ignored for libtool libraries" + test no = "$dlself" \ + || func_warning "'-dlopen self' is ignored for libtool libraries" set dummy $rpath shift - test "$#" -gt 1 && \ - func_warning "ignoring multiple \`-rpath's for a libtool library" + test 1 -lt "$#" \ + && func_warning "ignoring multiple '-rpath's for a libtool library" - install_libdir="$1" + install_libdir=$1 oldlibs= if test -z "$rpath"; then - if test "$build_libtool_libs" = yes; then + if test yes = "$build_libtool_libs"; then # Building a libtool convenience library. - # Some compilers have problems with a `.al' extension so + # Some compilers have problems with a '.al' extension so # convenience libraries should have the same extension an # archive normally would. oldlibs="$output_objdir/$libname.$libext $oldlibs" @@ -7287,20 +8873,20 @@ func_mode_link () fi test -n "$vinfo" && \ - func_warning "\`-version-info/-version-number' is ignored for convenience libraries" + func_warning "'-version-info/-version-number' is ignored for convenience libraries" test -n "$release" && \ - func_warning "\`-release' is ignored for convenience libraries" + func_warning "'-release' is ignored for convenience libraries" else # Parse the version information argument. - save_ifs="$IFS"; IFS=':' + save_ifs=$IFS; IFS=: set dummy $vinfo 0 0 0 shift - IFS="$save_ifs" + IFS=$save_ifs test -n "$7" && \ - func_fatal_help "too many parameters to \`-version-info'" + func_fatal_help "too many parameters to '-version-info'" # convert absolute version numbers to libtool ages # this retains compatibility with .la files and attempts @@ -7308,44 +8894,45 @@ func_mode_link () case $vinfo_number in yes) - number_major="$1" - number_minor="$2" - number_revision="$3" + number_major=$1 + number_minor=$2 + number_revision=$3 # # There are really only two kinds -- those that # use the current revision as the major version # and those that subtract age and use age as # a minor version. But, then there is irix - # which has an extra 1 added just for fun + # that has an extra 1 added just for fun # case $version_type in - darwin|linux|osf|windows|none) + # correct linux to gnu/linux during the next big refactor + darwin|freebsd-elf|linux|osf|windows|none) func_arith $number_major + $number_minor current=$func_arith_result - age="$number_minor" - revision="$number_revision" + age=$number_minor + revision=$number_revision ;; - freebsd-aout|freebsd-elf|qnx|sunos) - current="$number_major" - revision="$number_minor" - age="0" + freebsd-aout|qnx|sunos) + current=$number_major + revision=$number_minor + age=0 ;; irix|nonstopux) func_arith $number_major + $number_minor current=$func_arith_result - age="$number_minor" - revision="$number_minor" + age=$number_minor + revision=$number_minor lt_irix_increment=no ;; *) - func_fatal_configuration "$modename: unknown library version type \`$version_type'" + func_fatal_configuration "$modename: unknown library version type '$version_type'" ;; esac ;; no) - current="$1" - revision="$2" - age="$3" + current=$1 + revision=$2 + age=$3 ;; esac @@ -7353,30 +8940,30 @@ func_mode_link () case $current in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) - func_error "CURRENT \`$current' must be a nonnegative integer" - func_fatal_error "\`$vinfo' is not valid version information" + func_error "CURRENT '$current' must be a nonnegative integer" + func_fatal_error "'$vinfo' is not valid version information" ;; esac case $revision in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) - func_error "REVISION \`$revision' must be a nonnegative integer" - func_fatal_error "\`$vinfo' is not valid version information" + func_error "REVISION '$revision' must be a nonnegative integer" + func_fatal_error "'$vinfo' is not valid version information" ;; esac case $age in 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; *) - func_error "AGE \`$age' must be a nonnegative integer" - func_fatal_error "\`$vinfo' is not valid version information" + func_error "AGE '$age' must be a nonnegative integer" + func_fatal_error "'$vinfo' is not valid version information" ;; esac if test "$age" -gt "$current"; then - func_error "AGE \`$age' is greater than the current interface number \`$current'" - func_fatal_error "\`$vinfo' is not valid version information" + func_error "AGE '$age' is greater than the current interface number '$current'" + func_fatal_error "'$vinfo' is not valid version information" fi # Calculate the version variables. @@ -7391,26 +8978,36 @@ func_mode_link () # verstring for coding it into the library header func_arith $current - $age major=.$func_arith_result - versuffix="$major.$age.$revision" + versuffix=$major.$age.$revision # Darwin ld doesn't like 0 for these options... func_arith $current + 1 minor_current=$func_arith_result - xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + xlcverstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + # On Darwin other compilers + case $CC in + nagfor*) + verstring="$wl-compatibility_version $wl$minor_current $wl-current_version $wl$minor_current.$revision" + ;; + *) + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + esac ;; freebsd-aout) - major=".$current" - versuffix=".$current.$revision"; + major=.$current + versuffix=.$current.$revision ;; freebsd-elf) - major=".$current" - versuffix=".$current" + func_arith $current - $age + major=.$func_arith_result + versuffix=$major.$age.$revision ;; irix | nonstopux) - if test "X$lt_irix_increment" = "Xno"; then + if test no = "$lt_irix_increment"; then func_arith $current - $age else func_arith $current - $age + 1 @@ -7421,69 +9018,74 @@ func_mode_link () nonstopux) verstring_prefix=nonstopux ;; *) verstring_prefix=sgi ;; esac - verstring="$verstring_prefix$major.$revision" + verstring=$verstring_prefix$major.$revision # Add in all the interfaces that we are compatible with. loop=$revision - while test "$loop" -ne 0; do + while test 0 -ne "$loop"; do func_arith $revision - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result - verstring="$verstring_prefix$major.$iface:$verstring" + verstring=$verstring_prefix$major.$iface:$verstring done - # Before this point, $major must not contain `.'. + # Before this point, $major must not contain '.'. major=.$major - versuffix="$major.$revision" + versuffix=$major.$revision ;; - linux) + linux) # correct to gnu/linux during the next big refactor func_arith $current - $age major=.$func_arith_result - versuffix="$major.$age.$revision" + versuffix=$major.$age.$revision ;; osf) func_arith $current - $age major=.$func_arith_result - versuffix=".$current.$age.$revision" - verstring="$current.$age.$revision" + versuffix=.$current.$age.$revision + verstring=$current.$age.$revision # Add in all the interfaces that we are compatible with. loop=$age - while test "$loop" -ne 0; do + while test 0 -ne "$loop"; do func_arith $current - $loop iface=$func_arith_result func_arith $loop - 1 loop=$func_arith_result - verstring="$verstring:${iface}.0" + verstring=$verstring:$iface.0 done # Make executables depend on our current version. - func_append verstring ":${current}.0" + func_append verstring ":$current.0" ;; qnx) - major=".$current" - versuffix=".$current" + major=.$current + versuffix=.$current + ;; + + sco) + major=.$current + versuffix=.$current ;; sunos) - major=".$current" - versuffix=".$current.$revision" + major=.$current + versuffix=.$current.$revision ;; windows) # Use '-' rather than '.', since we only want one - # extension on DOS 8.3 filesystems. + # extension on DOS 8.3 file systems. func_arith $current - $age major=$func_arith_result - versuffix="-$major" + versuffix=-$major ;; *) - func_fatal_configuration "unknown library version type \`$version_type'" + func_fatal_configuration "unknown library version type '$version_type'" ;; esac @@ -7497,42 +9099,45 @@ func_mode_link () verstring= ;; *) - verstring="0.0" + verstring=0.0 ;; esac - if test "$need_version" = no; then + if test no = "$need_version"; then versuffix= else - versuffix=".0.0" + versuffix=.0.0 fi fi # Remove version info from name if versioning should be avoided - if test "$avoid_version" = yes && test "$need_version" = no; then + if test yes,no = "$avoid_version,$need_version"; then major= versuffix= - verstring="" + verstring= fi # Check to see if the archive will have undefined symbols. - if test "$allow_undefined" = yes; then - if test "$allow_undefined_flag" = unsupported; then - func_warning "undefined symbols not allowed in $host shared libraries" - build_libtool_libs=no - build_old_libs=yes + if test yes = "$allow_undefined"; then + if test unsupported = "$allow_undefined_flag"; then + if test yes = "$build_old_libs"; then + func_warning "undefined symbols not allowed in $host shared libraries; building static only" + build_libtool_libs=no + else + func_fatal_error "can't build $host shared library unless -no-undefined is specified" + fi fi else # Don't allow undefined symbols. - allow_undefined_flag="$no_undefined_flag" + allow_undefined_flag=$no_undefined_flag fi fi - func_generate_dlsyms "$libname" "$libname" "yes" + func_generate_dlsyms "$libname" "$libname" : func_append libobjs " $symfileobj" - test "X$libobjs" = "X " && libobjs= + test " " = "$libobjs" && libobjs= - if test "$opt_mode" != relink; then + if test relink != "$opt_mode"; then # Remove our outputs, but don't remove object files since they # may have been created when compiling PIC objects. removelist= @@ -7541,8 +9146,8 @@ func_mode_link () case $p in *.$objext | *.gcno) ;; - $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) - if test "X$precious_files_regex" != "X"; then + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/$libname$release.*) + if test -n "$precious_files_regex"; then if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 then continue @@ -7558,11 +9163,11 @@ func_mode_link () fi # Now set the variables for building old libraries. - if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + if test yes = "$build_old_libs" && test convenience != "$build_libtool_libs"; then func_append oldlibs " $output_objdir/$libname.$libext" # Transform .lo files to .o files. - oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; $lo2o" | $NL2SP` + oldobjs="$objs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; $lo2o" | $NL2SP` fi # Eliminate all temporary directories. @@ -7583,13 +9188,13 @@ func_mode_link () *) func_append finalize_rpath " $libdir" ;; esac done - if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + if test yes != "$hardcode_into_libs" || test yes = "$build_old_libs"; then dependency_libs="$temp_xrpath $dependency_libs" fi fi # Make sure dlfiles contains only unique files that won't be dlpreopened - old_dlfiles="$dlfiles" + old_dlfiles=$dlfiles dlfiles= for lib in $old_dlfiles; do case " $dlprefiles $dlfiles " in @@ -7599,7 +9204,7 @@ func_mode_link () done # Make sure dlprefiles contains only unique files - old_dlprefiles="$dlprefiles" + old_dlprefiles=$dlprefiles dlprefiles= for lib in $old_dlprefiles; do case "$dlprefiles " in @@ -7608,7 +9213,7 @@ func_mode_link () esac done - if test "$build_libtool_libs" = yes; then + if test yes = "$build_libtool_libs"; then if test -n "$rpath"; then case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc* | *-*-haiku*) @@ -7632,7 +9237,7 @@ func_mode_link () ;; *) # Add libc to deplibs on all other systems if necessary. - if test "$build_libtool_need_lc" = "yes"; then + if test yes = "$build_libtool_need_lc"; then func_append deplibs " -lc" fi ;; @@ -7648,9 +9253,9 @@ func_mode_link () # I'm not sure if I'm treating the release correctly. I think # release should show up in the -l (ie -lgmp5) so we don't want to # add it in twice. Is that correct? - release="" - versuffix="" - major="" + release= + versuffix= + major= newdeplibs= droppeddeps=no case $deplibs_check_method in @@ -7679,20 +9284,20 @@ EOF -l*) func_stripname -l '' "$i" name=$func_stripname_result - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $i "*) func_append newdeplibs " $i" - i="" + i= ;; esac fi - if test -n "$i" ; then + if test -n "$i"; then libname=`eval "\\$ECHO \"$libname_spec\""` deplib_matches=`eval "\\$ECHO \"$library_names_spec\""` set dummy $deplib_matches; shift deplib_match=$1 - if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then + if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then func_append newdeplibs " $i" else droppeddeps=yes @@ -7722,20 +9327,20 @@ EOF $opt_dry_run || $RM conftest if $LTCC $LTCFLAGS -o conftest conftest.c $i; then ldd_output=`ldd conftest` - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $i "*) func_append newdeplibs " $i" - i="" + i= ;; esac fi - if test -n "$i" ; then + if test -n "$i"; then libname=`eval "\\$ECHO \"$libname_spec\""` deplib_matches=`eval "\\$ECHO \"$library_names_spec\""` set dummy $deplib_matches; shift deplib_match=$1 - if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then + if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0; then func_append newdeplibs " $i" else droppeddeps=yes @@ -7772,24 +9377,24 @@ EOF -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" - a_deplib="" + a_deplib= ;; esac fi - if test -n "$a_deplib" ; then + if test -n "$a_deplib"; then libname=`eval "\\$ECHO \"$libname_spec\""` if test -n "$file_magic_glob"; then libnameglob=`func_echo_all "$libname" | $SED -e $file_magic_glob` else libnameglob=$libname fi - test "$want_nocaseglob" = yes && nocaseglob=`shopt -p nocaseglob` + test yes = "$want_nocaseglob" && nocaseglob=`shopt -p nocaseglob` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do - if test "$want_nocaseglob" = yes; then + if test yes = "$want_nocaseglob"; then shopt -s nocaseglob potential_libs=`ls $i/$libnameglob[.-]* 2>/dev/null` $nocaseglob @@ -7807,25 +9412,25 @@ EOF # We might still enter an endless loop, since a link # loop can be closed while we follow links, # but so what? - potlib="$potent_lib" + potlib=$potent_lib while test -h "$potlib" 2>/dev/null; do - potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + potliblink=`ls -ld $potlib | $SED 's/.* -> //'` case $potliblink in - [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; - *) potlib=`$ECHO "$potlib" | $SED 's,[^/]*$,,'`"$potliblink";; + [\\/]* | [A-Za-z]:[\\/]*) potlib=$potliblink;; + *) potlib=`$ECHO "$potlib" | $SED 's|[^/]*$||'`"$potliblink";; esac done if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | $SED -e 10q | $EGREP "$file_magic_regex" > /dev/null; then func_append newdeplibs " $a_deplib" - a_deplib="" + a_deplib= break 2 fi done done fi - if test -n "$a_deplib" ; then + if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." @@ -7833,7 +9438,7 @@ EOF echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" - if test -z "$potlib" ; then + if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for file magic test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" @@ -7856,30 +9461,30 @@ EOF -l*) func_stripname -l '' "$a_deplib" name=$func_stripname_result - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + if test yes = "$allow_libtool_libs_with_static_runtimes"; then case " $predeps $postdeps " in *" $a_deplib "*) func_append newdeplibs " $a_deplib" - a_deplib="" + a_deplib= ;; esac fi - if test -n "$a_deplib" ; then + if test -n "$a_deplib"; then libname=`eval "\\$ECHO \"$libname_spec\""` for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do potential_libs=`ls $i/$libname[.-]* 2>/dev/null` for potent_lib in $potential_libs; do - potlib="$potent_lib" # see symlink-check above in file_magic test + potlib=$potent_lib # see symlink-check above in file_magic test if eval "\$ECHO \"$potent_lib\"" 2>/dev/null | $SED 10q | \ $EGREP "$match_pattern_regex" > /dev/null; then func_append newdeplibs " $a_deplib" - a_deplib="" + a_deplib= break 2 fi done done fi - if test -n "$a_deplib" ; then + if test -n "$a_deplib"; then droppeddeps=yes echo $ECHO "*** Warning: linker path does not have real file for library $a_deplib." @@ -7887,7 +9492,7 @@ EOF echo "*** you link to this library. But I can only do this if you have a" echo "*** shared version of the library, which you do not appear to have" echo "*** because I did check the linker path looking for a file starting" - if test -z "$potlib" ; then + if test -z "$potlib"; then $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" else $ECHO "*** with $libname and none of the candidates passed a file format test" @@ -7903,18 +9508,18 @@ EOF done # Gone through all deplibs. ;; none | unknown | *) - newdeplibs="" + newdeplibs= tmp_deplibs=`$ECHO " $deplibs" | $SED 's/ -lc$//; s/ -[LR][^ ]*//g'` - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - for i in $predeps $postdeps ; do + if test yes = "$allow_libtool_libs_with_static_runtimes"; then + for i in $predeps $postdeps; do # can't use Xsed below, because $i might contain '/' - tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s,$i,,"` + tmp_deplibs=`$ECHO " $tmp_deplibs" | $SED "s|$i||"` done fi case $tmp_deplibs in *[!\ \ ]*) echo - if test "X$deplibs_check_method" = "Xnone"; then + if test none = "$deplibs_check_method"; then echo "*** Warning: inter-library dependencies are not supported in this platform." else echo "*** Warning: inter-library dependencies are not known to be supported." @@ -7938,8 +9543,8 @@ EOF ;; esac - if test "$droppeddeps" = yes; then - if test "$module" = yes; then + if test yes = "$droppeddeps"; then + if test yes = "$module"; then echo echo "*** Warning: libtool could not satisfy all declared inter-library" $ECHO "*** dependencies of module $libname. Therefore, libtool will create" @@ -7948,12 +9553,12 @@ EOF if test -z "$global_symbol_pipe"; then echo echo "*** However, this would only work if libtool was able to extract symbol" - echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + echo "*** lists from a program, using 'nm' or equivalent, but libtool could" echo "*** not find such a program. So, this module is probably useless." - echo "*** \`nm' from GNU binutils and a full rebuild may help." + echo "*** 'nm' from GNU binutils and a full rebuild may help." fi - if test "$build_old_libs" = no; then - oldlibs="$output_objdir/$libname.$libext" + if test no = "$build_old_libs"; then + oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else @@ -7964,14 +9569,14 @@ EOF echo "*** automatically added whenever a program is linked with this library" echo "*** or is declared to -dlopen it." - if test "$allow_undefined" = no; then + if test no = "$allow_undefined"; then echo echo "*** Since this library must not contain undefined symbols," echo "*** because either the platform does not support them or" echo "*** it was explicitly requested with -no-undefined," echo "*** libtool will only create a static version of it." - if test "$build_old_libs" = no; then - oldlibs="$output_objdir/$libname.$libext" + if test no = "$build_old_libs"; then + oldlibs=$output_objdir/$libname.$libext build_libtool_libs=module build_old_libs=yes else @@ -8017,7 +9622,7 @@ EOF *) func_append new_libs " $deplib" ;; esac done - deplibs="$new_libs" + deplibs=$new_libs # All the library-specific variables (install_libdir is set above). library_names= @@ -8025,20 +9630,25 @@ EOF dlname= # Test again, we may have decided not to build it any more - if test "$build_libtool_libs" = yes; then - if test "$hardcode_into_libs" = yes; then + if test yes = "$build_libtool_libs"; then + # Remove $wl instances when linking with ld. + # FIXME: should test the right _cmds variable. + case $archive_cmds in + *\$LD\ *) wl= ;; + esac + if test yes = "$hardcode_into_libs"; then # Hardcode the library paths hardcode_libdirs= dep_rpath= - rpath="$finalize_rpath" - test "$opt_mode" != relink && rpath="$compile_rpath$rpath" + rpath=$finalize_rpath + test relink = "$opt_mode" || rpath=$compile_rpath$rpath for libdir in $rpath; do if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then func_replace_sysroot "$libdir" libdir=$func_replace_sysroot_result if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" + hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in @@ -8056,19 +9666,15 @@ EOF elif test -n "$runpath_var"; then case "$perm_rpath " in *" $libdir "*) ;; - *) func_apped perm_rpath " $libdir" ;; + *) func_append perm_rpath " $libdir" ;; esac fi done # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - if test -n "$hardcode_libdir_flag_spec_ld"; then - eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" - else - eval dep_rpath=\"$hardcode_libdir_flag_spec\" - fi + libdir=$hardcode_libdirs + eval "dep_rpath=\"$hardcode_libdir_flag_spec\"" fi if test -n "$runpath_var" && test -n "$perm_rpath"; then # We should set the runpath_var. @@ -8081,8 +9687,8 @@ EOF test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" fi - shlibpath="$finalize_shlibpath" - test "$opt_mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + shlibpath=$finalize_shlibpath + test relink = "$opt_mode" || shlibpath=$compile_shlibpath$shlibpath if test -n "$shlibpath"; then eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" fi @@ -8092,19 +9698,19 @@ EOF eval library_names=\"$library_names_spec\" set dummy $library_names shift - realname="$1" + realname=$1 shift if test -n "$soname_spec"; then eval soname=\"$soname_spec\" else - soname="$realname" + soname=$realname fi if test -z "$dlname"; then dlname=$soname fi - lib="$output_objdir/$realname" + lib=$output_objdir/$realname linknames= for link do @@ -8118,7 +9724,7 @@ EOF delfiles= if test -n "$export_symbols" && test -n "$include_expsyms"; then $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" - export_symbols="$output_objdir/$libname.uexp" + export_symbols=$output_objdir/$libname.uexp func_append delfiles " $export_symbols" fi @@ -8127,31 +9733,31 @@ EOF cygwin* | mingw* | cegcc*) if test -n "$export_symbols" && test -z "$export_symbols_regex"; then # exporting using user supplied symfile - if test "x`$SED 1q $export_symbols`" != xEXPORTS; then + func_dll_def_p "$export_symbols" || { # and it's NOT already a .def file. Must figure out # which of the given symbols are data symbols and tag # them as such. So, trigger use of export_symbols_cmds. # export_symbols gets reassigned inside the "prepare # the list of exported symbols" if statement, so the # include_expsyms logic still works. - orig_export_symbols="$export_symbols" + orig_export_symbols=$export_symbols export_symbols= always_export_symbols=yes - fi + } fi ;; esac # Prepare the list of exported symbols if test -z "$export_symbols"; then - if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then - func_verbose "generating symbol list for \`$libname.la'" - export_symbols="$output_objdir/$libname.exp" + if test yes = "$always_export_symbols" || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for '$libname.la'" + export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols cmds=$export_symbols_cmds - save_ifs="$IFS"; IFS='~' + save_ifs=$IFS; IFS='~' for cmd1 in $cmds; do - IFS="$save_ifs" + IFS=$save_ifs # Take the normal branch if the nm_file_list_spec branch # doesn't work or if tool conversion is not needed. case $nm_file_list_spec~$to_tool_file_cmd in @@ -8165,7 +9771,7 @@ EOF try_normal_branch=no ;; esac - if test "$try_normal_branch" = yes \ + if test yes = "$try_normal_branch" \ && { test "$len" -lt "$max_cmd_len" \ || test "$max_cmd_len" -le -1; } then @@ -8176,7 +9782,7 @@ EOF output_la=$func_basename_result save_libobjs=$libobjs save_output=$output - output=${output_objdir}/${output_la}.nm + output=$output_objdir/$output_la.nm func_to_tool_file "$output" libobjs=$nm_file_list_spec$func_to_tool_file_result func_append delfiles " $output" @@ -8199,8 +9805,8 @@ EOF break fi done - IFS="$save_ifs" - if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then + IFS=$save_ifs + if test -n "$export_symbols_regex" && test : != "$skipped_export"; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi @@ -8208,16 +9814,16 @@ EOF fi if test -n "$export_symbols" && test -n "$include_expsyms"; then - tmp_export_symbols="$export_symbols" - test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + tmp_export_symbols=$export_symbols + test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi - if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then + if test : != "$skipped_export" && test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. - func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of - # 's' commands which not all seds can handle. GNU sed should be fine + # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. @@ -8236,11 +9842,11 @@ EOF ;; esac done - deplibs="$tmp_deplibs" + deplibs=$tmp_deplibs if test -n "$convenience"; then if test -n "$whole_archive_flag_spec" && - test "$compiler_needs_object" = yes && + test yes = "$compiler_needs_object" && test -z "$libobjs"; then # extract the archives, so we have objects to list. # TODO: could optimize this to just extract one archive. @@ -8251,7 +9857,7 @@ EOF eval libobjs=\"\$libobjs $whole_archive_flag_spec\" test "X$libobjs" = "X " && libobjs= else - gentop="$output_objdir/${outputname}x" + gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $convenience @@ -8260,18 +9866,18 @@ EOF fi fi - if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + if test yes = "$thread_safe" && test -n "$thread_safe_flag_spec"; then eval flag=\"$thread_safe_flag_spec\" func_append linker_flags " $flag" fi # Make a backup of the uninstalled library when relinking - if test "$opt_mode" = relink; then + if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi # Do each of the archive commands. - if test "$module" = yes && test -n "$module_cmds" ; then + if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then eval test_cmds=\"$module_expsym_cmds\" cmds=$module_expsym_cmds @@ -8289,7 +9895,7 @@ EOF fi fi - if test "X$skipped_export" != "X:" && + if test : != "$skipped_export" && func_len " $test_cmds" && len=$func_len_result && test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then @@ -8322,8 +9928,8 @@ EOF last_robj= k=1 - if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then - output=${output_objdir}/${output_la}.lnkscript + if test -n "$save_libobjs" && test : != "$skipped_export" && test yes = "$with_gnu_ld"; then + output=$output_objdir/$output_la.lnkscript func_verbose "creating GNU ld script: $output" echo 'INPUT (' > $output for obj in $save_libobjs @@ -8335,14 +9941,14 @@ EOF func_append delfiles " $output" func_to_tool_file "$output" output=$func_to_tool_file_result - elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then - output=${output_objdir}/${output_la}.lnk + elif test -n "$save_libobjs" && test : != "$skipped_export" && test -n "$file_list_spec"; then + output=$output_objdir/$output_la.lnk func_verbose "creating linker input file list: $output" : > $output set x $save_libobjs shift firstobj= - if test "$compiler_needs_object" = yes; then + if test yes = "$compiler_needs_object"; then firstobj="$1 " shift fi @@ -8357,7 +9963,7 @@ EOF else if test -n "$save_libobjs"; then func_verbose "creating reloadable object files..." - output=$output_objdir/$output_la-${k}.$objext + output=$output_objdir/$output_la-$k.$objext eval test_cmds=\"$reload_cmds\" func_len " $test_cmds" len0=$func_len_result @@ -8369,13 +9975,13 @@ EOF func_len " $obj" func_arith $len + $func_len_result len=$func_arith_result - if test "X$objlist" = X || + if test -z "$objlist" || test "$len" -lt "$max_cmd_len"; then func_append objlist " $obj" else # The command $test_cmds is almost too long, add a # command to the queue. - if test "$k" -eq 1 ; then + if test 1 -eq "$k"; then # The first file doesn't have a previous command to add. reload_objs=$objlist eval concat_cmds=\"$reload_cmds\" @@ -8385,10 +9991,10 @@ EOF reload_objs="$objlist $last_robj" eval concat_cmds=\"\$concat_cmds~$reload_cmds~\$RM $last_robj\" fi - last_robj=$output_objdir/$output_la-${k}.$objext + last_robj=$output_objdir/$output_la-$k.$objext func_arith $k + 1 k=$func_arith_result - output=$output_objdir/$output_la-${k}.$objext + output=$output_objdir/$output_la-$k.$objext objlist=" $obj" func_len " $last_robj" func_arith $len0 + $func_len_result @@ -8400,9 +10006,9 @@ EOF # files will link in the last one created. test -z "$concat_cmds" || concat_cmds=$concat_cmds~ reload_objs="$objlist $last_robj" - eval concat_cmds=\"\${concat_cmds}$reload_cmds\" + eval concat_cmds=\"\$concat_cmds$reload_cmds\" if test -n "$last_robj"; then - eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi func_append delfiles " $output" @@ -8410,9 +10016,9 @@ EOF output= fi - if ${skipped_export-false}; then - func_verbose "generating symbol list for \`$libname.la'" - export_symbols="$output_objdir/$libname.exp" + ${skipped_export-false} && { + func_verbose "generating symbol list for '$libname.la'" + export_symbols=$output_objdir/$libname.exp $opt_dry_run || $RM $export_symbols libobjs=$output # Append the command to create the export file. @@ -8421,16 +10027,16 @@ EOF if test -n "$last_robj"; then eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" fi - fi + } test -n "$save_libobjs" && func_verbose "creating a temporary reloadable object file: $output" # Loop through the commands generated above and execute them. - save_ifs="$IFS"; IFS='~' + save_ifs=$IFS; IFS='~' for cmd in $concat_cmds; do - IFS="$save_ifs" - $opt_silent || { + IFS=$save_ifs + $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } @@ -8438,7 +10044,7 @@ EOF lt_exit=$? # Restore the uninstalled library and exit - if test "$opt_mode" = relink; then + if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) @@ -8447,7 +10053,7 @@ EOF exit $lt_exit } done - IFS="$save_ifs" + IFS=$save_ifs if test -n "$export_symbols_regex" && ${skipped_export-false}; then func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' @@ -8455,18 +10061,18 @@ EOF fi fi - if ${skipped_export-false}; then + ${skipped_export-false} && { if test -n "$export_symbols" && test -n "$include_expsyms"; then - tmp_export_symbols="$export_symbols" - test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + tmp_export_symbols=$export_symbols + test -n "$orig_export_symbols" && tmp_export_symbols=$orig_export_symbols $opt_dry_run || eval '$ECHO "$include_expsyms" | $SP2NL >> "$tmp_export_symbols"' fi if test -n "$orig_export_symbols"; then # The given exports_symbols file has to be filtered, so filter it. - func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + func_verbose "filter symbol list for '$libname.la' to tag DATA exports" # FIXME: $output_objdir/$libname.filter potentially contains lots of - # 's' commands which not all seds can handle. GNU sed should be fine + # 's' commands, which not all seds can handle. GNU sed should be fine # though. Also, the filter scales superlinearly with the number of # global variables. join(1) would be nice here, but unfortunately # isn't a blessed tool. @@ -8475,7 +10081,7 @@ EOF export_symbols=$output_objdir/$libname.def $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi - fi + } libobjs=$output # Restore the value of output. @@ -8489,7 +10095,7 @@ EOF # value of $libobjs for piecewise linking. # Do each of the archive commands. - if test "$module" = yes && test -n "$module_cmds" ; then + if test yes = "$module" && test -n "$module_cmds"; then if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then cmds=$module_expsym_cmds else @@ -8511,7 +10117,7 @@ EOF # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then - gentop="$output_objdir/${outputname}x" + gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles @@ -8519,11 +10125,12 @@ EOF test "X$libobjs" = "X " && libobjs= fi - save_ifs="$IFS"; IFS='~' + save_ifs=$IFS; IFS='~' for cmd in $cmds; do - IFS="$save_ifs" + IFS=$sp$nl eval cmd=\"$cmd\" - $opt_silent || { + IFS=$save_ifs + $opt_quiet || { func_quote_for_expand "$cmd" eval "func_echo $func_quote_for_expand_result" } @@ -8531,7 +10138,7 @@ EOF lt_exit=$? # Restore the uninstalled library and exit - if test "$opt_mode" = relink; then + if test relink = "$opt_mode"; then ( cd "$output_objdir" && \ $RM "${realname}T" && \ $MV "${realname}U" "$realname" ) @@ -8540,10 +10147,10 @@ EOF exit $lt_exit } done - IFS="$save_ifs" + IFS=$save_ifs # Restore the uninstalled library and exit - if test "$opt_mode" = relink; then + if test relink = "$opt_mode"; then $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? if test -n "$convenience"; then @@ -8563,39 +10170,39 @@ EOF done # If -module or -export-dynamic was specified, set the dlname. - if test "$module" = yes || test "$export_dynamic" = yes; then + if test yes = "$module" || test yes = "$export_dynamic"; then # On all known operating systems, these are identical. - dlname="$soname" + dlname=$soname fi fi ;; obj) - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - func_warning "\`-dlopen' is ignored for objects" + if test -n "$dlfiles$dlprefiles" || test no != "$dlself"; then + func_warning "'-dlopen' is ignored for objects" fi case " $deplibs" in *\ -l* | *\ -L*) - func_warning "\`-l' and \`-L' are ignored for objects" ;; + func_warning "'-l' and '-L' are ignored for objects" ;; esac test -n "$rpath" && \ - func_warning "\`-rpath' is ignored for objects" + func_warning "'-rpath' is ignored for objects" test -n "$xrpath" && \ - func_warning "\`-R' is ignored for objects" + func_warning "'-R' is ignored for objects" test -n "$vinfo" && \ - func_warning "\`-version-info' is ignored for objects" + func_warning "'-version-info' is ignored for objects" test -n "$release" && \ - func_warning "\`-release' is ignored for objects" + func_warning "'-release' is ignored for objects" case $output in *.lo) test -n "$objs$old_deplibs" && \ - func_fatal_error "cannot build library object \`$output' from non-libtool objects" + func_fatal_error "cannot build library object '$output' from non-libtool objects" libobj=$output func_lo2o "$libobj" @@ -8603,7 +10210,7 @@ EOF ;; *) libobj= - obj="$output" + obj=$output ;; esac @@ -8616,17 +10223,19 @@ EOF # the extraction. reload_conv_objs= gentop= - # reload_cmds runs $LD directly, so let us get rid of - # -Wl from whole_archive_flag_spec and hope we can get by with - # turning comma into space.. - wl= - + # if reload_cmds runs $LD directly, get rid of -Wl from + # whole_archive_flag_spec and hope we can get by with turning comma + # into space. + case $reload_cmds in + *\$LD[\ \$]*) wl= ;; + esac if test -n "$convenience"; then if test -n "$whole_archive_flag_spec"; then eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" - reload_conv_objs=$reload_objs\ `$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` + test -n "$wl" || tmp_whole_archive_flags=`$ECHO "$tmp_whole_archive_flags" | $SED 's|,| |g'` + reload_conv_objs=$reload_objs\ $tmp_whole_archive_flags else - gentop="$output_objdir/${obj}x" + gentop=$output_objdir/${obj}x func_append generated " $gentop" func_extract_archives $gentop $convenience @@ -8635,12 +10244,12 @@ EOF fi # If we're not building shared, we need to use non_pic_objs - test "$build_libtool_libs" != yes && libobjs="$non_pic_objects" + test yes = "$build_libtool_libs" || libobjs=$non_pic_objects # Create the old-style object. - reload_objs="$objs$old_deplibs "`$ECHO "$libobjs" | $SP2NL | $SED "/\.${libext}$/d; /\.lib$/d; $lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + reload_objs=$objs$old_deplibs' '`$ECHO "$libobjs" | $SP2NL | $SED "/\.$libext$/d; /\.lib$/d; $lo2o" | $NL2SP`' '$reload_conv_objs - output="$obj" + output=$obj func_execute_cmds "$reload_cmds" 'exit $?' # Exit if we aren't doing a library object file. @@ -8652,7 +10261,7 @@ EOF exit $EXIT_SUCCESS fi - if test "$build_libtool_libs" != yes; then + test yes = "$build_libtool_libs" || { if test -n "$gentop"; then func_show_eval '${RM}r "$gentop"' fi @@ -8662,12 +10271,12 @@ EOF # $show "echo timestamp > $libobj" # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? exit $EXIT_SUCCESS - fi + } - if test -n "$pic_flag" || test "$pic_mode" != default; then + if test -n "$pic_flag" || test default != "$pic_mode"; then # Only do commands if we really have different PIC objects. reload_objs="$libobjs $reload_conv_objs" - output="$libobj" + output=$libobj func_execute_cmds "$reload_cmds" 'exit $?' fi @@ -8684,16 +10293,14 @@ EOF output=$func_stripname_result.exe;; esac test -n "$vinfo" && \ - func_warning "\`-version-info' is ignored for programs" + func_warning "'-version-info' is ignored for programs" test -n "$release" && \ - func_warning "\`-release' is ignored for programs" + func_warning "'-release' is ignored for programs" - test "$preload" = yes \ - && test "$dlopen_support" = unknown \ - && test "$dlopen_self" = unknown \ - && test "$dlopen_self_static" = unknown && \ - func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." + $preload \ + && test unknown,unknown,unknown = "$dlopen_support,$dlopen_self,$dlopen_self_static" \ + && func_warning "'LT_INIT([dlopen])' not used. Assuming no dlopen support." case $host in *-*-rhapsody* | *-*-darwin1.[012]) @@ -8707,11 +10314,11 @@ EOF *-*-darwin*) # Don't allow lazy linking, it breaks C++ global constructors # But is supposedly fixed on 10.4 or later (yay!). - if test "$tagname" = CXX ; then + if test CXX = "$tagname"; then case ${MACOSX_DEPLOYMENT_TARGET-10.0} in 10.[0123]) - func_append compile_command " ${wl}-bind_at_load" - func_append finalize_command " ${wl}-bind_at_load" + func_append compile_command " $wl-bind_at_load" + func_append finalize_command " $wl-bind_at_load" ;; esac fi @@ -8747,7 +10354,7 @@ EOF *) func_append new_libs " $deplib" ;; esac done - compile_deplibs="$new_libs" + compile_deplibs=$new_libs func_append compile_command " $compile_deplibs" @@ -8771,7 +10378,7 @@ EOF if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" + hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in @@ -8794,7 +10401,7 @@ EOF fi case $host in *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) - testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` + testbindir=`$ECHO "$libdir" | $SED -e 's*/lib$*/bin*'` case :$dllsearchpath: in *":$libdir:"*) ;; ::) dllsearchpath=$libdir;; @@ -8811,10 +10418,10 @@ EOF # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" + libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi - compile_rpath="$rpath" + compile_rpath=$rpath rpath= hardcode_libdirs= @@ -8822,7 +10429,7 @@ EOF if test -n "$hardcode_libdir_flag_spec"; then if test -n "$hardcode_libdir_separator"; then if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" + hardcode_libdirs=$libdir else # Just accumulate the unique libdirs. case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in @@ -8847,45 +10454,43 @@ EOF # Substitute the hardcoded libdirs into the rpath. if test -n "$hardcode_libdir_separator" && test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" + libdir=$hardcode_libdirs eval rpath=\" $hardcode_libdir_flag_spec\" fi - finalize_rpath="$rpath" + finalize_rpath=$rpath - if test -n "$libobjs" && test "$build_old_libs" = yes; then + if test -n "$libobjs" && test yes = "$build_old_libs"; then # Transform all the library objects into standard objects. compile_command=`$ECHO "$compile_command" | $SP2NL | $SED "$lo2o" | $NL2SP` finalize_command=`$ECHO "$finalize_command" | $SP2NL | $SED "$lo2o" | $NL2SP` fi - func_generate_dlsyms "$outputname" "@PROGRAM@" "no" + func_generate_dlsyms "$outputname" "@PROGRAM@" false # template prelinking step if test -n "$prelink_cmds"; then func_execute_cmds "$prelink_cmds" 'exit $?' fi - wrappers_required=yes + wrappers_required=: case $host in *cegcc* | *mingw32ce*) # Disable wrappers for cegcc and mingw32ce hosts, we are cross compiling anyway. - wrappers_required=no + wrappers_required=false ;; *cygwin* | *mingw* ) - if test "$build_libtool_libs" != yes; then - wrappers_required=no - fi + test yes = "$build_libtool_libs" || wrappers_required=false ;; *) - if test "$need_relink" = no || test "$build_libtool_libs" != yes; then - wrappers_required=no + if test no = "$need_relink" || test yes != "$build_libtool_libs"; then + wrappers_required=false fi ;; esac - if test "$wrappers_required" = no; then + $wrappers_required || { # Replace the output file specification. compile_command=`$ECHO "$compile_command" | $SED 's%@OUTPUT@%'"$output"'%g'` - link_command="$compile_command$compile_rpath" + link_command=$compile_command$compile_rpath # We have no uninstalled library dependencies, so finalize right now. exit_status=0 @@ -8898,12 +10503,12 @@ EOF fi # Delete the generated files. - if test -f "$output_objdir/${outputname}S.${objext}"; then - func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' + if test -f "$output_objdir/${outputname}S.$objext"; then + func_show_eval '$RM "$output_objdir/${outputname}S.$objext"' fi exit $exit_status - fi + } if test -n "$compile_shlibpath$finalize_shlibpath"; then compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" @@ -8933,9 +10538,9 @@ EOF fi fi - if test "$no_install" = yes; then + if test yes = "$no_install"; then # We don't need to create a wrapper script. - link_command="$compile_var$compile_command$compile_rpath" + link_command=$compile_var$compile_command$compile_rpath # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output"'%g'` # Delete the old output file. @@ -8952,27 +10557,28 @@ EOF exit $EXIT_SUCCESS fi - if test "$hardcode_action" = relink; then - # Fast installation is not supported - link_command="$compile_var$compile_command$compile_rpath" - relink_command="$finalize_var$finalize_command$finalize_rpath" + case $hardcode_action,$fast_install in + relink,*) + # Fast installation is not supported + link_command=$compile_var$compile_command$compile_rpath + relink_command=$finalize_var$finalize_command$finalize_rpath - func_warning "this platform does not like uninstalled shared libraries" - func_warning "\`$output' will be relinked during installation" - else - if test "$fast_install" != no; then - link_command="$finalize_var$compile_command$finalize_rpath" - if test "$fast_install" = yes; then - relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` - else - # fast_install is set to needless - relink_command= - fi - else - link_command="$compile_var$compile_command$compile_rpath" - relink_command="$finalize_var$finalize_command$finalize_rpath" - fi - fi + func_warning "this platform does not like uninstalled shared libraries" + func_warning "'$output' will be relinked during installation" + ;; + *,yes) + link_command=$finalize_var$compile_command$finalize_rpath + relink_command=`$ECHO "$compile_var$compile_command$compile_rpath" | $SED 's%@OUTPUT@%\$progdir/\$file%g'` + ;; + *,no) + link_command=$compile_var$compile_command$compile_rpath + relink_command=$finalize_var$finalize_command$finalize_rpath + ;; + *,needless) + link_command=$finalize_var$compile_command$finalize_rpath + relink_command= + ;; + esac # Replace the output file specification. link_command=`$ECHO "$link_command" | $SED 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` @@ -9029,8 +10635,8 @@ EOF func_dirname_and_basename "$output" "" "." output_name=$func_basename_result output_path=$func_dirname_result - cwrappersource="$output_path/$objdir/lt-$output_name.c" - cwrapper="$output_path/$output_name.exe" + cwrappersource=$output_path/$objdir/lt-$output_name.c + cwrapper=$output_path/$output_name.exe $RM $cwrappersource $cwrapper trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 @@ -9051,7 +10657,7 @@ EOF trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 $opt_dry_run || { # note: this script will not be executed, so do not chmod. - if test "x$build" = "x$host" ; then + if test "x$build" = "x$host"; then $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result else func_emit_wrapper no > $func_ltwrapper_scriptname_result @@ -9074,25 +10680,27 @@ EOF # See if we need to build an old-fashioned archive. for oldlib in $oldlibs; do - if test "$build_libtool_libs" = convenience; then - oldobjs="$libobjs_save $symfileobj" - addlibs="$convenience" - build_libtool_libs=no - else - if test "$build_libtool_libs" = module; then - oldobjs="$libobjs_save" + case $build_libtool_libs in + convenience) + oldobjs="$libobjs_save $symfileobj" + addlibs=$convenience build_libtool_libs=no - else + ;; + module) + oldobjs=$libobjs_save + addlibs=$old_convenience + build_libtool_libs=no + ;; + *) oldobjs="$old_deplibs $non_pic_objects" - if test "$preload" = yes && test -f "$symfileobj"; then - func_append oldobjs " $symfileobj" - fi - fi - addlibs="$old_convenience" - fi + $preload && test -f "$symfileobj" \ + && func_append oldobjs " $symfileobj" + addlibs=$old_convenience + ;; + esac if test -n "$addlibs"; then - gentop="$output_objdir/${outputname}x" + gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $addlibs @@ -9100,13 +10708,13 @@ EOF fi # Do each command in the archive commands. - if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + if test -n "$old_archive_from_new_cmds" && test yes = "$build_libtool_libs"; then cmds=$old_archive_from_new_cmds else # Add any objects from preloaded convenience libraries if test -n "$dlprefiles"; then - gentop="$output_objdir/${outputname}x" + gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_extract_archives $gentop $dlprefiles @@ -9127,7 +10735,7 @@ EOF : else echo "copying selected object files to avoid basename conflicts..." - gentop="$output_objdir/${outputname}x" + gentop=$output_objdir/${outputname}x func_append generated " $gentop" func_mkdir_p "$gentop" save_oldobjs=$oldobjs @@ -9136,7 +10744,7 @@ EOF for obj in $save_oldobjs do func_basename "$obj" - objbase="$func_basename_result" + objbase=$func_basename_result case " $oldobjs " in " ") oldobjs=$obj ;; *[\ /]"$objbase "*) @@ -9158,6 +10766,8 @@ EOF esac done fi + func_to_tool_file "$oldlib" func_convert_file_msys_to_w32 + tool_oldlib=$func_to_tool_file_result eval cmds=\"$old_archive_cmds\" func_len " $cmds" @@ -9203,18 +10813,18 @@ EOF else # the above command should be used before it gets too long oldobjs=$objlist - if test "$obj" = "$last_oldobj" ; then + if test "$obj" = "$last_oldobj"; then RANLIB=$save_RANLIB fi test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + eval concat_cmds=\"\$concat_cmds$old_archive_cmds\" objlist= len=$len0 fi done RANLIB=$save_RANLIB oldobjs=$objlist - if test "X$oldobjs" = "X" ; then + if test -z "$oldobjs"; then eval cmds=\"\$concat_cmds\" else eval cmds=\"\$concat_cmds~\$old_archive_cmds\" @@ -9231,7 +10841,7 @@ EOF case $output in *.la) old_library= - test "$build_old_libs" = yes && old_library="$libname.$libext" + test yes = "$build_old_libs" && old_library=$libname.$libext func_verbose "creating $output" # Preserve any variables that may affect compiler behavior @@ -9246,30 +10856,31 @@ EOF fi done # Quote the link command for shipping. - relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command="(cd `pwd`; $SHELL \"$progpath\" $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" relink_command=`$ECHO "$relink_command" | $SED "$sed_quote_subst"` - if test "$hardcode_automatic" = yes ; then + if test yes = "$hardcode_automatic"; then relink_command= fi # Only create the output if not a dry run. $opt_dry_run || { for installed in no yes; do - if test "$installed" = yes; then + if test yes = "$installed"; then if test -z "$install_libdir"; then break fi - output="$output_objdir/$outputname"i + output=$output_objdir/${outputname}i # Replace all uninstalled libtool libraries with the installed ones newdependency_libs= for deplib in $dependency_libs; do case $deplib in *.la) func_basename "$deplib" - name="$func_basename_result" - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + name=$func_basename_result + func_resolve_sysroot "$deplib" + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $func_resolve_sysroot_result` test -z "$libdir" && \ - func_fatal_error "\`$deplib' is not a valid libtool archive" + func_fatal_error "'$deplib' is not a valid libtool archive" func_append newdependency_libs " ${lt_sysroot:+=}$libdir/$name" ;; -L*) @@ -9285,23 +10896,23 @@ EOF *) func_append newdependency_libs " $deplib" ;; esac done - dependency_libs="$newdependency_libs" + dependency_libs=$newdependency_libs newdlfiles= for lib in $dlfiles; do case $lib in *.la) func_basename "$lib" - name="$func_basename_result" - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + name=$func_basename_result + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ - func_fatal_error "\`$lib' is not a valid libtool archive" + func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlfiles " ${lt_sysroot:+=}$libdir/$name" ;; *) func_append newdlfiles " $lib" ;; esac done - dlfiles="$newdlfiles" + dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in @@ -9311,34 +10922,34 @@ EOF # didn't already link the preopened objects directly into # the library: func_basename "$lib" - name="$func_basename_result" - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + name=$func_basename_result + eval libdir=`$SED -n -e 's/^libdir=\(.*\)$/\1/p' $lib` test -z "$libdir" && \ - func_fatal_error "\`$lib' is not a valid libtool archive" + func_fatal_error "'$lib' is not a valid libtool archive" func_append newdlprefiles " ${lt_sysroot:+=}$libdir/$name" ;; esac done - dlprefiles="$newdlprefiles" + dlprefiles=$newdlprefiles else newdlfiles= for lib in $dlfiles; do case $lib in - [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlfiles " $abs" done - dlfiles="$newdlfiles" + dlfiles=$newdlfiles newdlprefiles= for lib in $dlprefiles; do case $lib in - [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + [\\/]* | [A-Za-z]:[\\/]*) abs=$lib ;; *) abs=`pwd`"/$lib" ;; esac func_append newdlprefiles " $abs" done - dlprefiles="$newdlprefiles" + dlprefiles=$newdlprefiles fi $RM $output # place dlname in correct position for cygwin @@ -9354,10 +10965,9 @@ EOF case $host,$output,$installed,$module,$dlname in *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) # If a -bindir argument was supplied, place the dll there. - if test "x$bindir" != x ; - then + if test -n "$bindir"; then func_relative_path "$install_libdir" "$bindir" - tdlname=$func_relative_path_result$dlname + tdlname=$func_relative_path_result/$dlname else # Otherwise fall back on heuristic. tdlname=../bin/$dlname @@ -9366,7 +10976,7 @@ EOF esac $ECHO > $output "\ # $outputname - a libtool library file -# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# Generated by $PROGRAM (GNU $PACKAGE) $VERSION # # Please DO NOT delete this file! # It is necessary for linking the library. @@ -9380,7 +10990,7 @@ library_names='$library_names' # The name of the static archive. old_library='$old_library' -# Linker flags that can not go in dependency_libs. +# Linker flags that cannot go in dependency_libs. inherited_linker_flags='$new_inherited_linker_flags' # Libraries that this one depends upon. @@ -9406,7 +11016,7 @@ dlpreopen='$dlprefiles' # Directory that this library needs to be installed in: libdir='$install_libdir'" - if test "$installed" = no && test "$need_relink" = yes; then + if test no,yes = "$installed,$need_relink"; then $ECHO >> $output "\ relink_command=\"$relink_command\"" fi @@ -9421,27 +11031,29 @@ relink_command=\"$relink_command\"" exit $EXIT_SUCCESS } -{ test "$opt_mode" = link || test "$opt_mode" = relink; } && - func_mode_link ${1+"$@"} +if test link = "$opt_mode" || test relink = "$opt_mode"; then + func_mode_link ${1+"$@"} +fi # func_mode_uninstall arg... func_mode_uninstall () { - $opt_debug - RM="$nonopt" + $debug_cmd + + RM=$nonopt files= - rmforce= + rmforce=false exit_status=0 # This variable tells wrapper scripts just to set variables rather # than running their programs. - libtool_install_magic="$magic" + libtool_install_magic=$magic for arg do case $arg in - -f) func_append RM " $arg"; rmforce=yes ;; + -f) func_append RM " $arg"; rmforce=: ;; -*) func_append RM " $arg" ;; *) func_append files " $arg" ;; esac @@ -9454,18 +11066,18 @@ func_mode_uninstall () for file in $files; do func_dirname "$file" "" "." - dir="$func_dirname_result" - if test "X$dir" = X.; then - odir="$objdir" + dir=$func_dirname_result + if test . = "$dir"; then + odir=$objdir else - odir="$dir/$objdir" + odir=$dir/$objdir fi func_basename "$file" - name="$func_basename_result" - test "$opt_mode" = uninstall && odir="$dir" + name=$func_basename_result + test uninstall = "$opt_mode" && odir=$dir # Remember odir for removal later, being careful to avoid duplicates - if test "$opt_mode" = clean; then + if test clean = "$opt_mode"; then case " $rmdirs " in *" $odir "*) ;; *) func_append rmdirs " $odir" ;; @@ -9480,11 +11092,11 @@ func_mode_uninstall () elif test -d "$file"; then exit_status=1 continue - elif test "$rmforce" = yes; then + elif $rmforce; then continue fi - rmfiles="$file" + rmfiles=$file case $name in *.la) @@ -9498,7 +11110,7 @@ func_mode_uninstall () done test -n "$old_library" && func_append rmfiles " $odir/$old_library" - case "$opt_mode" in + case $opt_mode in clean) case " $library_names " in *" $dlname "*) ;; @@ -9509,12 +11121,12 @@ func_mode_uninstall () uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. - func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + func_execute_cmds "$postuninstall_cmds" '$rmforce || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. - func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + func_execute_cmds "$old_postuninstall_cmds" '$rmforce || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; @@ -9530,21 +11142,19 @@ func_mode_uninstall () func_source $dir/$name # Add PIC object to the list of files to remove. - if test -n "$pic_object" && - test "$pic_object" != none; then + if test -n "$pic_object" && test none != "$pic_object"; then func_append rmfiles " $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. - if test -n "$non_pic_object" && - test "$non_pic_object" != none; then + if test -n "$non_pic_object" && test none != "$non_pic_object"; then func_append rmfiles " $dir/$non_pic_object" fi fi ;; *) - if test "$opt_mode" = clean ; then + if test clean = "$opt_mode"; then noexename=$name case $file in *.exe) @@ -9571,12 +11181,12 @@ func_mode_uninstall () # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles - func_append rmfiles " $odir/$name $odir/${name}S.${objext}" - if test "$fast_install" = yes && test -n "$relink_command"; then + func_append rmfiles " $odir/$name $odir/${name}S.$objext" + if test yes = "$fast_install" && test -n "$relink_command"; then func_append rmfiles " $odir/lt-$name" fi - if test "X$noexename" != "X$name" ; then - func_append rmfiles " $odir/lt-${noexename}.c" + if test "X$noexename" != "X$name"; then + func_append rmfiles " $odir/lt-$noexename.c" fi fi fi @@ -9585,7 +11195,7 @@ func_mode_uninstall () func_show_eval "$RM $rmfiles" 'exit_status=1' done - # Try to remove the ${objdir}s in the directories where we deleted files + # Try to remove the $objdir's in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then func_show_eval "rmdir $dir >/dev/null 2>&1" @@ -9595,16 +11205,17 @@ func_mode_uninstall () exit $exit_status } -{ test "$opt_mode" = uninstall || test "$opt_mode" = clean; } && - func_mode_uninstall ${1+"$@"} +if test uninstall = "$opt_mode" || test clean = "$opt_mode"; then + func_mode_uninstall ${1+"$@"} +fi test -z "$opt_mode" && { - help="$generic_help" + help=$generic_help func_fatal_help "you must specify a MODE" } test -z "$exec_cmd" && \ - func_fatal_help "invalid operation mode \`$opt_mode'" + func_fatal_help "invalid operation mode '$opt_mode'" if test -n "$exec_cmd"; then eval exec "$exec_cmd" @@ -9615,7 +11226,7 @@ exit $exit_status # The TAGs below are defined such that we never get into a situation -# in which we disable both kinds of libraries. Given conflicting +# where we disable both kinds of libraries. Given conflicting # choices, we go for a static library, that is the most portable, # since we can't tell whether shared libraries were disabled because # the user asked for that or because the platform doesn't support @@ -9638,5 +11249,3 @@ build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # mode:shell-script # sh-indentation:2 # End: -# vi:sw=2 - diff --git a/m4/ax_c___attribute__.m4 b/m4/ax_c___attribute__.m4 new file mode 100644 index 0000000..0e952e7 --- /dev/null +++ b/m4/ax_c___attribute__.m4 @@ -0,0 +1,134 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_c___attribute__.html +# +# Downloaded into the Network UPS Tools (NUT) codebase from +# http://git.savannah.gnu.org/gitweb/?p=autoconf-archive.git;a=blob_plain;f=m4/ax_c___attribute__.m4 +# as of 2020-11-20 and adapted for attribute supports we needed +# =========================================================================== +# +# SYNOPSIS +# +# AX_C___ATTRIBUTE__ +# +# DESCRIPTION +# +# Provides a test for the compiler support of __attribute__ extensions. +# Defines HAVE___ATTRIBUTE__ if it is found. +# Also in particular defines +# HAVE___ATTRIBUTE__UNUSED_ARG +# HAVE___ATTRIBUTE__UNUSED_FUNC +# HAVE___ATTRIBUTE__NORETURN +# if support for respective values and use-cases of interest for NUT +# codebase is found. +# +# LICENSE +# +# Copyright (c) 2008 Stepan Kasal +# Copyright (c) 2008 Christian Haggstrom +# Copyright (c) 2008 Ryan McCabe +# Copyright (c) 2020 Jim Klimov +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 2 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 9 + +AC_DEFUN([AX_C___ATTRIBUTE__], [ + AC_LANG_PUSH([C]) + AC_CACHE_CHECK([for __attribute__((unused)) for function arguments], [ax_cv___attribute__unused_arg], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include + static void foo( int); + static void + foo(__attribute__ ((unused)) int i) { + return; + } + ]], [func(1);])], + [ax_cv___attribute__unused_arg=yes], + [ax_cv___attribute__unused_arg=no] + ) + ]) + + AC_CACHE_CHECK([for __attribute__((unused)) for functions], [ax_cv___attribute__unused_func], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include + static void foo(void) __attribute__ ((unused)); + static void + foo(void) { + return; + } + ]], [])], + [ax_cv___attribute__unused_func=yes], + [ax_cv___attribute__unused_func=no] + ) + ]) + + AC_CACHE_CHECK([for __attribute__((noreturn))], [ax_cv___attribute__noreturn], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[#include + static void foo(void) __attribute__ ((noreturn)); + static void + foo(void) { + exit(1); + } + ]], [foo();])], + [ax_cv___attribute__noreturn=yes], + [ax_cv___attribute__noreturn=no] + ) + ]) + AC_LANG_POP([C]) + + AC_CACHE_CHECK([for at least some __attribute__ support], [ax_cv___attribute__], + [if test "$ax_cv___attribute__unused_arg" = "yes" \ + || test "$ax_cv___attribute__unused_func" = "yes" \ + || test "$ax_cv___attribute__noreturn" = "yes" \ + ; then + dnl # Some values did not error, support for keyword itself exists + ax_cv___attribute__=yes + else + dnl # At least none of the options we are interested in work... + ax_cv___attribute__=no + fi + ]) + + if test "$ax_cv___attribute__unused_arg" = "yes"; then + AC_DEFINE([HAVE___ATTRIBUTE__UNUSED_ARG], 1, [define if your compiler has __attribute__((unused)) for function arguments]) + fi + + if test "$ax_cv___attribute__unused_func" = "yes"; then + AC_DEFINE([HAVE___ATTRIBUTE__UNUSED_FUNC], 1, [define if your compiler has __attribute__((unused)) for functions]) + fi + if test "$ax_cv___attribute__noreturn" = "yes"; then + AC_DEFINE([HAVE___ATTRIBUTE__NORETURN], 1, [define if your compiler has __attribute__((noreturn))]) + fi + + if test "$ax_cv___attribute__" = "yes"; then + AC_DEFINE([HAVE___ATTRIBUTE__], 1, [define if your compiler has __attribute__]) + fi +]) diff --git a/m4/ax_c_pragmas.m4 b/m4/ax_c_pragmas.m4 new file mode 100644 index 0000000..53c1aeb --- /dev/null +++ b/m4/ax_c_pragmas.m4 @@ -0,0 +1,983 @@ +dnl Check for current compiler support of specific pragmas we use, +dnl e.g. diagnostics management to keep warnings quiet sometimes + +AC_DEFUN([AX_C_PRAGMAS], [ +if test -z "${nut_have_ax_c_pragmas_seen}"; then + nut_have_ax_c_pragmas_seen="yes" + + CFLAGS_SAVED="${CFLAGS}" + CXXFLAGS_SAVED="${CXXFLAGS}" + + dnl ### To be sure, bolt the language + AC_LANG_PUSH([C]) + + dnl # This is expected to fail builds with unknown pragma names on GCC or CLANG at least + AS_IF([test "${CLANG}" = "yes"], + [CFLAGS="${CFLAGS_SAVED} -Werror=pragmas -Werror=unknown-pragmas -Werror=unknown-warning-option" + CXXFLAGS="${CXXFLAGS_SAVED} -Werror=pragmas -Werror=unknown-pragmas -Werror=unknown-warning-option"], + [AS_IF([test "${GCC}" = "yes"], +dnl ### Despite the docs, this dies with lack of (apparently) support for +dnl ### -Wunknown-warning(-options) on all GCC versions I tried (v5-v10) +dnl ### [CFLAGS="${CFLAGS_SAVED} -Werror=pragmas -Werror=unknown-warning"], + [CFLAGS="${CFLAGS_SAVED} -Wall -Wextra -Werror" + CXXFLAGS="${CXXFLAGS_SAVED} -Wall -Wextra -Werror"], + [CFLAGS="${CFLAGS_SAVED} -Wall -Wextra -Werror" + CXXFLAGS="${CXXFLAGS_SAVED} -Wall -Wextra -Werror"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic push and pop (outside functions)], + [ax_cv__pragma__gcc__diags_push_pop_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ +#pragma GCC diagnostic push +#pragma GCC diagnostic pop + ]], [])], + [ax_cv__pragma__gcc__diags_push_pop_besidefunc=yes], + [ax_cv__pragma__gcc__diags_push_pop_besidefunc=no] + )] + ) + + AC_CACHE_CHECK([for pragma clang diagnostic push and pop (outside functions)], + [ax_cv__pragma__clang__diags_push_pop_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[ +#ifdef __clang__ +#endif +#pragma clang diagnostic push +#pragma clang diagnostic pop + ]], [])], + [ax_cv__pragma__clang__diags_push_pop_besidefunc=yes], + [ax_cv__pragma__clang__diags_push_pop_besidefunc=no] + )] + ) + + dnl # Currently our code uses these pragmas as close to lines that cause + dnl # questions from linters as possible. GCC before 4.5 did not allow + dnl # for diag pragmas inside function bodies, but also did not complain + dnl # about messy code as new compilers do. For completeness, we support + dnl # the possibility of defining larger-scoped pragmas around whole + dnl # function bodies. In practice, we don't currently do that so in + dnl # most cases the shorter names (without _INSIDEFUNC) are used with + dnl # that implied meaning. + + AC_CACHE_CHECK([for pragma GCC diagnostic push and pop (inside functions)], + [ax_cv__pragma__gcc__diags_push_pop_insidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic push +#pragma GCC diagnostic pop +} + ]], [])], + [ax_cv__pragma__gcc__diags_push_pop_insidefunc=yes], + [ax_cv__pragma__gcc__diags_push_pop_insidefunc=no] + )] + ) + + AC_CACHE_CHECK([for pragma clang diagnostic push and pop (inside functions)], + [ax_cv__pragma__clang__diags_push_pop_insidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#ifdef __clang__ +#endif +#pragma clang diagnostic push +#pragma clang diagnostic pop +} + ]], [])], + [ax_cv__pragma__clang__diags_push_pop_insidefunc=yes], + [ax_cv__pragma__clang__diags_push_pop_insidefunc=no] + )] + ) + + AS_IF([test "$ax_cv__pragma__gcc__diags_push_pop_insidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic push and pop inside function bodies]) + ]) + + AS_IF([test "$ax_cv__pragma__gcc__diags_push_pop_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic push and pop outside function bodies]) + ]) + + AS_IF([test "$ax_cv__pragma__gcc__diags_push_pop_besidefunc" = "yes" && test "$ax_cv__pragma__gcc__diags_push_pop_insidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP], 1, [define if your compiler has #pragma GCC diagnostic push and pop]) + ]) + + AS_IF([test "$ax_cv__pragma__clang__diags_push_pop_insidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_CLANG_DIAGNOSTIC_PUSH_POP_INSIDEFUNC], 1, [define if your compiler has #pragma clang diagnostic push and pop inside function bodies]) + ]) + + AS_IF([test "$ax_cv__pragma__clang__diags_push_pop_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_CLANG_DIAGNOSTIC_PUSH_POP_BESIDEFUNC], 1, [define if your compiler has #pragma clang diagnostic push and pop outside function bodies]) + ]) + + AS_IF([test "$ax_cv__pragma__clang__diags_push_pop_besidefunc" = "yes" && test "$ax_cv__pragma__clang__diags_push_pop_insidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_CLANG_DIAGNOSTIC_PUSH_POP], 1, [define if your compiler has #pragma clang diagnostic push and pop]) + ]) + + dnl Test for some clang-specific pragma support: primarily useful for older + dnl clang (3.x) releases, so polluting NUT codebase only when unavoidable. + dnl In most cases, GCC pragmas are usable by both; in a few others, direct + dnl use of `#ifdef __clang__` suffices. + AS_IF([test "${CLANG}" = "yes"], [ + AC_CACHE_CHECK([for pragma CLANG diagnostic ignored "-Wunreachable-code-return"], + [ax_cv__pragma__clang__diags_ignored_unreachable_code_return], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma clang diagnostic ignored "-Wunreachable-code-return" +} + ]], [])], + [ax_cv__pragma__clang__diags_ignored_unreachable_code_return=yes], + [ax_cv__pragma__clang__diags_ignored_unreachable_code_return=no] + )] + ) + AS_IF([test "$ax_cv__pragma__clang__diags_ignored_unreachable_code_return" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_RETURN], 1, [define if your compiler has #pragma clang diagnostic ignored "-Wunreachable-code-return"]) + ]) + + AC_CACHE_CHECK([for pragma CLANG diagnostic ignored "-Wunreachable-code-return" (outside functions)], + [ax_cv__pragma__clang__diags_ignored_unreachable_code_return_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma clang diagnostic ignored "-Wunreachable-code-return"]], [])], + [ax_cv__pragma__clang__diags_ignored_unreachable_code_return_besidefunc=yes], + [ax_cv__pragma__clang__diags_ignored_unreachable_code_return_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__clang__diags_ignored_unreachable_code_return_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_RETURN_BESIDEFUNC], 1, [define if your compiler has #pragma clang diagnostic ignored "-Wunreachable-code-return" (outside functions)]) + ]) + ]) dnl Special pragma support testing for clang + + dnl Test common pragmas for GCC (and compatible) compilers + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wunused-function"], + [ax_cv__pragma__gcc__diags_ignored_unused_function], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wunused-function" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_unused_function=yes], + [ax_cv__pragma__gcc__diags_ignored_unused_function=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_unused_function" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNUSED_FUNCTION], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wunused-function"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wformat-nonliteral"], + [ax_cv__pragma__gcc__diags_ignored_format_nonliteral], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_format_nonliteral=yes], + [ax_cv__pragma__gcc__diags_ignored_format_nonliteral=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_format_nonliteral" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wformat-nonliteral"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wformat-nonliteral" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_format_nonliteral_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wformat-nonliteral"]], [])], + [ax_cv__pragma__gcc__diags_ignored_format_nonliteral_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_format_nonliteral_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_format_nonliteral_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wformat-nonliteral" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wformat-security"], + [ax_cv__pragma__gcc__diags_ignored_format_security], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wformat-security" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_format_security=yes], + [ax_cv__pragma__gcc__diags_ignored_format_security=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_format_security" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wformat-security"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wformat-security" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_format_security_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wformat-security"]], [])], + [ax_cv__pragma__gcc__diags_ignored_format_security_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_format_security_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_format_security_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wformat-security" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wformat-truncation"], + [ax_cv__pragma__gcc__diags_ignored_format_truncation], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wformat-truncation" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_format_truncation=yes], + [ax_cv__pragma__gcc__diags_ignored_format_truncation=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_format_truncation" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wformat-truncation"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wformat-truncation" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_format_truncation_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wformat-truncation"]], [])], + [ax_cv__pragma__gcc__diags_ignored_format_truncation_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_format_truncation_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_format_truncation_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wformat-truncation" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wstringop-truncation"], + [ax_cv__pragma__gcc__diags_ignored_stringop_truncation], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wstringop-truncation" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_stringop_truncation=yes], + [ax_cv__pragma__gcc__diags_ignored_stringop_truncation=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_stringop_truncation" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRINGOP_TRUNCATION], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wstringop-truncation"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wstringop-truncation" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_stringop_truncation_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wstringop-truncation"]], [])], + [ax_cv__pragma__gcc__diags_ignored_stringop_truncation_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_stringop_truncation_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_stringop_truncation_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRINGOP_TRUNCATION_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wstringop-truncation" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtype-limits"], + [ax_cv__pragma__gcc__diags_ignored_type_limits], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wtype-limits" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_type_limits=yes], + [ax_cv__pragma__gcc__diags_ignored_type_limits=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_type_limits" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wtype-limits"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtype-limits" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_type_limits_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wtype-limits"]], [])], + [ax_cv__pragma__gcc__diags_ignored_type_limits_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_type_limits_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_type_limits_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wtype-limits" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Warray-bounds"], + [ax_cv__pragma__gcc__diags_ignored_array_bounds], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Warray-bounds" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_array_bounds=yes], + [ax_cv__pragma__gcc__diags_ignored_array_bounds=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_array_bounds" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ARRAY_BOUNDS], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Warray-bounds"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Warray-bounds" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_array_bounds_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Warray-bounds"]], [])], + [ax_cv__pragma__gcc__diags_ignored_array_bounds_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_array_bounds_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_array_bounds_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ARRAY_BOUNDS_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Warray-bounds" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtautological-type-limit-compare"], + [ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare=yes], + [ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wtautological-type-limit-compare"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wtautological-type-limit-compare"]], [])], + [ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_tautological_type_limit_compare_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_TYPE_LIMIT_COMPARE_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wtautological-type-limit-compare" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"], + [ax_cv__pragma__gcc__diags_ignored_tautological_constant_out_of_range_compare], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_tautological_constant_out_of_range_compare=yes], + [ax_cv__pragma__gcc__diags_ignored_tautological_constant_out_of_range_compare=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_tautological_constant_out_of_range_compare" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_tautological_constant_out_of_range_compare_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"]], [])], + [ax_cv__pragma__gcc__diags_ignored_tautological_constant_out_of_range_compare_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_tautological_constant_out_of_range_compare_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_tautological_constant_out_of_range_compare_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare"], + [ax_cv__pragma__gcc__diags_ignored_tautological_unsigned_zero_compare], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_tautological_unsigned_zero_compare=yes], + [ax_cv__pragma__gcc__diags_ignored_tautological_unsigned_zero_compare=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_tautological_unsigned_zero_compare" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_tautological_unsigned_zero_compare_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare"]], [])], + [ax_cv__pragma__gcc__diags_ignored_tautological_unsigned_zero_compare_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_tautological_unsigned_zero_compare_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_tautological_unsigned_zero_compare_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wtautological-unsigned-zero-compare" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtautological-compare"], + [ax_cv__pragma__gcc__diags_ignored_tautological_compare], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wtautological-compare" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_tautological_compare=yes], + [ax_cv__pragma__gcc__diags_ignored_tautological_compare=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_tautological_compare" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wtautological-compare"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wtautological-compare" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_tautological_compare_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wtautological-compare"]], [])], + [ax_cv__pragma__gcc__diags_ignored_tautological_compare_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_tautological_compare_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_tautological_compare_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wtautological-compare" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wsign-compare"], + [ax_cv__pragma__gcc__diags_ignored_sign_compare], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wsign-compare" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_sign_compare=yes], + [ax_cv__pragma__gcc__diags_ignored_sign_compare=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_sign_compare" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_COMPARE], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wsign-compare"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wsign-compare" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_sign_compare_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wsign-compare"]], [])], + [ax_cv__pragma__gcc__diags_ignored_sign_compare_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_sign_compare_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_sign_compare_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_COMPARE_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wsign-compare" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wsign-conversion"], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wsign-conversion" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion=yes], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_sign_conversion" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wsign-conversion"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wsign-conversion" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wsign-conversion"]], [])], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_sign_conversion_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_sign_conversion_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wsign-conversion" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wunreachable-code-break"], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_break], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wunreachable-code-break" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_break=yes], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_break=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_unreachable_code_break" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_BREAK], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wunreachable-code-break"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wunreachable-code-break" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_break_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wunreachable-code-break"]], [])], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_break_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_break_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_unreachable_code_break_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_BREAK_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wunreachable-code-break" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wunreachable-code-return"], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_return], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wunreachable-code-return" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_return=yes], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_return=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_unreachable_code_return" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_RETURN], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wunreachable-code-return"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wunreachable-code-return" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_return_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wunreachable-code-return"]], [])], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_return_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_return_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_unreachable_code_return_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_RETURN_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wunreachable-code-return" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wunreachable-code"], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wunreachable-code" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code=yes], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_unreachable_code" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wunreachable-code"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wunreachable-code" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wunreachable-code"]], [])], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_unreachable_code_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_unreachable_code_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wunreachable-code" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wformat-overflow"], + [ax_cv__pragma__gcc__diags_ignored_format_overflow], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wformat-overflow" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_format_overflow=yes], + [ax_cv__pragma__gcc__diags_ignored_format_overflow=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_format_overflow" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_OVERFLOW], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wformat-overflow"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wformat-overflow" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_format_overflow_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wformat-overflow"]], [])], + [ax_cv__pragma__gcc__diags_ignored_format_overflow_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_format_overflow_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_format_overflow_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_OVERFLOW_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wformat-overflow" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wcovered-switch-default"], + [ax_cv__pragma__gcc__diags_ignored_covered_switch_default], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wcovered-switch-default" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_covered_switch_default=yes], + [ax_cv__pragma__gcc__diags_ignored_covered_switch_default=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_covered_switch_default" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wcovered-switch-default"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wcovered-switch-default" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_covered_switch_default_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wcovered-switch-default"]], [])], + [ax_cv__pragma__gcc__diags_ignored_covered_switch_default_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_covered_switch_default_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_covered_switch_default_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wcovered-switch-default" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wextra-semi-stmt"], + [ax_cv__pragma__gcc__diags_ignored_extra_semi_stmt], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wextra-semi-stmt" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_extra_semi_stmt=yes], + [ax_cv__pragma__gcc__diags_ignored_extra_semi_stmt=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_extra_semi_stmt" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wextra-semi-stmt"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wextra-semi-stmt" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_extra_semi_stmt_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wextra-semi-stmt"]], [])], + [ax_cv__pragma__gcc__diags_ignored_extra_semi_stmt_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_extra_semi_stmt_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_extra_semi_stmt_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXTRA_SEMI_STMT_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wextra-semi-stmt" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wcast-align"], + [ax_cv__pragma__gcc__diags_ignored_cast_align], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wcast-align" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_cast_align=yes], + [ax_cv__pragma__gcc__diags_ignored_cast_align=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_cast_align" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wcast-align"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wcast-align" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_cast_align_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wcast-align"]], [])], + [ax_cv__pragma__gcc__diags_ignored_cast_align_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_cast_align_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_cast_align_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wcast-align" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wstrict-prototypes"], + [ax_cv__pragma__gcc__diags_ignored_strict_prototypes], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wstrict-prototypes" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_strict_prototypes=yes], + [ax_cv__pragma__gcc__diags_ignored_strict_prototypes=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_strict_prototypes" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wstrict-prototypes"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wstrict-prototypes" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_strict_prototypes_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wstrict-prototypes"]], [])], + [ax_cv__pragma__gcc__diags_ignored_strict_prototypes_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_strict_prototypes_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_strict_prototypes_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wstrict-prototypes" (outside functions)]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wassign-enum"], + [ax_cv__pragma__gcc__diags_ignored_assign_enum], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wassign-enum" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_assign_enum=yes], + [ax_cv__pragma__gcc__diags_ignored_assign_enum=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_assign_enum" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ASSIGN_ENUM], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wassign-enum"]) + ]) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-Wassign-enum" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_assign_enum_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wassign-enum"]], [])], + [ax_cv__pragma__gcc__diags_ignored_assign_enum_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_assign_enum_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_assign_enum_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ASSIGN_ENUM_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wassign-enum" (outside functions)]) + ]) + + AC_LANG_POP([C]) + + dnl ### Series of tests for C++ specific pragmas + AC_LANG_PUSH([C++]) + + AC_CACHE_CHECK([for C++ pragma GCC diagnostic ignored "-Wc++98-compat-pedantic"], + [ax_cv__pragma__gcc__diags_ignored_cxx98_compat_pedantic], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wc++98-compat-pedantic" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_cxx98_compat_pedantic=yes], + [ax_cv__pragma__gcc__diags_ignored_cxx98_compat_pedantic=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_cxx98_compat_pedantic" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT_PEDANTIC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wc++98-compat-pedantic"]) + ]) + + AC_CACHE_CHECK([for C++ pragma GCC diagnostic ignored "-Wc++98-compat-pedantic" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_cxx98_compat_pedantic_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wc++98-compat-pedantic"]], [])], + [ax_cv__pragma__gcc__diags_ignored_cxx98_compat_pedantic_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_cxx98_compat_pedantic_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_cxx98_compat_pedantic_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT_PEDANTIC_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wc++98-compat-pedantic" (outside functions)]) + ]) + + AC_CACHE_CHECK([for C++ pragma GCC diagnostic ignored "-Wc++98-compat"], + [ax_cv__pragma__gcc__diags_ignored_cxx98_compat], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wc++98-compat" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_cxx98_compat=yes], + [ax_cv__pragma__gcc__diags_ignored_cxx98_compat=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_cxx98_compat" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wc++98-compat"]) + ]) + + AC_CACHE_CHECK([for C++ pragma GCC diagnostic ignored "-Wc++98-compat" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_cxx98_compat_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wc++98-compat"]], [])], + [ax_cv__pragma__gcc__diags_ignored_cxx98_compat_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_cxx98_compat_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_cxx98_compat_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wc++98-compat" (outside functions)]) + ]) + + AC_CACHE_CHECK([for C++ pragma GCC diagnostic ignored "-Wglobal-constructors"], + [ax_cv__pragma__gcc__diags_ignored_global_constructors], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wglobal-constructors" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_global_constructors=yes], + [ax_cv__pragma__gcc__diags_ignored_global_constructors=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_global_constructors" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wglobal-constructors"]) + ]) + + AC_CACHE_CHECK([for C++ pragma GCC diagnostic ignored "-Wglobal-constructors" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_global_constructors_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wglobal-constructors"]], [])], + [ax_cv__pragma__gcc__diags_ignored_global_constructors_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_global_constructors_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_global_constructors_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wglobal-constructors" (outside functions)]) + ]) + + AC_CACHE_CHECK([for C++ pragma GCC diagnostic ignored "-Wexit-time-destructors"], + [ax_cv__pragma__gcc__diags_ignored_exit_time_destructors], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-Wexit-time-destructors" +} +]], [])], + [ax_cv__pragma__gcc__diags_ignored_exit_time_destructors=yes], + [ax_cv__pragma__gcc__diags_ignored_exit_time_destructors=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_exit_time_destructors" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wexit-time-destructors"]) + ]) + + AC_CACHE_CHECK([for C++ pragma GCC diagnostic ignored "-Wexit-time-destructors" (outside functions)], + [ax_cv__pragma__gcc__diags_ignored_exit_time_destructors_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-Wexit-time-destructors"]], [])], + [ax_cv__pragma__gcc__diags_ignored_exit_time_destructors_besidefunc=yes], + [ax_cv__pragma__gcc__diags_ignored_exit_time_destructors_besidefunc=no] + )] + ) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_exit_time_destructors_besidefunc" = "yes"],[ + AC_DEFINE([HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS_BESIDEFUNC], 1, [define if your compiler has #pragma GCC diagnostic ignored "-Wexit-time-destructors" (outside functions)]) + ]) + + AC_LANG_POP([C++]) + + dnl # Meta-macros for simpler use-cases where we pick + dnl # equivalent-effect macros for different compiler versions + AS_IF([test "$ax_cv__pragma__gcc__diags_push_pop_insidefunc" = "yes"],[ + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_format_security" = "yes" || test "$ax_cv__pragma__gcc__diags_ignored_format_nonliteral" = "yes" ],[ + AC_DEFINE([HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL], 1, [define if your compiler has pragmas for GCC diagnostic ignored "-Wformat-nonliteral" or "-Wformat-security" and for push-pop support]) + ]) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_format_truncation" = "yes" || test "$ax_cv__pragma__gcc__diags_ignored_stringop_truncation" = "yes" ],[ + AC_DEFINE([HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION], 1, [define if your compiler has pragmas for GCC diagnostic ignored "-Wformat-truncation" or "-Werror=stringop-truncation" and for push-pop support]) + ]) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_unreachable_code" = "yes" || test "$ax_cv__pragma__gcc__diags_ignored_unreachable_code_break" = "yes" ],[ + AC_DEFINE([HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE], 1, [define if your compiler has pragmas for GCC diagnostic ignored "-Wunreachable-code(-break)" and for push-pop support]) + ]) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_cxx98_compat_pedantic" = "yes" || test "$ax_cv__pragma__gcc__diags_ignored_cxx98_compat" = "yes" ],[ + AC_DEFINE([HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT], 1, [define if your compiler has pragmas for GCC diagnostic ignored "-Wc++98-compat(-pedantic)" and for push-pop support]) + ]) + ]) + + AS_IF([test "$ax_cv__pragma__gcc__diags_push_pop_besidefunc" = "yes"],[ + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_format_security" = "yes" || test "$ax_cv__pragma__gcc__diags_ignored_format_nonliteral" = "yes" ],[ + AC_DEFINE([HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL_BESIDEFUNC], 1, [define if your compiler has pragmas for GCC diagnostic ignored "-Wformat-nonliteral" or "-Wformat-security" and for push-pop support (outside function bodies)]) + ]) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_format_truncation" = "yes" || test "$ax_cv__pragma__gcc__diags_ignored_stringop_truncation" = "yes" ],[ + AC_DEFINE([HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_TRUNCATION_BESIDEFUNC], 1, [define if your compiler has pragmas for GCC diagnostic ignored "-Wformat-truncation" or "-Werror=stringop-truncation" and for push-pop support (outside function bodies)]) + ]) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_unreachable_code" = "yes" || test "$ax_cv__pragma__gcc__diags_ignored_unreachable_code_break" = "yes" ],[ + AC_DEFINE([HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE_BESIDEFUNC], 1, [define if your compiler has pragmas for GCC diagnostic ignored "-Wunreachable-code(-break)" and for push-pop support (outside function bodies)]) + ]) + AS_IF([test "$ax_cv__pragma__gcc__diags_ignored_cxx98_compat_pedantic" = "yes" || test "$ax_cv__pragma__gcc__diags_ignored_cxx98_compat" = "yes" ],[ + AC_DEFINE([HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_CXX98_COMPAT_BESIDEFUNC], 1, [define if your compiler has pragmas for GCC diagnostic ignored "-Wc++98-compat(-pedantic)" and for push-pop support (outside function bodies)]) + ]) + ]) + + dnl ### Sanity check if the CLI options actually work: + AC_CACHE_CHECK([for pragma BOGUSforTest], + [ax_cv__pragma__bogus], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma BOGUSforTest +} +]], [])], + [ax_cv__pragma__bogus=yes], + [ax_cv__pragma__bogus=no] + )] + ) + + AC_CACHE_CHECK([for pragma BOGUSforTest (outside functions)], + [ax_cv__pragma__bogus_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma BOGUSforTest]], [])], + [ax_cv__pragma__bogus_besidefunc=yes], + [ax_cv__pragma__bogus_besidefunc=no] + )] + ) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-WBOGUSforTest"], + [ax_cv__pragma__bogus_diag], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[void func(void) { +#pragma GCC diagnostic ignored "-WBOGUSforTest" +} +]], [])], + [ax_cv__pragma__bogus_diag=yes], + [ax_cv__pragma__bogus_diag=no] + )] + ) + + AC_CACHE_CHECK([for pragma GCC diagnostic ignored "-WBOGUSforTest" (outside functions)], + [ax_cv__pragma__bogus_diag_besidefunc], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([[#pragma GCC diagnostic ignored "-WBOGUSforTest"]], [])], + [ax_cv__pragma__bogus_diag_besidefunc=yes], + [ax_cv__pragma__bogus_diag_besidefunc=no] + )] + ) + + AS_IF([test "${ax_cv__pragma__bogus}" != "no"], + [AC_MSG_WARN([A bogus test that was expected to fail did not! ax_cv__pragma__bogus=$ax_cv__pragma__bogus (not 'no')])]) + + AS_IF([test "${ax_cv__pragma__bogus_besidefunc}" != "no"], + [AC_MSG_WARN([A bogus test that was expected to fail did not! ax_cv__pragma__bogus_besidefunc=$ax_cv__pragma__bogus_besidefunc (not 'no')])]) + + AS_IF([test "${ax_cv__pragma__bogus_diag}" != "no"], + [AC_MSG_WARN([A bogus test that was expected to fail did not! ax_cv__pragma__bogus_diag=$ax_cv__pragma__bogus_diag (not 'no')])]) + + AS_IF([test "${ax_cv__pragma__bogus_diag_besidefunc}" != "no"], + [AC_MSG_WARN([A bogus test that was expected to fail did not! ax_cv__pragma__bogus_diag_besidefunc=$ax_cv__pragma__bogus_diag_besidefunc (not 'no')])]) + + CFLAGS="${CFLAGS_SAVED}" + CXXFLAGS="${CXXFLAGS_SAVED}" +fi +]) + +AC_DEFUN([AX_C_PRINTF_STRING_NULL], [ +if test -z "${nut_have_ax_c_printf_string_null_seen}"; then + nut_have_ax_c_printf_string_null_seen="yes" + AC_REQUIRE([AX_RUN_OR_LINK_IFELSE])dnl + + dnl ### To be sure, bolt the language + AC_LANG_PUSH([C]) + + AC_CACHE_CHECK([for practical support to pritnf("%s", NULL)], + [ax_cv__printf_string_null], + [AX_RUN_OR_LINK_IFELSE( + [AC_LANG_PROGRAM([dnl +#include +#include +], [dnl +char buf[128]; +char *s = NULL; +int res = snprintf(buf, sizeof(buf), "%s", s); +buf[sizeof(buf)-1] = '\0'; +if (res < 0) { + printf(stderr, "FAILED to snprintf() a NULL string argument"); + exit 1; +} +if (buf[0] == '\0') + printf(stderr, "RETURNED empty string from snprintf() with a NULL string argument"); + exit 0; +} +if (strcasestr(buf, 'null') == NULL) + printf(stderr, "RETURNED some string from snprintf() with a NULL string argument: '%s'", buf); + exit 0; +} +printf(stderr, "SUCCESS: RETURNED a string that contains something like 'null' from snprintf() with a NULL string argument: '%s'", buf); +exit 0; + ])], + [ax_cv__printf_string_null=yes], + [ax_cv__printf_string_null=no] + )] + ) + + AS_IF([test "$ax_cv__printf_string_null" = "yes"],[ + AC_DEFINE([HAVE_PRINTF_STRING_NULL], 1, [define if your libc can printf("%s", NULL) sanely]) + ]) + + AC_LANG_POP([C]) +fi +]) diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 0000000..a72fc05 --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,61 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# NOTE: This implementation was extended with check for compiler complaints +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# Copyright (c) 2022 Jim Klimov +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 6 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [dnl Toolkit per se did not return an error code; but did it complain? + dnl Below are a few strings typical for some versions of GCC and CLANG + dnl This relies on AC_COMPILE_IFELSE implementation retaining conftest.err + AS_IF([grep -E '(unrecognized.* option|did you mean|unknown argument:)' < conftest.err >/dev/null 2>/dev/null], + [AS_VAR_SET(CACHEVAR,[no])],dnl Hit a complaint, flag is not supported after all + [AS_VAR_SET(CACHEVAR,[yes])])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/m4/ax_compare_version.m4 b/m4/ax_compare_version.m4 old mode 100755 new mode 100644 diff --git a/m4/ax_run_or_link_ifelse.m4 b/m4/ax_run_or_link_ifelse.m4 new file mode 100644 index 0000000..0e36c7e --- /dev/null +++ b/m4/ax_run_or_link_ifelse.m4 @@ -0,0 +1,21 @@ +dnl By default, AC_RUN_IFELSE() fails if it detects cross-compilation +dnl but it provides the fourth argument to customize the handling. +dnl In our case, we would fall back to trying just to link then - +dnl the result would not be as relevant regarding run-time behavior +dnl of the code, but at least we would know that API and ABI are ok. + +dnl Per original /usr/share/autoconf/autoconf/general.m4 which makes +dnl a similar wrapper: +dnl # AC_TRY_RUN(PROGRAM, +dnl # [ACTION-IF-TRUE], [ACTION-IF-FALSE], +dnl # [ACTION-IF-CROSS-COMPILING = RUNTIME-ERROR]) +dnl # ------------------------------------------------------- +AC_DEFUN([AX_RUN_OR_LINK_IFELSE], +[ + AC_RUN_IFELSE([$1], [$2], [$3], + [ + AC_MSG_WARN([Current build is a cross-build, so not running test binaries, just linking them]) + AC_LINK_IFELSE([$1], [$2], [$3]) + ] + ) +]) diff --git a/m4/libtool.m4 b/m4/libtool.m4 index 8ff3c76..c4c0294 100644 --- a/m4/libtool.m4 +++ b/m4/libtool.m4 @@ -1,8 +1,6 @@ # libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- # -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, -# 2006, 2007, 2008, 2009, 2010 Free Software Foundation, -# Inc. +# Copyright (C) 1996-2001, 2003-2015 Free Software Foundation, Inc. # Written by Gordon Matzigkeit, 1996 # # This file is free software; the Free Software Foundation gives @@ -10,36 +8,30 @@ # modifications, as long as this notice is preserved. m4_define([_LT_COPYING], [dnl -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, -# 2006, 2007, 2008, 2009, 2010 Free Software Foundation, -# Inc. -# Written by Gordon Matzigkeit, 1996 +# Copyright (C) 2014 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of of the License, or +# (at your option) any later version. # -# This file is part of GNU Libtool. +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program or library that is built +# using GNU Libtool, you may include this file under the same +# distribution terms that you use for the rest of that program. # -# GNU Libtool is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation; either version 2 of -# the License, or (at your option) any later version. -# -# As a special exception to the GNU General Public License, -# if you distribute this file as part of a program or library that -# is built using GNU Libtool, you may include this file under the -# same distribution terms that you use for the rest of that program. -# -# GNU Libtool is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of +# GNU Libtool is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with GNU Libtool; see the file COPYING. If not, a copy -# can be downloaded from http://www.gnu.org/licenses/gpl.html, or -# obtained by writing to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# along with this program. If not, see . ]) -# serial 57 LT_INIT +# serial 58 LT_INIT # LT_PREREQ(VERSION) @@ -67,7 +59,7 @@ esac # LT_INIT([OPTIONS]) # ------------------ AC_DEFUN([LT_INIT], -[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT +[AC_PREREQ([2.62])dnl We use AC_PATH_PROGS_FEATURE_CHECK AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl AC_BEFORE([$0], [LT_LANG])dnl AC_BEFORE([$0], [LT_OUTPUT])dnl @@ -91,7 +83,7 @@ dnl Parse OPTIONS _LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed -LIBTOOL_DEPS="$ltmain" +LIBTOOL_DEPS=$ltmain # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' @@ -111,26 +103,43 @@ dnl AC_DEFUN([AC_PROG_LIBTOOL], []) dnl AC_DEFUN([AM_PROG_LIBTOOL], []) +# _LT_PREPARE_CC_BASENAME +# ----------------------- +m4_defun([_LT_PREPARE_CC_BASENAME], [ +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +func_cc_basename () +{ + for cc_temp in @S|@*""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac + done + func_cc_basename_result=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +} +])# _LT_PREPARE_CC_BASENAME + + # _LT_CC_BASENAME(CC) # ------------------- -# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +# It would be clearer to call AC_REQUIREs from _LT_PREPARE_CC_BASENAME, +# but that macro is also expanded into generated libtool script, which +# arranges for $SED and $ECHO to be set by different means. m4_defun([_LT_CC_BASENAME], -[for cc_temp in $1""; do - case $cc_temp in - compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; - distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$ECHO "$cc_temp" | $SED "s%.*/%%; s%^$host_alias-%%"` +[m4_require([_LT_PREPARE_CC_BASENAME])dnl +AC_REQUIRE([_LT_DECL_SED])dnl +AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl +func_cc_basename $1 +cc_basename=$func_cc_basename_result ]) # _LT_FILEUTILS_DEFAULTS # ---------------------- # It is okay to use these file commands and assume they have been set -# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. +# sensibly after 'm4_require([_LT_FILEUTILS_DEFAULTS])'. m4_defun([_LT_FILEUTILS_DEFAULTS], [: ${CP="cp -f"} : ${MV="mv -f"} @@ -146,6 +155,8 @@ AC_REQUIRE([AC_CANONICAL_BUILD])dnl AC_REQUIRE([_LT_PREPARE_SED_QUOTE_VARS])dnl AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH])dnl +_LT_DECL([], [PATH_SEPARATOR], [1], [The PATH separator for the build system])dnl +dnl _LT_DECL([], [host_alias], [0], [The host system])dnl _LT_DECL([], [host], [0])dnl _LT_DECL([], [host_os], [0])dnl @@ -175,15 +186,16 @@ m4_require([_LT_CHECK_SHAREDLIB_FROM_LINKLIB])dnl m4_require([_LT_CMD_OLD_ARCHIVE])dnl m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl m4_require([_LT_WITH_SYSROOT])dnl +m4_require([_LT_CMD_TRUNCATE])dnl _LT_CONFIG_LIBTOOL_INIT([ -# See if we are running on zsh, and set the options which allow our +# See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes INIT. -if test -n "\${ZSH_VERSION+set}" ; then +if test -n "\${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi ]) -if test -n "${ZSH_VERSION+set}" ; then +if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi @@ -196,7 +208,7 @@ aix3*) # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. - if test "X${COLLECT_NAMES+set}" != Xset; then + if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi @@ -207,14 +219,14 @@ esac ofile=libtool can_build_shared=yes -# All known linkers require a `.a' archive for static linking (except MSVC, +# All known linkers require a '.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a -with_gnu_ld="$lt_cv_prog_gnu_ld" +with_gnu_ld=$lt_cv_prog_gnu_ld -old_CC="$CC" -old_CFLAGS="$CFLAGS" +old_CC=$CC +old_CFLAGS=$CFLAGS # Set sane defaults for various variables test -z "$CC" && CC=cc @@ -267,14 +279,14 @@ no_glob_subst='s/\*/\\\*/g' # _LT_PROG_LTMAIN # --------------- -# Note that this code is called both from `configure', and `config.status' +# Note that this code is called both from 'configure', and 'config.status' # now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, -# `config.status' has no value for ac_aux_dir unless we are using Automake, +# 'config.status' has no value for ac_aux_dir unless we are using Automake, # so we pass a copy along to make sure it has a sensible value anyway. m4_defun([_LT_PROG_LTMAIN], [m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl _LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) -ltmain="$ac_aux_dir/ltmain.sh" +ltmain=$ac_aux_dir/ltmain.sh ])# _LT_PROG_LTMAIN @@ -284,7 +296,7 @@ ltmain="$ac_aux_dir/ltmain.sh" # So that we can recreate a full libtool script including additional # tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS -# in macros and then make a single call at the end using the `libtool' +# in macros and then make a single call at the end using the 'libtool' # label. @@ -419,8 +431,8 @@ m4_define([_lt_decl_all_varnames], # _LT_CONFIG_STATUS_DECLARE([VARNAME]) # ------------------------------------ -# Quote a variable value, and forward it to `config.status' so that its -# declaration there will have the same value as in `configure'. VARNAME +# Quote a variable value, and forward it to 'config.status' so that its +# declaration there will have the same value as in 'configure'. VARNAME # must have a single quote delimited value for this to work. m4_define([_LT_CONFIG_STATUS_DECLARE], [$1='`$ECHO "$][$1" | $SED "$delay_single_quote_subst"`']) @@ -444,7 +456,7 @@ m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], # Output comment and list of tags supported by the script m4_defun([_LT_LIBTOOL_TAGS], [_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl -available_tags="_LT_TAGS"dnl +available_tags='_LT_TAGS'dnl ]) @@ -472,7 +484,7 @@ m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl # _LT_LIBTOOL_CONFIG_VARS # ----------------------- # Produce commented declarations of non-tagged libtool config variables -# suitable for insertion in the LIBTOOL CONFIG section of the `libtool' +# suitable for insertion in the LIBTOOL CONFIG section of the 'libtool' # script. Tagged libtool config variables (even for the LIBTOOL CONFIG # section) are produced by _LT_LIBTOOL_TAG_VARS. m4_defun([_LT_LIBTOOL_CONFIG_VARS], @@ -498,8 +510,8 @@ m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) # Send accumulated output to $CONFIG_STATUS. Thanks to the lists of # variables for single and double quote escaping we saved from calls # to _LT_DECL, we can put quote escaped variables declarations -# into `config.status', and then the shell code to quote escape them in -# for loops in `config.status'. Finally, any additional code accumulated +# into 'config.status', and then the shell code to quote escape them in +# for loops in 'config.status'. Finally, any additional code accumulated # from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. m4_defun([_LT_CONFIG_COMMANDS], [AC_PROVIDE_IFELSE([LT_OUTPUT], @@ -545,7 +557,7 @@ for var in lt_decl_all_varnames([[ \ ]], lt_decl_quote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) - eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" @@ -558,7 +570,7 @@ for var in lt_decl_all_varnames([[ \ ]], lt_decl_dquote_varnames); do case \`eval \\\\\$ECHO \\\\""\\\\\$\$var"\\\\"\` in *[[\\\\\\\`\\"\\\$]]*) - eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"\\\$\$var\\" | \\\$SED -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ## exclude from sc_prohibit_nested_quotes ;; *) eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" @@ -574,7 +586,7 @@ _LT_OUTPUT_LIBTOOL_INIT # Generate a child script FILE with all initialization necessary to # reuse the environment learned by the parent script, and make the # file executable. If COMMENT is supplied, it is inserted after the -# `#!' sequence but before initialization text begins. After this +# '#!' sequence but before initialization text begins. After this # macro, additional text can be appended to FILE to form the body of # the child script. The macro ends with non-zero status if the # file could not be fully written (such as if the disk is full). @@ -596,7 +608,7 @@ AS_SHELL_SANITIZE _AS_PREPARE exec AS_MESSAGE_FD>&1 _ASEOF -test $lt_write_fail = 0 && chmod +x $1[]dnl +test 0 = "$lt_write_fail" && chmod +x $1[]dnl m4_popdef([AS_MESSAGE_LOG_FD])])])# _LT_GENERATED_FILE_INIT # LT_OUTPUT @@ -619,7 +631,7 @@ exec AS_MESSAGE_LOG_FD>>config.log } >&AS_MESSAGE_LOG_FD lt_cl_help="\ -\`$as_me' creates a local libtool stub from the current configuration, +'$as_me' creates a local libtool stub from the current configuration, for use in further configure time tests before the real libtool is generated. @@ -637,11 +649,11 @@ m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) configured by $[0], generated by m4_PACKAGE_STRING. -Copyright (C) 2010 Free Software Foundation, Inc. +Copyright (C) 2011 Free Software Foundation, Inc. This config.lt script is free software; the Free Software Foundation gives unlimited permision to copy, distribute and modify it." -while test $[#] != 0 +while test 0 != $[#] do case $[1] in --version | --v* | -V ) @@ -654,10 +666,10 @@ do lt_cl_silent=: ;; -*) AC_MSG_ERROR([unrecognized option: $[1] -Try \`$[0] --help' for more information.]) ;; +Try '$[0] --help' for more information.]) ;; *) AC_MSG_ERROR([unrecognized argument: $[1] -Try \`$[0] --help' for more information.]) ;; +Try '$[0] --help' for more information.]) ;; esac shift done @@ -683,7 +695,7 @@ chmod +x "$CONFIG_LT" # open by configure. Here we exec the FD to /dev/null, effectively closing # config.log, so it can be properly (re)opened and appended to by config.lt. lt_cl_success=: -test "$silent" = yes && +test yes = "$silent" && lt_config_lt_args="$lt_config_lt_args --quiet" exec AS_MESSAGE_LOG_FD>/dev/null $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false @@ -703,32 +715,46 @@ m4_defun([_LT_CONFIG], _LT_CONFIG_SAVE_COMMANDS([ m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl m4_if(_LT_TAG, [C], [ - # See if we are running on zsh, and set the options which allow our + # See if we are running on zsh, and set the options that allow our # commands through without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then + if test -n "${ZSH_VERSION+set}"; then setopt NO_GLOB_SUBST fi - cfgfile="${ofile}T" + cfgfile=${ofile}T trap "$RM \"$cfgfile\"; exit 1" 1 2 15 $RM "$cfgfile" cat <<_LT_EOF >> "$cfgfile" #! $SHELL - -# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. -# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# Generated automatically by $as_me ($PACKAGE) $VERSION # NOTE: Changes made to this file will be lost: look at ltmain.sh. -# + +# Provide generalized library-building support services. +# Written by Gordon Matzigkeit, 1996 + _LT_COPYING _LT_LIBTOOL_TAGS +# Configured defaults for sys_lib_dlsearch_path munging. +: \${LT_SYS_LIBRARY_PATH="$configure_time_lt_sys_library_path"} + # ### BEGIN LIBTOOL CONFIG _LT_LIBTOOL_CONFIG_VARS _LT_LIBTOOL_TAG_VARS # ### END LIBTOOL CONFIG +_LT_EOF + + cat <<'_LT_EOF' >> "$cfgfile" + +# ### BEGIN FUNCTIONS SHARED WITH CONFIGURE + +_LT_PREPARE_MUNGE_PATH_LIST +_LT_PREPARE_CC_BASENAME + +# ### END FUNCTIONS SHARED WITH CONFIGURE + _LT_EOF case $host_os in @@ -737,7 +763,7 @@ _LT_EOF # AIX sometimes has problems with the GCC collect2 program. For some # reason, if we set the COLLECT_NAMES environment variable, the problems # vanish in a puff of smoke. -if test "X${COLLECT_NAMES+set}" != Xset; then +if test set != "${COLLECT_NAMES+set}"; then COLLECT_NAMES= export COLLECT_NAMES fi @@ -754,8 +780,6 @@ _LT_EOF sed '$q' "$ltmain" >> "$cfgfile" \ || (rm -f "$cfgfile"; exit 1) - _LT_PROG_REPLACE_SHELLFNS - mv -f "$cfgfile" "$ofile" || (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") chmod +x "$ofile" @@ -773,7 +797,6 @@ _LT_EOF [m4_if([$1], [], [ PACKAGE='$PACKAGE' VERSION='$VERSION' - TIMESTAMP='$TIMESTAMP' RM='$RM' ofile='$ofile'], []) ])dnl /_LT_CONFIG_SAVE_COMMANDS @@ -801,6 +824,7 @@ AC_DEFUN([LT_LANG], m4_case([$1], [C], [_LT_LANG(C)], [C++], [_LT_LANG(CXX)], + [Go], [_LT_LANG(GO)], [Java], [_LT_LANG(GCJ)], [Fortran 77], [_LT_LANG(F77)], [Fortran], [_LT_LANG(FC)], @@ -822,6 +846,31 @@ m4_defun([_LT_LANG], ])# _LT_LANG +m4_ifndef([AC_PROG_GO], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_GO. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ +m4_defun([AC_PROG_GO], +[AC_LANG_PUSH(Go)dnl +AC_ARG_VAR([GOC], [Go compiler command])dnl +AC_ARG_VAR([GOFLAGS], [Go compiler flags])dnl +_AC_ARG_VAR_LDFLAGS()dnl +AC_CHECK_TOOL(GOC, gccgo) +if test -z "$GOC"; then + if test -n "$ac_tool_prefix"; then + AC_CHECK_PROG(GOC, [${ac_tool_prefix}gccgo], [${ac_tool_prefix}gccgo]) + fi +fi +if test -z "$GOC"; then + AC_CHECK_PROG(GOC, gccgo, gccgo, false) +fi +])#m4_defun +])#m4_ifndef + + # _LT_LANG_DEFAULT_CONFIG # ----------------------- m4_defun([_LT_LANG_DEFAULT_CONFIG], @@ -852,6 +901,10 @@ AC_PROVIDE_IFELSE([AC_PROG_GCJ], m4_ifdef([LT_PROG_GCJ], [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) +AC_PROVIDE_IFELSE([AC_PROG_GO], + [LT_LANG(GO)], + [m4_define([AC_PROG_GO], defn([AC_PROG_GO])[LT_LANG(GO)])]) + AC_PROVIDE_IFELSE([LT_PROG_RC], [LT_LANG(RC)], [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) @@ -942,7 +995,7 @@ m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], [lt_cv_apple_cc_single_mod=no - if test -z "${LT_MULTI_MODULE}"; then + if test -z "$LT_MULTI_MODULE"; then # By default we will add the -single_module flag. You can override # by either setting the environment variable LT_MULTI_MODULE # non-empty at configure time, or by adding -multi_module to the @@ -954,7 +1007,13 @@ m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ -dynamiclib -Wl,-single_module conftest.c 2>conftest.err _lt_result=$? - if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then + # If there is a non-empty error log, and "single_module" + # appears in it, assume the flag caused a linker warning + if test -s conftest.err && $GREP single_module conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + # Otherwise, if the output was created with a 0 exit code from + # the compiler, it worked. + elif test -f libconftest.dylib && test 0 = "$_lt_result"; then lt_cv_apple_cc_single_mod=yes else cat conftest.err >&AS_MESSAGE_LOG_FD @@ -962,6 +1021,7 @@ m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ rm -rf libconftest.dylib* rm -f conftest.* fi]) + AC_CACHE_CHECK([for -exported_symbols_list linker flag], [lt_cv_ld_exported_symbols_list], [lt_cv_ld_exported_symbols_list=no @@ -971,8 +1031,9 @@ m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], [lt_cv_ld_exported_symbols_list=yes], [lt_cv_ld_exported_symbols_list=no]) - LDFLAGS="$save_LDFLAGS" + LDFLAGS=$save_LDFLAGS ]) + AC_CACHE_CHECK([for -force_load linker flag],[lt_cv_ld_force_load], [lt_cv_ld_force_load=no cat > conftest.c << _LT_EOF @@ -980,8 +1041,8 @@ int forced_loaded() { return 2;} _LT_EOF echo "$LTCC $LTCFLAGS -c -o conftest.o conftest.c" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS -c -o conftest.o conftest.c 2>&AS_MESSAGE_LOG_FD - echo "$AR cru libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD - $AR cru libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD + echo "$AR cr libconftest.a conftest.o" >&AS_MESSAGE_LOG_FD + $AR cr libconftest.a conftest.o 2>&AS_MESSAGE_LOG_FD echo "$RANLIB libconftest.a" >&AS_MESSAGE_LOG_FD $RANLIB libconftest.a 2>&AS_MESSAGE_LOG_FD cat > conftest.c << _LT_EOF @@ -990,7 +1051,9 @@ _LT_EOF echo "$LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a" >&AS_MESSAGE_LOG_FD $LTCC $LTCFLAGS $LDFLAGS -o conftest conftest.c -Wl,-force_load,./libconftest.a 2>conftest.err _lt_result=$? - if test -f conftest && test ! -s conftest.err && test $_lt_result = 0 && $GREP forced_load conftest 2>&1 >/dev/null; then + if test -s conftest.err && $GREP force_load conftest.err; then + cat conftest.err >&AS_MESSAGE_LOG_FD + elif test -f conftest && test 0 = "$_lt_result" && $GREP forced_load conftest >/dev/null 2>&1; then lt_cv_ld_force_load=yes else cat conftest.err >&AS_MESSAGE_LOG_FD @@ -1000,32 +1063,32 @@ _LT_EOF ]) case $host_os in rhapsody* | darwin1.[[012]]) - _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;; darwin1.*) - _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; darwin*) # darwin 5.x on # if running on 10.5 or later, the deployment target defaults # to the OS version, if on x86, and 10.4, the deployment # target defaults to 10.4. Don't you love it? case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in - 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) - _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; - 10.[[012]]*) - _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; - 10.*) - _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.0,*86*-darwin8*|10.0,*-darwin[[912]]*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; + 10.[[012]][[,.]]*) + _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;; + 10.*|11.*) + _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;; esac ;; esac - if test "$lt_cv_apple_cc_single_mod" = "yes"; then + if test yes = "$lt_cv_apple_cc_single_mod"; then _lt_dar_single_mod='$single_module' fi - if test "$lt_cv_ld_exported_symbols_list" = "yes"; then - _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + if test yes = "$lt_cv_ld_exported_symbols_list"; then + _lt_dar_export_syms=' $wl-exported_symbols_list,$output_objdir/$libname-symbols.expsym' else - _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/$libname-symbols.expsym $lib' fi - if test "$DSYMUTIL" != ":" && test "$lt_cv_ld_force_load" = "no"; then + if test : != "$DSYMUTIL" && test no = "$lt_cv_ld_force_load"; then _lt_dsymutil='~$DSYMUTIL $lib || :' else _lt_dsymutil= @@ -1035,8 +1098,8 @@ _LT_EOF ]) -# _LT_DARWIN_LINKER_FEATURES -# -------------------------- +# _LT_DARWIN_LINKER_FEATURES([TAG]) +# --------------------------------- # Checks for linker and compiler features on darwin m4_defun([_LT_DARWIN_LINKER_FEATURES], [ @@ -1045,27 +1108,29 @@ m4_defun([_LT_DARWIN_LINKER_FEATURES], _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported - if test "$lt_cv_ld_force_load" = "yes"; then - _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience ${wl}-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + if test yes = "$lt_cv_ld_force_load"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience $wl-force_load,$conv\"; done; func_echo_all \"$new_convenience\"`' + m4_case([$1], [F77], [_LT_TAGVAR(compiler_needs_object, $1)=yes], + [FC], [_LT_TAGVAR(compiler_needs_object, $1)=yes]) else _LT_TAGVAR(whole_archive_flag_spec, $1)='' fi _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" + _LT_TAGVAR(allow_undefined_flag, $1)=$_lt_dar_allow_undefined case $cc_basename in - ifort*) _lt_dar_can_shared=yes ;; + ifort*|nagfor*) _lt_dar_can_shared=yes ;; *) _lt_dar_can_shared=$GCC ;; esac - if test "$_lt_dar_can_shared" = "yes"; then + if test yes = "$_lt_dar_can_shared"; then output_verbose_link_cmd=func_echo_all - _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" - _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" - _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" - _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dsymutil" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod$_lt_dar_export_syms$_lt_dsymutil" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags$_lt_dar_export_syms$_lt_dsymutil" m4_if([$1], [CXX], -[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then - _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" - _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" +[ if test yes != "$lt_cv_apple_cc_single_mod"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dsymutil" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's|^|_|' < \$export_symbols > \$output_objdir/\$libname-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \$lib-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$lib-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring$_lt_dar_export_syms$_lt_dsymutil" fi ],[]) else @@ -1085,7 +1150,7 @@ m4_defun([_LT_DARWIN_LINKER_FEATURES], # Allow to override them for all tags through lt_cv_aix_libpath. m4_defun([_LT_SYS_MODULE_PATH_AIX], [m4_require([_LT_DECL_SED])dnl -if test "${lt_cv_aix_libpath+set}" = set; then +if test set = "${lt_cv_aix_libpath+set}"; then aix_libpath=$lt_cv_aix_libpath else AC_CACHE_VAL([_LT_TAGVAR([lt_cv_aix_libpath_], [$1])], @@ -1103,7 +1168,7 @@ else _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` fi],[]) if test -z "$_LT_TAGVAR([lt_cv_aix_libpath_], [$1])"; then - _LT_TAGVAR([lt_cv_aix_libpath_], [$1])="/usr/lib:/lib" + _LT_TAGVAR([lt_cv_aix_libpath_], [$1])=/usr/lib:/lib fi ]) aix_libpath=$_LT_TAGVAR([lt_cv_aix_libpath_], [$1]) @@ -1123,8 +1188,8 @@ m4_define([_LT_SHELL_INIT], # ----------------------- # Find how we can fake an echo command that does not interpret backslash. # In particular, with Autoconf 2.60 or later we add some code to the start -# of the generated configure script which will find a shell with a builtin -# printf (which we can use as an echo command). +# of the generated configure script that will find a shell with a builtin +# printf (that we can use as an echo command). m4_defun([_LT_PROG_ECHO_BACKSLASH], [ECHO='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' ECHO=$ECHO$ECHO$ECHO$ECHO$ECHO @@ -1152,10 +1217,10 @@ fi # Invoke $ECHO with all args, space-separated. func_echo_all () { - $ECHO "$*" + $ECHO "$*" } -case "$ECHO" in +case $ECHO in printf*) AC_MSG_RESULT([printf]) ;; print*) AC_MSG_RESULT([print -r]) ;; *) AC_MSG_RESULT([cat]) ;; @@ -1181,16 +1246,17 @@ _LT_DECL([], [ECHO], [1], [An echo program that protects backslashes]) AC_DEFUN([_LT_WITH_SYSROOT], [AC_MSG_CHECKING([for sysroot]) AC_ARG_WITH([sysroot], -[ --with-sysroot[=DIR] Search for dependent libraries within DIR - (or the compiler's sysroot if not specified).], +[AS_HELP_STRING([--with-sysroot@<:@=DIR@:>@], + [Search for dependent libraries within DIR (or the compiler's sysroot + if not specified).])], [], [with_sysroot=no]) dnl lt_sysroot will always be passed unquoted. We quote it here dnl in case the user passed a directory name. lt_sysroot= -case ${with_sysroot} in #( +case $with_sysroot in #( yes) - if test "$GCC" = yes; then + if test yes = "$GCC"; then lt_sysroot=`$CC --print-sysroot 2>/dev/null` fi ;; #( @@ -1200,14 +1266,14 @@ case ${with_sysroot} in #( no|'') ;; #( *) - AC_MSG_RESULT([${with_sysroot}]) + AC_MSG_RESULT([$with_sysroot]) AC_MSG_ERROR([The sysroot must be an absolute path.]) ;; esac AC_MSG_RESULT([${lt_sysroot:-no}]) _LT_DECL([], [lt_sysroot], [0], [The root where to search for ]dnl -[dependent libraries, and in which our libraries should be installed.])]) +[dependent libraries, and where our libraries should be installed.])]) # _LT_ENABLE_LOCK # --------------- @@ -1215,31 +1281,33 @@ m4_defun([_LT_ENABLE_LOCK], [AC_ARG_ENABLE([libtool-lock], [AS_HELP_STRING([--disable-libtool-lock], [avoid locking (might break parallel builds)])]) -test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes +test no = "$enable_libtool_lock" || enable_libtool_lock=yes # Some flags need to be propagated to the compiler or linker for good # libtool support. case $host in ia64-*-hpux*) - # Find out which ABI we are using. + # Find out what ABI is being produced by ac_compile, and set mode + # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.$ac_objext` in *ELF-32*) - HPUX_IA64_MODE="32" + HPUX_IA64_MODE=32 ;; *ELF-64*) - HPUX_IA64_MODE="64" + HPUX_IA64_MODE=64 ;; esac fi rm -rf conftest* ;; *-*-irix6*) - # Find out which ABI we are using. + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then - if test "$lt_cv_prog_gnu_ld" = yes; then + if test yes = "$lt_cv_prog_gnu_ld"; then case `/usr/bin/file conftest.$ac_objext` in *32-bit*) LD="${LD-ld} -melf32bsmip" @@ -1268,9 +1336,46 @@ ia64-*-hpux*) rm -rf conftest* ;; -x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \ +mips64*-*linux*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. + echo '[#]line '$LINENO' "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + emul=elf + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + emul="${emul}32" + ;; + *64-bit*) + emul="${emul}64" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *MSB*) + emul="${emul}btsmip" + ;; + *LSB*) + emul="${emul}ltsmip" + ;; + esac + case `/usr/bin/file conftest.$ac_objext` in + *N32*) + emul="${emul}n32" + ;; + esac + LD="${LD-ld} -m $emul" + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|powerpc*-*linux*| \ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) - # Find out which ABI we are using. + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. Note that the listed cases only cover the + # situations where additional linker options are needed (such as when + # doing 32-bit compilation for a host where ld defaults to 64-bit, or + # vice versa); the common cases where no linker options are needed do + # not appear in the list. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in @@ -1280,9 +1385,19 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) LD="${LD-ld} -m elf_i386_fbsd" ;; x86_64-*linux*) - LD="${LD-ld} -m elf_i386" + case `/usr/bin/file conftest.o` in + *x86-64*) + LD="${LD-ld} -m elf32_x86_64" + ;; + *) + LD="${LD-ld} -m elf_i386" + ;; + esac ;; - ppc64-*linux*|powerpc64-*linux*) + powerpc64le-*linux*) + LD="${LD-ld} -m elf32lppclinux" + ;; + powerpc64-*linux*) LD="${LD-ld} -m elf32ppclinux" ;; s390x-*linux*) @@ -1301,7 +1416,10 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) x86_64-*linux*) LD="${LD-ld} -m elf_x86_64" ;; - ppc*-*linux*|powerpc*-*linux*) + powerpcle-*linux*) + LD="${LD-ld} -m elf64lppc" + ;; + powerpc-*linux*) LD="${LD-ld} -m elf64ppc" ;; s390*-*linux*|s390*-*tpf*) @@ -1319,25 +1437,39 @@ s390*-*linux*|s390*-*tpf*|sparc*-*linux*) *-*-sco3.2v5*) # On SCO OpenServer 5, we need -belf to get full-featured binaries. - SAVE_CFLAGS="$CFLAGS" + SAVE_CFLAGS=$CFLAGS CFLAGS="$CFLAGS -belf" AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, [AC_LANG_PUSH(C) AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) AC_LANG_POP]) - if test x"$lt_cv_cc_needs_belf" != x"yes"; then + if test yes != "$lt_cv_cc_needs_belf"; then # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf - CFLAGS="$SAVE_CFLAGS" + CFLAGS=$SAVE_CFLAGS fi ;; -sparc*-*solaris*) - # Find out which ABI we are using. +*-*solaris*) + # Find out what ABI is being produced by ac_compile, and set linker + # options accordingly. echo 'int i;' > conftest.$ac_ext if AC_TRY_EVAL(ac_compile); then case `/usr/bin/file conftest.o` in *64-bit*) case $lt_cv_prog_gnu_ld in - yes*) LD="${LD-ld} -m elf64_sparc" ;; + yes*) + case $host in + i?86-*-solaris*|x86_64-*-solaris*) + LD="${LD-ld} -m elf_x86_64" + ;; + sparc*-*-solaris*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + # GNU ld 2.21 introduced _sol2 emulations. Use them if available. + if ${LD-ld} -V | grep _sol2 >/dev/null 2>&1; then + LD=${LD-ld}_sol2 + fi + ;; *) if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then LD="${LD-ld} -64" @@ -1351,7 +1483,7 @@ sparc*-*solaris*) ;; esac -need_locks="$enable_libtool_lock" +need_locks=$enable_libtool_lock ])# _LT_ENABLE_LOCK @@ -1360,7 +1492,7 @@ need_locks="$enable_libtool_lock" m4_defun([_LT_PROG_AR], [AC_CHECK_TOOLS(AR, [ar], false) : ${AR=ar} -: ${AR_FLAGS=cru} +: ${AR_FLAGS=cr} _LT_DECL([], [AR], [1], [The archiver]) _LT_DECL([], [AR_FLAGS], [1], [Flags to create an archive]) @@ -1370,11 +1502,11 @@ AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], [echo conftest.$ac_objext > conftest.lst lt_ar_try='$AR $AR_FLAGS libconftest.a @conftest.lst >&AS_MESSAGE_LOG_FD' AC_TRY_EVAL([lt_ar_try]) - if test "$ac_status" -eq 0; then + if test 0 -eq "$ac_status"; then # Ensure the archiver fails upon bogus file names. rm -f conftest.$ac_objext libconftest.a AC_TRY_EVAL([lt_ar_try]) - if test "$ac_status" -ne 0; then + if test 0 -ne "$ac_status"; then lt_cv_ar_at_file=@ fi fi @@ -1382,7 +1514,7 @@ AC_CACHE_CHECK([for archiver @FILE support], [lt_cv_ar_at_file], ]) ]) -if test "x$lt_cv_ar_at_file" = xno; then +if test no = "$lt_cv_ar_at_file"; then archiver_list_spec= else archiver_list_spec=$lt_cv_ar_at_file @@ -1413,14 +1545,14 @@ old_postuninstall_cmds= if test -n "$RANLIB"; then case $host_os in - openbsd*) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" + bitrig* | openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$tool_oldlib" ;; *) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$tool_oldlib" ;; esac - old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" + old_archive_cmds="$old_archive_cmds~\$RANLIB \$tool_oldlib" fi case $host_os in @@ -1449,7 +1581,7 @@ AC_CACHE_CHECK([$1], [$2], [$2=no m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) echo "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$3" + lt_compiler_flag="$3" ## exclude from sc_useless_quotes_in_assignment # Insert the option either (1) after the last *FLAGS variable, or # (2) before a word containing "conftest.", or (3) at the end. # Note that $ac_compile itself does not contain backslashes and begins @@ -1476,7 +1608,7 @@ AC_CACHE_CHECK([$1], [$2], $RM conftest* ]) -if test x"[$]$2" = xyes; then +if test yes = "[$]$2"; then m4_if([$5], , :, [$5]) else m4_if([$6], , :, [$6]) @@ -1498,7 +1630,7 @@ AC_DEFUN([_LT_LINKER_OPTION], m4_require([_LT_DECL_SED])dnl AC_CACHE_CHECK([$1], [$2], [$2=no - save_LDFLAGS="$LDFLAGS" + save_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $3" echo "$lt_simple_link_test_code" > conftest.$ac_ext if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then @@ -1517,10 +1649,10 @@ AC_CACHE_CHECK([$1], [$2], fi fi $RM -r conftest* - LDFLAGS="$save_LDFLAGS" + LDFLAGS=$save_LDFLAGS ]) -if test x"[$]$2" = xyes; then +if test yes = "[$]$2"; then m4_if([$4], , :, [$4]) else m4_if([$5], , :, [$5]) @@ -1541,7 +1673,7 @@ AC_DEFUN([LT_CMD_MAX_LEN], AC_MSG_CHECKING([the maximum length of command line arguments]) AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl i=0 - teststring="ABCD" + teststring=ABCD case $build_os in msdosdjgpp*) @@ -1581,7 +1713,7 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl lt_cv_sys_max_cmd_len=8192; ;; - netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + bitrig* | darwin* | dragonfly* | freebsd* | netbsd* | openbsd*) # This has been around since 386BSD, at least. Likely further. if test -x /sbin/sysctl; then lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` @@ -1600,6 +1732,11 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl lt_cv_sys_max_cmd_len=196608 ;; + os2*) + # The test takes a long time on OS/2. + lt_cv_sys_max_cmd_len=8192 + ;; + osf*) # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not @@ -1626,22 +1763,23 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl ;; *) lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` - if test -n "$lt_cv_sys_max_cmd_len"; then + if test -n "$lt_cv_sys_max_cmd_len" && \ + test undefined != "$lt_cv_sys_max_cmd_len"; then lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` else # Make teststring a little bigger before we do anything with it. # a 1K string should be a reasonable start. - for i in 1 2 3 4 5 6 7 8 ; do + for i in 1 2 3 4 5 6 7 8; do teststring=$teststring$teststring done SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} # If test is not a shell built-in, we'll probably end up computing a # maximum length that is only half of the actual maximum length, but # we can't tell. - while { test "X"`func_fallback_echo "$teststring$teststring" 2>/dev/null` \ + while { test X`env echo "$teststring$teststring" 2>/dev/null` \ = "X$teststring$teststring"; } >/dev/null 2>&1 && - test $i != 17 # 1/2 MB should be enough + test 17 != "$i" # 1/2 MB should be enough do i=`expr $i + 1` teststring=$teststring$teststring @@ -1657,7 +1795,7 @@ AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl ;; esac ]) -if test -n $lt_cv_sys_max_cmd_len ; then +if test -n "$lt_cv_sys_max_cmd_len"; then AC_MSG_RESULT($lt_cv_sys_max_cmd_len) else AC_MSG_RESULT(none) @@ -1685,7 +1823,7 @@ m4_defun([_LT_HEADER_DLFCN], # ---------------------------------------------------------------- m4_defun([_LT_TRY_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl -if test "$cross_compiling" = yes; then : +if test yes = "$cross_compiling"; then : [$4] else lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 @@ -1732,9 +1870,9 @@ else # endif #endif -/* When -fvisbility=hidden is used, assume the code has been annotated +/* When -fvisibility=hidden is used, assume the code has been annotated correspondingly for the symbols needed. */ -#if defined(__GNUC__) && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) +#if defined __GNUC__ && (((__GNUC__ == 3) && (__GNUC_MINOR__ >= 3)) || (__GNUC__ > 3)) int fnord () __attribute__((visibility("default"))); #endif @@ -1760,7 +1898,7 @@ int main () return status; }] _LT_EOF - if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then + if AC_TRY_EVAL(ac_link) && test -s "conftest$ac_exeext" 2>/dev/null; then (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null lt_status=$? case x$lt_status in @@ -1781,7 +1919,7 @@ rm -fr conftest* # ------------------ AC_DEFUN([LT_SYS_DLOPEN_SELF], [m4_require([_LT_HEADER_DLFCN])dnl -if test "x$enable_dlopen" != xyes; then +if test yes != "$enable_dlopen"; then enable_dlopen=unknown enable_dlopen_self=unknown enable_dlopen_self_static=unknown @@ -1791,44 +1929,52 @@ else case $host_os in beos*) - lt_cv_dlopen="load_add_on" + lt_cv_dlopen=load_add_on lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ;; mingw* | pw32* | cegcc*) - lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen=LoadLibrary lt_cv_dlopen_libs= ;; cygwin*) - lt_cv_dlopen="dlopen" + lt_cv_dlopen=dlopen lt_cv_dlopen_libs= ;; darwin*) - # if libdl is installed we need to link against it + # if libdl is installed we need to link against it AC_CHECK_LIB([dl], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ - lt_cv_dlopen="dyld" + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl],[ + lt_cv_dlopen=dyld lt_cv_dlopen_libs= lt_cv_dlopen_self=yes ]) ;; + tpf*) + # Don't try to run any link tests for TPF. We know it's impossible + # because TPF is a cross-compiler, and we know how we open DSOs. + lt_cv_dlopen=dlopen + lt_cv_dlopen_libs= + lt_cv_dlopen_self=no + ;; + *) AC_CHECK_FUNC([shl_load], - [lt_cv_dlopen="shl_load"], + [lt_cv_dlopen=shl_load], [AC_CHECK_LIB([dld], [shl_load], - [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], + [lt_cv_dlopen=shl_load lt_cv_dlopen_libs=-ldld], [AC_CHECK_FUNC([dlopen], - [lt_cv_dlopen="dlopen"], + [lt_cv_dlopen=dlopen], [AC_CHECK_LIB([dl], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-ldl], [AC_CHECK_LIB([svld], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], + [lt_cv_dlopen=dlopen lt_cv_dlopen_libs=-lsvld], [AC_CHECK_LIB([dld], [dld_link], - [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) + [lt_cv_dlopen=dld_link lt_cv_dlopen_libs=-ldld]) ]) ]) ]) @@ -1837,21 +1983,21 @@ else ;; esac - if test "x$lt_cv_dlopen" != xno; then - enable_dlopen=yes - else + if test no = "$lt_cv_dlopen"; then enable_dlopen=no + else + enable_dlopen=yes fi case $lt_cv_dlopen in dlopen) - save_CPPFLAGS="$CPPFLAGS" - test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + save_CPPFLAGS=$CPPFLAGS + test yes = "$ac_cv_header_dlfcn_h" && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" - save_LDFLAGS="$LDFLAGS" + save_LDFLAGS=$LDFLAGS wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" - save_LIBS="$LIBS" + save_LIBS=$LIBS LIBS="$lt_cv_dlopen_libs $LIBS" AC_CACHE_CHECK([whether a program can dlopen itself], @@ -1861,7 +2007,7 @@ else lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) ]) - if test "x$lt_cv_dlopen_self" = xyes; then + if test yes = "$lt_cv_dlopen_self"; then wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" AC_CACHE_CHECK([whether a statically linked program can dlopen itself], lt_cv_dlopen_self_static, [dnl @@ -1871,9 +2017,9 @@ else ]) fi - CPPFLAGS="$save_CPPFLAGS" - LDFLAGS="$save_LDFLAGS" - LIBS="$save_LIBS" + CPPFLAGS=$save_CPPFLAGS + LDFLAGS=$save_LDFLAGS + LIBS=$save_LIBS ;; esac @@ -1965,8 +2111,8 @@ m4_defun([_LT_COMPILER_FILE_LOCKS], m4_require([_LT_FILEUTILS_DEFAULTS])dnl _LT_COMPILER_C_O([$1]) -hard_links="nottested" -if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then +hard_links=nottested +if test no = "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" && test no != "$need_locks"; then # do not overwrite the value of need_locks provided by the user AC_MSG_CHECKING([if we can lock with hard links]) hard_links=yes @@ -1976,8 +2122,8 @@ if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != ln conftest.a conftest.b 2>&5 || hard_links=no ln conftest.a conftest.b 2>/dev/null && hard_links=no AC_MSG_RESULT([$hard_links]) - if test "$hard_links" = no; then - AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) + if test no = "$hard_links"; then + AC_MSG_WARN(['$CC' does not support '-c -o', so 'make -j' may be unsafe]) need_locks=warn fi else @@ -2004,8 +2150,8 @@ objdir=$lt_cv_objdir _LT_DECL([], [objdir], [0], [The name of the directory that contains temporary libtool files])dnl m4_pattern_allow([LT_OBJDIR])dnl -AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", - [Define to the sub-directory in which libtool stores uninstalled libraries.]) +AC_DEFINE_UNQUOTED([LT_OBJDIR], "$lt_cv_objdir/", + [Define to the sub-directory where libtool stores uninstalled libraries.]) ])# _LT_CHECK_OBJDIR @@ -2017,15 +2163,15 @@ m4_defun([_LT_LINKER_HARDCODE_LIBPATH], _LT_TAGVAR(hardcode_action, $1)= if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || test -n "$_LT_TAGVAR(runpath_var, $1)" || - test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then + test yes = "$_LT_TAGVAR(hardcode_automatic, $1)"; then # We can hardcode non-existent directories. - if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && + if test no != "$_LT_TAGVAR(hardcode_direct, $1)" && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one - ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && - test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then + ## test no != "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" && + test no != "$_LT_TAGVAR(hardcode_minus_L, $1)"; then # Linking always hardcodes the temporary library directory. _LT_TAGVAR(hardcode_action, $1)=relink else @@ -2039,12 +2185,12 @@ else fi AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) -if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || - test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then +if test relink = "$_LT_TAGVAR(hardcode_action, $1)" || + test yes = "$_LT_TAGVAR(inherit_rpath, $1)"; then # Fast installation is not supported enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then +elif test yes = "$shlibpath_overrides_runpath" || + test no = "$enable_shared"; then # Fast installation is not necessary enable_fast_install=needless fi @@ -2068,7 +2214,7 @@ else # FIXME - insert some real tests, host_os isn't really good enough case $host_os in darwin*) - if test -n "$STRIP" ; then + if test -n "$STRIP"; then striplib="$STRIP -x" old_striplib="$STRIP -S" AC_MSG_RESULT([yes]) @@ -2086,6 +2232,47 @@ _LT_DECL([], [striplib], [1]) ])# _LT_CMD_STRIPLIB +# _LT_PREPARE_MUNGE_PATH_LIST +# --------------------------- +# Make sure func_munge_path_list() is defined correctly. +m4_defun([_LT_PREPARE_MUNGE_PATH_LIST], +[[# func_munge_path_list VARIABLE PATH +# ----------------------------------- +# VARIABLE is name of variable containing _space_ separated list of +# directories to be munged by the contents of PATH, which is string +# having a format: +# "DIR[:DIR]:" +# string "DIR[ DIR]" will be prepended to VARIABLE +# ":DIR[:DIR]" +# string "DIR[ DIR]" will be appended to VARIABLE +# "DIRP[:DIRP]::[DIRA:]DIRA" +# string "DIRP[ DIRP]" will be prepended to VARIABLE and string +# "DIRA[ DIRA]" will be appended to VARIABLE +# "DIR[:DIR]" +# VARIABLE will be replaced by "DIR[ DIR]" +func_munge_path_list () +{ + case x@S|@2 in + x) + ;; + *:) + eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'` \@S|@@S|@1\" + ;; + x:*) + eval @S|@1=\"\@S|@@S|@1 `$ECHO @S|@2 | $SED 's/:/ /g'`\" + ;; + *::*) + eval @S|@1=\"\@S|@@S|@1\ `$ECHO @S|@2 | $SED -e 's/.*:://' -e 's/:/ /g'`\" + eval @S|@1=\"`$ECHO @S|@2 | $SED -e 's/::.*//' -e 's/:/ /g'`\ \@S|@@S|@1\" + ;; + *) + eval @S|@1=\"`$ECHO @S|@2 | $SED 's/:/ /g'`\" + ;; + esac +} +]])# _LT_PREPARE_PATH_LIST + + # _LT_SYS_DYNAMIC_LINKER([TAG]) # ----------------------------- # PORTME Fill in your ld.so characteristics @@ -2096,17 +2283,18 @@ m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_OBJDUMP])dnl m4_require([_LT_DECL_SED])dnl m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_PREPARE_MUNGE_PATH_LIST])dnl AC_MSG_CHECKING([dynamic linker characteristics]) m4_if([$1], [], [ -if test "$GCC" = yes; then +if test yes = "$GCC"; then case $host_os in - darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; - *) lt_awk_arg="/^libraries:/" ;; + darwin*) lt_awk_arg='/^libraries:/,/LR/' ;; + *) lt_awk_arg='/^libraries:/' ;; esac case $host_os in - mingw* | cegcc*) lt_sed_strip_eq="s,=\([[A-Za-z]]:\),\1,g" ;; - *) lt_sed_strip_eq="s,=/,/,g" ;; + mingw* | cegcc*) lt_sed_strip_eq='s|=\([[A-Za-z]]:\)|\1|g' ;; + *) lt_sed_strip_eq='s|=/|/|g' ;; esac lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e $lt_sed_strip_eq` case $lt_search_path_spec in @@ -2122,28 +2310,35 @@ if test "$GCC" = yes; then ;; esac # Ok, now we have the path, separated by spaces, we can step through it - # and add multilib dir if necessary. + # and add multilib dir if necessary... lt_tmp_lt_search_path_spec= - lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + lt_multi_os_dir=/`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + # ...but if some path component already ends with the multilib dir we assume + # that all is fine and trust -print-search-dirs as is (GCC 4.2? or newer). + case "$lt_multi_os_dir; $lt_search_path_spec " in + "/; "* | "/.; "* | "/./; "* | *"$lt_multi_os_dir "* | *"$lt_multi_os_dir/ "*) + lt_multi_os_dir= + ;; + esac for lt_sys_path in $lt_search_path_spec; do - if test -d "$lt_sys_path/$lt_multi_os_dir"; then - lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" - else + if test -d "$lt_sys_path$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path$lt_multi_os_dir" + elif test -n "$lt_multi_os_dir"; then test -d "$lt_sys_path" && \ lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" fi done lt_search_path_spec=`$ECHO "$lt_tmp_lt_search_path_spec" | awk ' -BEGIN {RS=" "; FS="/|\n";} { - lt_foo=""; - lt_count=0; +BEGIN {RS = " "; FS = "/|\n";} { + lt_foo = ""; + lt_count = 0; for (lt_i = NF; lt_i > 0; lt_i--) { if ($lt_i != "" && $lt_i != ".") { if ($lt_i == "..") { lt_count++; } else { if (lt_count == 0) { - lt_foo="/" $lt_i lt_foo; + lt_foo = "/" $lt_i lt_foo; } else { lt_count--; } @@ -2157,7 +2352,7 @@ BEGIN {RS=" "; FS="/|\n";} { # for these hosts. case $host_os in mingw* | cegcc*) lt_search_path_spec=`$ECHO "$lt_search_path_spec" |\ - $SED 's,/\([[A-Za-z]]:\),\1,g'` ;; + $SED 's|/\([[A-Za-z]]:\)|\1|g'` ;; esac sys_lib_search_path_spec=`$ECHO "$lt_search_path_spec" | $lt_NL2SP` else @@ -2166,7 +2361,7 @@ fi]) library_names_spec= libname_spec='lib$name' soname_spec= -shrext_cmds=".so" +shrext_cmds=.so postinstall_cmds= postuninstall_cmds= finish_cmds= @@ -2183,56 +2378,109 @@ hardcode_into_libs=no # flags to be left without arguments need_version=unknown +AC_ARG_VAR([LT_SYS_LIBRARY_PATH], +[User-defined run-time library search path.]) + case $host_os in aix3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname.a' shlibpath_var=LIBPATH # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' + soname_spec='$libname$release$shared_ext$major' ;; aix[[4-9]]*) - version_type=linux + version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no hardcode_into_libs=yes - if test "$host_cpu" = ia64; then + if test ia64 = "$host_cpu"; then # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + library_names_spec='$libname$release$shared_ext$major $libname$release$shared_ext$versuffix $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH else # With GCC up to 2.95.x, collect2 would create an import file # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in + # the line '#! .'. This would cause the generated library to + # depend on '.', always an invalid library. This was fixed in # development snapshots of GCC prior to 3.0. case $host_os in aix4 | aix4.[[01]] | aix4.[[01]].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' - echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + echo '#endif'; } | $CC -E - | $GREP yes > /dev/null; then : else can_build_shared=no fi ;; esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # Using Import Files as archive members, it is possible to support + # filename-based versioning of shared library archives on AIX. While + # this would work for both with and without runtime linking, it will + # prevent static linking of such archives. So we do filename-based + # shared library versioning with .so extension only, which is used + # when both runtime linking and shared linking is enabled. + # Unfortunately, runtime linking may impact performance, so we do + # not want this to be the default eventually. Also, we use the + # versioned .so libs for executables only if there is the -brtl + # linker flag in LDFLAGS as well, or --with-aix-soname=svr4 only. + # To allow for filename-based versioning support, we need to create + # libNAME.so.V as an archive file, containing: + # *) an Import File, referring to the versioned filename of the + # archive as well as the shared archive member, telling the + # bitwidth (32 or 64) of that shared object, and providing the + # list of exported symbols of that shared object, eventually + # decorated with the 'weak' keyword + # *) the shared object with the F_LOADONLY flag set, to really avoid + # it being seen by the linker. + # At run time we better use the real file rather than another symlink, + # but for link time we create the symlink libNAME.so -> libNAME.so.V + + case $with_aix_soname,$aix_use_runtimelinking in + # AIX (on Power*) has no versioning support, so currently we cannot hardcode correct # soname into executable. Probably we can add versioning support to # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then + aix,yes) # traditional libtool + dynamic_linker='AIX unversionable lib.so' # If using run time linking (on AIX 4.2 or later) use lib.so # instead of lib.a to let people know that these are not # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - else + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + ;; + aix,no) # traditional AIX only + dynamic_linker='AIX lib.a[(]lib.so.V[)]' # We preserve .a as extension for shared libraries through AIX4.2 # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' - fi + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + ;; + svr4,*) # full svr4 only + dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)]" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,yes) # both, prefer svr4 + dynamic_linker="AIX lib.so.V[(]$shared_archive_member_spec.o[)], lib.a[(]lib.so.V[)]" + library_names_spec='$libname$release$shared_ext$major $libname$shared_ext' + # unpreferred sharedlib libNAME.a needs extra handling + postinstall_cmds='test -n "$linkname" || linkname="$realname"~func_stripname "" ".so" "$linkname"~$install_shared_prog "$dir/$func_stripname_result.$libext" "$destdir/$func_stripname_result.$libext"~test -z "$tstripme" || test -z "$striplib" || $striplib "$destdir/$func_stripname_result.$libext"' + postuninstall_cmds='for n in $library_names $old_library; do :; done~func_stripname "" ".so" "$n"~test "$func_stripname_result" = "$n" || func_append rmfiles " $odir/$func_stripname_result.$libext"' + # We do not specify a path in Import Files, so LIBPATH fires. + shlibpath_overrides_runpath=yes + ;; + *,no) # both, prefer aix + dynamic_linker="AIX lib.a[(]lib.so.V[)], lib.so.V[(]$shared_archive_member_spec.o[)]" + library_names_spec='$libname$release.a $libname.a' + soname_spec='$libname$release$shared_ext$major' + # unpreferred sharedlib libNAME.so.V and symlink libNAME.so need extra handling + postinstall_cmds='test -z "$dlname" || $install_shared_prog $dir/$dlname $destdir/$dlname~test -z "$tstripme" || test -z "$striplib" || $striplib $destdir/$dlname~test -n "$linkname" || linkname=$realname~func_stripname "" ".a" "$linkname"~(cd "$destdir" && $LN_S -f $dlname $func_stripname_result.so)' + postuninstall_cmds='test -z "$dlname" || func_append rmfiles " $odir/$dlname"~for n in $old_library $library_names; do :; done~func_stripname "" ".a" "$n"~func_append rmfiles " $odir/$func_stripname_result.so"' + ;; + esac shlibpath_var=LIBPATH fi ;; @@ -2242,27 +2490,27 @@ amigaos*) powerpc) # Since July 2007 AmigaOS4 officially supports .so libraries. # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' ;; m68k) library_names_spec='$libname.ixlibrary $libname.a' # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`func_echo_all "$lib" | $SED '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' ;; esac ;; beos*) - library_names_spec='${libname}${shared_ext}' + library_names_spec='$libname$shared_ext' dynamic_linker="$host_os ld.so" shlibpath_var=LIBRARY_PATH ;; bsdi[[45]]*) - version_type=linux + version_type=linux # correct to gnu/linux during the next big refactor need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" @@ -2274,7 +2522,7 @@ bsdi[[45]]*) cygwin* | mingw* | pw32* | cegcc*) version_type=windows - shrext_cmds=".dll" + shrext_cmds=.dll need_version=no need_lib_prefix=no @@ -2283,8 +2531,8 @@ cygwin* | mingw* | pw32* | cegcc*) # gcc library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ @@ -2300,17 +2548,17 @@ cygwin* | mingw* | pw32* | cegcc*) case $host_os in cygwin*) # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + soname_spec='`echo $libname | sed -e 's/^lib/cyg/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' m4_if([$1], [],[ sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/lib/w32api"]) ;; mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; pw32*) # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + library_names_spec='`echo $libname | sed -e 's/^lib/pw/'``echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' ;; esac dynamic_linker='Win32 ld.exe' @@ -2319,8 +2567,8 @@ m4_if([$1], [],[ *,cl*) # Native MSVC libname_spec='$name' - soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - library_names_spec='${libname}.dll.lib' + soname_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext' + library_names_spec='$libname.dll.lib' case $build_os in mingw*) @@ -2347,7 +2595,7 @@ m4_if([$1], [],[ sys_lib_search_path_spec=`cygpath --path --unix "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` ;; *) - sys_lib_search_path_spec="$LIB" + sys_lib_search_path_spec=$LIB if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then # It is most probably a Windows format PATH. sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` @@ -2360,8 +2608,8 @@ m4_if([$1], [],[ esac # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname' @@ -2374,7 +2622,7 @@ m4_if([$1], [],[ *) # Assume MSVC wrapper - library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' + library_names_spec='$libname`echo $release | $SED -e 's/[[.]]/-/g'`$versuffix$shared_ext $libname.lib' dynamic_linker='Win32 ld.exe' ;; esac @@ -2387,8 +2635,8 @@ darwin* | rhapsody*) version_type=darwin need_lib_prefix=no need_version=no - library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' + library_names_spec='$libname$release$major$shared_ext $libname$shared_ext' + soname_spec='$libname$release$major$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' @@ -2398,18 +2646,14 @@ m4_if([$1], [],[ ;; dgux*) - version_type=linux + version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; -freebsd1*) - dynamic_linker=no - ;; - freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. @@ -2417,25 +2661,26 @@ freebsd* | dragonfly*) objformat=`/usr/bin/objformat` else case $host_os in - freebsd[[123]]*) objformat=aout ;; + freebsd[[23]].*) objformat=aout ;; *) objformat=elf ;; esac fi version_type=freebsd-$objformat case $version_type in freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' need_version=no need_lib_prefix=no ;; freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' need_version=yes ;; esac shlibpath_var=LD_LIBRARY_PATH case $host_os in - freebsd2*) + freebsd2.*) shlibpath_overrides_runpath=yes ;; freebsd3.[[01]]* | freebsdelf3.[[01]]*) @@ -2454,26 +2699,15 @@ freebsd* | dragonfly*) esac ;; -gnu*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - haiku*) - version_type=linux + version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no dynamic_linker="$host_os runtime_loader" - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' shlibpath_var=LIBRARY_PATH - shlibpath_overrides_runpath=yes + shlibpath_overrides_runpath=no sys_lib_dlsearch_path_spec='/boot/home/config/lib /boot/common/lib /boot/system/lib' hardcode_into_libs=yes ;; @@ -2491,14 +2725,15 @@ hpux9* | hpux10* | hpux11*) dynamic_linker="$host_os dld.so" shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' + if test 32 = "$HPUX_IA64_MODE"; then sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + sys_lib_dlsearch_path_spec=/usr/lib/hpux32 else sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + sys_lib_dlsearch_path_spec=/usr/lib/hpux64 fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) shrext_cmds='.sl' @@ -2506,8 +2741,8 @@ hpux9* | hpux10* | hpux11*) dynamic_linker="$host_os dld.sl" shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; @@ -2516,8 +2751,8 @@ hpux9* | hpux10* | hpux11*) dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' ;; esac # HP-UX runs *really* slowly unless shared libraries are mode 555, ... @@ -2527,11 +2762,11 @@ hpux9* | hpux10* | hpux11*) ;; interix[[3-9]]*) - version_type=linux + version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no @@ -2542,16 +2777,16 @@ irix5* | irix6* | nonstopux*) case $host_os in nonstopux*) version_type=nonstopux ;; *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux + if test yes = "$lt_cv_prog_gnu_ld"; then + version_type=linux # correct to gnu/linux during the next big refactor else version_type=irix fi ;; esac need_lib_prefix=no need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$release$shared_ext $libname$shared_ext' case $host_os in irix5* | nonstopux*) libsuff= shlibsuff= @@ -2570,8 +2805,8 @@ irix5* | irix6* | nonstopux*) esac shlibpath_var=LD_LIBRARY${shlibsuff}_PATH shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + sys_lib_search_path_spec="/usr/lib$libsuff /lib$libsuff /usr/local/lib$libsuff" + sys_lib_dlsearch_path_spec="/usr/lib$libsuff /lib$libsuff" hardcode_into_libs=yes ;; @@ -2580,13 +2815,33 @@ linux*oldld* | linux*aout* | linux*coff*) dynamic_linker=no ;; -# This must be Linux ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu) - version_type=linux +linux*android*) + version_type=none # Android doesn't support versioned libraries. need_lib_prefix=no need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='$libname$release$shared_ext' + soname_spec='$libname$release$shared_ext' + finish_cmds= + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + dynamic_linker='Android linker' + # Don't embed -rpath directories since the linker doesn't support them. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + ;; + +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) + version_type=linux # correct to gnu/linux during the next big refactor + need_lib_prefix=no + need_version=no + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no @@ -2611,7 +2866,12 @@ linux* | k*bsd*-gnu | kopensolaris*-gnu) # before this can be enabled. hardcode_into_libs=yes - # Append ld.so.conf contents to the search path + # Ideally, we could use ldconfig to report *all* directores which are + # searched for libraries, however this is still not possible. Aside from not + # being certain /sbin/ldconfig is available, command + # 'ldconfig -N -X -v | grep ^/' on 64bit Fedora does not report /usr/lib64, + # even though it is searched at run-time. Try to do the best guess by + # appending ld.so.conf contents (and includes) to the search path. if test -f /etc/ld.so.conf; then lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;s/"//g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" @@ -2643,12 +2903,12 @@ netbsd*) need_lib_prefix=no need_version=no if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' dynamic_linker='NetBSD ld.elf_so' fi shlibpath_var=LD_LIBRARY_PATH @@ -2657,8 +2917,8 @@ netbsd*) ;; newsos6) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes ;; @@ -2667,58 +2927,68 @@ newsos6) version_type=qnx need_lib_prefix=no need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes dynamic_linker='ldqnx.so' ;; -openbsd*) +openbsd* | bitrig*) version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" + sys_lib_dlsearch_path_spec=/usr/lib need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. - case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then + need_version=no + else + need_version=yes + fi + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[[89]] | openbsd2.[[89]].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi + shlibpath_overrides_runpath=yes ;; os2*) libname_spec='$name' - shrext_cmds=".dll" + version_type=windows + shrext_cmds=.dll + need_version=no need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' + # OS/2 can only load a DLL with a base name of 8 characters or less. + soname_spec='`test -n "$os2dllname" && libname="$os2dllname"; + v=$($ECHO $release$versuffix | tr -d .-); + n=$($ECHO $libname | cut -b -$((8 - ${#v})) | tr . _); + $ECHO $n$v`$shared_ext' + library_names_spec='${libname}_dll.$libext' dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH + shlibpath_var=BEGINLIBPATH + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + postinstall_cmds='base_file=`basename \$file`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\$base_file'\''i; $ECHO \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; $ECHO \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' ;; osf3* | osf4* | osf5*) version_type=osf need_lib_prefix=no need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='$libname$release$shared_ext$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; rdos*) @@ -2726,11 +2996,11 @@ rdos*) ;; solaris*) - version_type=linux + version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes @@ -2740,20 +3010,20 @@ solaris*) sunos4*) version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + library_names_spec='$libname$release$shared_ext$versuffix $libname$shared_ext$versuffix' finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then + if test yes = "$with_gnu_ld"; then need_lib_prefix=no fi need_version=yes ;; sysv4 | sysv4.3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH case $host_vendor in sni) @@ -2774,24 +3044,24 @@ sysv4 | sysv4.3*) ;; sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' + if test -d /usr/nec; then + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$shared_ext.$versuffix $libname$shared_ext.$major $libname$shared_ext' + soname_spec='$libname$shared_ext.$major' shlibpath_var=LD_LIBRARY_PATH fi ;; sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf + version_type=sco need_lib_prefix=no need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=yes hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then + if test yes = "$with_gnu_ld"; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' @@ -2806,19 +3076,19 @@ sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) tpf*) # TPF is a cross-target only. Preferred cross-host = GNU/Linux. - version_type=linux + version_type=linux # correct to gnu/linux during the next big refactor need_lib_prefix=no need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; uts4*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + version_type=linux # correct to gnu/linux during the next big refactor + library_names_spec='$libname$release$shared_ext$versuffix $libname$release$shared_ext$major $libname$shared_ext' + soname_spec='$libname$release$shared_ext$major' shlibpath_var=LD_LIBRARY_PATH ;; @@ -2827,20 +3097,30 @@ uts4*) ;; esac AC_MSG_RESULT([$dynamic_linker]) -test "$dynamic_linker" = no && can_build_shared=no +test no = "$dynamic_linker" && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then +if test yes = "$GCC"; then variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi -if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then - sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +if test set = "${lt_cv_sys_lib_search_path_spec+set}"; then + sys_lib_search_path_spec=$lt_cv_sys_lib_search_path_spec fi -if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then - sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" + +if test set = "${lt_cv_sys_lib_dlsearch_path_spec+set}"; then + sys_lib_dlsearch_path_spec=$lt_cv_sys_lib_dlsearch_path_spec fi +# remember unaugmented sys_lib_dlsearch_path content for libtool script decls... +configure_time_dlsearch_path=$sys_lib_dlsearch_path_spec + +# ... but it needs LT_SYS_LIBRARY_PATH munging for other configure-time code +func_munge_path_list sys_lib_dlsearch_path_spec "$LT_SYS_LIBRARY_PATH" + +# to be used as default LT_SYS_LIBRARY_PATH value in generated libtool +configure_time_lt_sys_library_path=$LT_SYS_LIBRARY_PATH + _LT_DECL([], [variables_saved_for_relink], [1], [Variables whose values should be saved in libtool wrapper scripts and restored at link time]) @@ -2873,39 +3153,41 @@ _LT_DECL([], [hardcode_into_libs], [0], [Whether we should hardcode library paths into libraries]) _LT_DECL([], [sys_lib_search_path_spec], [2], [Compile-time system search path for libraries]) -_LT_DECL([], [sys_lib_dlsearch_path_spec], [2], - [Run-time system search path for libraries]) +_LT_DECL([sys_lib_dlsearch_path_spec], [configure_time_dlsearch_path], [2], + [Detected run-time system search path for libraries]) +_LT_DECL([], [configure_time_lt_sys_library_path], [2], + [Explicit LT_SYS_LIBRARY_PATH set during ./configure time]) ])# _LT_SYS_DYNAMIC_LINKER # _LT_PATH_TOOL_PREFIX(TOOL) # -------------------------- -# find a file program which can recognize shared library +# find a file program that can recognize shared library AC_DEFUN([_LT_PATH_TOOL_PREFIX], [m4_require([_LT_DECL_EGREP])dnl AC_MSG_CHECKING([for $1]) AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, [case $MAGIC_CMD in [[\\/*] | ?:[\\/]*]) - lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + lt_cv_path_MAGIC_CMD=$MAGIC_CMD # Let the user override the test with a path. ;; *) - lt_save_MAGIC_CMD="$MAGIC_CMD" - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + lt_save_MAGIC_CMD=$MAGIC_CMD + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR dnl $ac_dummy forces splitting on constant user-supplied paths. dnl POSIX.2 word splitting is done only on the output of word expansions, dnl not every word. This closes a longstanding sh security hole. ac_dummy="m4_if([$2], , $PATH, [$2])" for ac_dir in $ac_dummy; do - IFS="$lt_save_ifs" + IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/$1; then - lt_cv_path_MAGIC_CMD="$ac_dir/$1" + if test -f "$ac_dir/$1"; then + lt_cv_path_MAGIC_CMD=$ac_dir/"$1" if test -n "$file_magic_test_file"; then case $deplibs_check_method in "file_magic "*) file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + MAGIC_CMD=$lt_cv_path_MAGIC_CMD if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | $EGREP "$file_magic_regex" > /dev/null; then : @@ -2928,11 +3210,11 @@ _LT_EOF break fi done - IFS="$lt_save_ifs" - MAGIC_CMD="$lt_save_MAGIC_CMD" + IFS=$lt_save_ifs + MAGIC_CMD=$lt_save_MAGIC_CMD ;; esac]) -MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +MAGIC_CMD=$lt_cv_path_MAGIC_CMD if test -n "$MAGIC_CMD"; then AC_MSG_RESULT($MAGIC_CMD) else @@ -2950,7 +3232,7 @@ dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) # _LT_PATH_MAGIC # -------------- -# find a file program which can recognize a shared library +# find a file program that can recognize a shared library m4_defun([_LT_PATH_MAGIC], [_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) if test -z "$lt_cv_path_MAGIC_CMD"; then @@ -2977,16 +3259,16 @@ m4_require([_LT_PROG_ECHO_BACKSLASH])dnl AC_ARG_WITH([gnu-ld], [AS_HELP_STRING([--with-gnu-ld], [assume the C compiler uses GNU ld @<:@default=no@:>@])], - [test "$withval" = no || with_gnu_ld=yes], + [test no = "$withval" || with_gnu_ld=yes], [with_gnu_ld=no])dnl ac_prog=ld -if test "$GCC" = yes; then +if test yes = "$GCC"; then # Check if gcc -print-prog-name=ld gives a path. AC_MSG_CHECKING([for ld used by $CC]) case $host in *-*-mingw*) - # gcc leaves a trailing carriage return which upsets mingw + # gcc leaves a trailing carriage return, which upsets mingw ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; *) ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; @@ -3000,7 +3282,7 @@ if test "$GCC" = yes; then while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done - test -z "$LD" && LD="$ac_prog" + test -z "$LD" && LD=$ac_prog ;; "") # If it fails, then pretend we aren't using GCC. @@ -3011,37 +3293,37 @@ if test "$GCC" = yes; then with_gnu_ld=unknown ;; esac -elif test "$with_gnu_ld" = yes; then +elif test yes = "$with_gnu_ld"; then AC_MSG_CHECKING([for GNU ld]) else AC_MSG_CHECKING([for non-GNU ld]) fi AC_CACHE_VAL(lt_cv_path_LD, [if test -z "$LD"; then - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH; do - IFS="$lt_save_ifs" + IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then - lt_cv_path_LD="$ac_dir/$ac_prog" + lt_cv_path_LD=$ac_dir/$ac_prog # Check to see if the program is GNU ld. I'd rather use --version, # but apparently some variants of GNU ld only accept -v. # Break only if it was the GNU/non-GNU ld that we prefer. case `"$lt_cv_path_LD" -v 2>&1 conftest.i +cat conftest.i conftest.i >conftest2.i +: ${lt_DD:=$DD} +AC_PATH_PROGS_FEATURE_CHECK([lt_DD], [dd], +[if "$ac_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && ac_cv_path_lt_DD="$ac_path_lt_DD" ac_path_lt_DD_found=: +fi]) +rm -f conftest.i conftest2.i conftest.out]) +])# _LT_PATH_DD + + +# _LT_CMD_TRUNCATE +# ---------------- +# find command to truncate a binary pipe +m4_defun([_LT_CMD_TRUNCATE], +[m4_require([_LT_PATH_DD]) +AC_CACHE_CHECK([how to truncate binary pipes], [lt_cv_truncate_bin], +[printf 0123456789abcdef0123456789abcdef >conftest.i +cat conftest.i conftest.i >conftest2.i +lt_cv_truncate_bin= +if "$ac_cv_path_lt_DD" bs=32 count=1 conftest.out 2>/dev/null; then + cmp -s conftest.i conftest.out \ + && lt_cv_truncate_bin="$ac_cv_path_lt_DD bs=4096 count=1" +fi +rm -f conftest.i conftest2.i conftest.out +test -z "$lt_cv_truncate_bin" && lt_cv_truncate_bin="$SED -e 4q"]) +_LT_DECL([lt_truncate_bin], [lt_cv_truncate_bin], [1], + [Command to truncate a binary pipe]) +])# _LT_CMD_TRUNCATE + + # _LT_CHECK_MAGIC_METHOD # ---------------------- # how to check for library dependencies @@ -3127,13 +3446,13 @@ lt_cv_deplibs_check_method='unknown' # Need to set the preceding variable on all platforms that support # interlibrary dependencies. # 'none' -- dependencies not supported. -# `unknown' -- same as none, but documents that we really don't know. +# 'unknown' -- same as none, but documents that we really don't know. # 'pass_all' -- all dependencies passed with no checks. # 'test_compile' -- check by making test program. # 'file_magic [[regex]]' -- check by looking for files in library path -# which responds to the $file_magic_cmd with a given extended regex. -# If you have `file' or equivalent on your system and you're not sure -# whether `pass_all' will *always* work, you probably want this one. +# that responds to the $file_magic_cmd with a given extended regex. +# If you have 'file' or equivalent on your system and you're not sure +# whether 'pass_all' will *always* work, you probably want this one. case $host_os in aix[[4-9]]*) @@ -3160,8 +3479,7 @@ mingw* | pw32*) # Base MSYS/MinGW do not provide the 'file' command needed by # func_win32_libid shell function, so use a weaker test based on 'objdump', # unless we find 'file', for example because we are cross-compiling. - # func_win32_libid assumes BSD nm, so disallow it if using MS dumpbin. - if ( test "$lt_cv_nm_interface" = "BSD nm" && file / ) >/dev/null 2>&1; then + if ( file / ) >/dev/null 2>&1; then lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' lt_cv_file_magic_cmd='func_win32_libid' else @@ -3197,10 +3515,6 @@ freebsd* | dragonfly*) fi ;; -gnu*) - lt_cv_deplibs_check_method=pass_all - ;; - haiku*) lt_cv_deplibs_check_method=pass_all ;; @@ -3238,8 +3552,8 @@ irix5* | irix6* | nonstopux*) lt_cv_deplibs_check_method=pass_all ;; -# This must be Linux ELF. -linux* | k*bsd*-gnu | kopensolaris*-gnu) +# This must be glibc/ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) lt_cv_deplibs_check_method=pass_all ;; @@ -3261,8 +3575,8 @@ newos6*) lt_cv_deplibs_check_method=pass_all ;; -openbsd*) - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then +openbsd* | bitrig*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' else lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' @@ -3315,6 +3629,9 @@ sysv4 | sysv4.3*) tpf*) lt_cv_deplibs_check_method=pass_all ;; +os2*) + lt_cv_deplibs_check_method=pass_all + ;; esac ]) @@ -3355,33 +3672,38 @@ AC_DEFUN([LT_PATH_NM], AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, [if test -n "$NM"; then # Let the user override the test. - lt_cv_path_NM="$NM" + lt_cv_path_NM=$NM else - lt_nm_to_check="${ac_tool_prefix}nm" + lt_nm_to_check=${ac_tool_prefix}nm if test -n "$ac_tool_prefix" && test "$build" = "$host"; then lt_nm_to_check="$lt_nm_to_check nm" fi for lt_tmp_nm in $lt_nm_to_check; do - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + lt_save_ifs=$IFS; IFS=$PATH_SEPARATOR for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do - IFS="$lt_save_ifs" + IFS=$lt_save_ifs test -z "$ac_dir" && ac_dir=. - tmp_nm="$ac_dir/$lt_tmp_nm" - if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + tmp_nm=$ac_dir/$lt_tmp_nm + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext"; then # Check to see if the nm accepts a BSD-compat flag. - # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # Adding the 'sed 1q' prevents false positives on HP-UX, which says: # nm: unknown option "B" ignored # Tru64's nm complains that /dev/null is an invalid object file - case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in - */dev/null* | *'Invalid file or object type'*) + # MSYS converts /dev/null to NUL, MinGW nm treats NUL as empty + case $build_os in + mingw*) lt_bad_file=conftest.nm/nofile ;; + *) lt_bad_file=/dev/null ;; + esac + case `"$tmp_nm" -B $lt_bad_file 2>&1 | sed '1q'` in + *$lt_bad_file* | *'Invalid file or object type'*) lt_cv_path_NM="$tmp_nm -B" - break + break 2 ;; *) case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in */dev/null*) lt_cv_path_NM="$tmp_nm -p" - break + break 2 ;; *) lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but @@ -3392,21 +3714,21 @@ else esac fi done - IFS="$lt_save_ifs" + IFS=$lt_save_ifs done : ${lt_cv_path_NM=no} fi]) -if test "$lt_cv_path_NM" != "no"; then - NM="$lt_cv_path_NM" +if test no != "$lt_cv_path_NM"; then + NM=$lt_cv_path_NM else # Didn't find any BSD compatible name lister, look for dumpbin. if test -n "$DUMPBIN"; then : # Let the user override the test. else AC_CHECK_TOOLS(DUMPBIN, [dumpbin "link -dump"], :) - case `$DUMPBIN -symbols /dev/null 2>&1 | sed '1q'` in + case `$DUMPBIN -symbols -headers /dev/null 2>&1 | sed '1q'` in *COFF*) - DUMPBIN="$DUMPBIN -symbols" + DUMPBIN="$DUMPBIN -symbols -headers" ;; *) DUMPBIN=: @@ -3414,8 +3736,8 @@ else esac fi AC_SUBST([DUMPBIN]) - if test "$DUMPBIN" != ":"; then - NM="$DUMPBIN" + if test : != "$DUMPBIN"; then + NM=$DUMPBIN fi fi test -z "$NM" && NM=nm @@ -3461,8 +3783,8 @@ lt_cv_sharedlib_from_linklib_cmd, case $host_os in cygwin* | mingw* | pw32* | cegcc*) - # two different shell functions defined in ltmain.sh - # decide which to use based on capabilities of $DLLTOOL + # two different shell functions defined in ltmain.sh; + # decide which one to use based on capabilities of $DLLTOOL case `$DLLTOOL --help 2>&1` in *--identify-strict*) lt_cv_sharedlib_from_linklib_cmd=func_cygming_dll_for_implib @@ -3474,7 +3796,7 @@ cygwin* | mingw* | pw32* | cegcc*) ;; *) # fallback: assume linklib IS sharedlib - lt_cv_sharedlib_from_linklib_cmd="$ECHO" + lt_cv_sharedlib_from_linklib_cmd=$ECHO ;; esac ]) @@ -3501,13 +3823,28 @@ AC_CACHE_CHECK([if $MANIFEST_TOOL is a manifest tool], [lt_cv_path_mainfest_tool lt_cv_path_mainfest_tool=yes fi rm -f conftest*]) -if test "x$lt_cv_path_mainfest_tool" != xyes; then +if test yes != "$lt_cv_path_mainfest_tool"; then MANIFEST_TOOL=: fi _LT_DECL([], [MANIFEST_TOOL], [1], [Manifest tool])dnl ])# _LT_PATH_MANIFEST_TOOL +# _LT_DLL_DEF_P([FILE]) +# --------------------- +# True iff FILE is a Windows DLL '.def' file. +# Keep in sync with func_dll_def_p in the libtool script +AC_DEFUN([_LT_DLL_DEF_P], +[dnl + test DEF = "`$SED -n dnl + -e '\''s/^[[ ]]*//'\'' dnl Strip leading whitespace + -e '\''/^\(;.*\)*$/d'\'' dnl Delete empty lines and comments + -e '\''s/^\(EXPORTS\|LIBRARY\)\([[ ]].*\)*$/DEF/p'\'' dnl + -e q dnl Only consider the first "real" line + $1`" dnl +])# _LT_DLL_DEF_P + + # LT_LIB_M # -------- # check for math library @@ -3519,11 +3856,11 @@ case $host in # These system don't have libm, or don't need it ;; *-ncr-sysv4.3*) - AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM=-lmw) AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") ;; *) - AC_CHECK_LIB(m, cos, LIBM="-lm") + AC_CHECK_LIB(m, cos, LIBM=-lm) ;; esac AC_SUBST([LIBM]) @@ -3542,7 +3879,7 @@ m4_defun([_LT_COMPILER_NO_RTTI], _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= -if test "$GCC" = yes; then +if test yes = "$GCC"; then case $cc_basename in nvcc*) _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -Xcompiler -fno-builtin' ;; @@ -3594,7 +3931,7 @@ cygwin* | mingw* | pw32* | cegcc*) symcode='[[ABCDGISTW]]' ;; hpux*) - if test "$host_cpu" = ia64; then + if test ia64 = "$host_cpu"; then symcode='[[ABCDEGRST]]' fi ;; @@ -3627,14 +3964,44 @@ case `$NM -V 2>&1` in symcode='[[ABCDGIRSTW]]' ;; esac +if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Gets list of data symbols to import. + lt_cv_sys_global_symbol_to_import="sed -n -e 's/^I .* \(.*\)$/\1/p'" + # Adjust the below global symbol transforms to fixup imported variables. + lt_cdecl_hook=" -e 's/^I .* \(.*\)$/extern __declspec(dllimport) char \1;/p'" + lt_c_name_hook=" -e 's/^I .* \(.*\)$/ {\"\1\", (void *) 0},/p'" + lt_c_name_lib_hook="\ + -e 's/^I .* \(lib.*\)$/ {\"\1\", (void *) 0},/p'\ + -e 's/^I .* \(.*\)$/ {\"lib\1\", (void *) 0},/p'" +else + # Disable hooks by default. + lt_cv_sys_global_symbol_to_import= + lt_cdecl_hook= + lt_c_name_hook= + lt_c_name_lib_hook= +fi + # Transform an extracted symbol line into a proper C declaration. # Some systems (esp. on ia64) link data and code symbols differently, # so use this general approach. -lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" +lt_cv_sys_global_symbol_to_cdecl="sed -n"\ +$lt_cdecl_hook\ +" -e 's/^T .* \(.*\)$/extern int \1();/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/extern char \1;/p'" # Transform an extracted symbol line into symbol name and symbol address -lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" -lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address="sed -n"\ +$lt_c_name_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/p'" + +# Transform an extracted symbol line into symbol name with lib prefix and +# symbol address. +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n"\ +$lt_c_name_lib_hook\ +" -e 's/^: \(.*\) .*$/ {\"\1\", (void *) 0},/p'"\ +" -e 's/^$symcode$symcode* .* \(lib.*\)$/ {\"\1\", (void *) \&\1},/p'"\ +" -e 's/^$symcode$symcode* .* \(.*\)$/ {\"lib\1\", (void *) \&\1},/p'" # Handle CRLF in mingw tool chain opt_cr= @@ -3652,20 +4019,24 @@ for ac_symprfx in "" "_"; do # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then - # Fake it for dumpbin and say T for any non-static function - # and D for any global variable. + # Fake it for dumpbin and say T for any non-static function, + # D for any global variable and I for any imported variable. # Also find C++ and __fastcall symbols from MSVC++, # which start with @ or ?. lt_cv_sys_global_symbol_pipe="$AWK ['"\ " {last_section=section; section=\$ 3};"\ +" /^COFF SYMBOL TABLE/{for(i in hide) delete hide[i]};"\ " /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" /^ *Symbol name *: /{split(\$ 0,sn,\":\"); si=substr(sn[2],2)};"\ +" /^ *Type *: code/{print \"T\",si,substr(si,length(prfx))};"\ +" /^ *Type *: data/{print \"I\",si,substr(si,length(prfx))};"\ " \$ 0!~/External *\|/{next};"\ " / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ " {if(hide[section]) next};"\ -" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ -" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ -" s[1]~/^[@?]/{print s[1], s[1]; next};"\ -" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" {f=\"D\"}; \$ 0~/\(\).*\|/{f=\"T\"};"\ +" {split(\$ 0,a,/\||\r/); split(a[2],s)};"\ +" s[1]~/^[@?]/{print f,s[1],s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print f,t[1],substr(t[1],length(prfx))}"\ " ' prfx=^$ac_symprfx]" else lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" @@ -3692,7 +4063,8 @@ _LT_EOF if AC_TRY_EVAL(ac_compile); then # Now try to grab the symbols. nlist=conftest.nm - if AC_TRY_EVAL(NM conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist) && test -s "$nlist"; then + $ECHO "$as_me:$LINENO: $NM conftest.$ac_objext | $lt_cv_sys_global_symbol_pipe > $nlist" >&AS_MESSAGE_LOG_FD + if eval "$NM" conftest.$ac_objext \| "$lt_cv_sys_global_symbol_pipe" \> $nlist 2>&AS_MESSAGE_LOG_FD && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" @@ -3705,11 +4077,11 @@ _LT_EOF if $GREP ' nm_test_func$' "$nlist" >/dev/null; then cat <<_LT_EOF > conftest.$ac_ext /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ -#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) -/* DATA imports from DLLs on WIN32 con't be const, because runtime +#if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE +/* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT@&t@_DLSYM_CONST -#elif defined(__osf__) +#elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT@&t@_DLSYM_CONST #else @@ -3735,7 +4107,7 @@ lt__PROGRAM__LTX_preloaded_symbols[[]] = { { "@PROGRAM@", (void *) 0 }, _LT_EOF - $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + $SED "s/^$symcode$symcode* .* \(.*\)$/ {\"\1\", (void *) \&\1},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext cat <<\_LT_EOF >> conftest.$ac_ext {0, (void *) 0} }; @@ -3755,9 +4127,9 @@ _LT_EOF mv conftest.$ac_objext conftstm.$ac_objext lt_globsym_save_LIBS=$LIBS lt_globsym_save_CFLAGS=$CFLAGS - LIBS="conftstm.$ac_objext" + LIBS=conftstm.$ac_objext CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" - if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then + if AC_TRY_EVAL(ac_link) && test -s conftest$ac_exeext; then pipe_works=yes fi LIBS=$lt_globsym_save_LIBS @@ -3778,7 +4150,7 @@ _LT_EOF rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. - if test "$pipe_works" = yes; then + if test yes = "$pipe_works"; then break else lt_cv_sys_global_symbol_pipe= @@ -3805,12 +4177,16 @@ _LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], [Take the output of nm and produce a listing of raw symbols and C names]) _LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_import], [lt_cv_sys_global_symbol_to_import], [1], + [Transform the output of nm into a list of symbols to manually relocate]) _LT_DECL([global_symbol_to_c_name_address], [lt_cv_sys_global_symbol_to_c_name_address], [1], [Transform the output of nm in a C name address pair]) _LT_DECL([global_symbol_to_c_name_address_lib_prefix], [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], [Transform the output of nm in a C name address pair when lib prefix is needed]) +_LT_DECL([nm_interface], [lt_cv_nm_interface], [1], + [The name lister interface]) _LT_DECL([], [nm_file_list_spec], [1], [Specify filename containing input files for $NM]) ]) # _LT_CMD_GLOBAL_SYMBOLS @@ -3826,17 +4202,18 @@ _LT_TAGVAR(lt_prog_compiler_static, $1)= m4_if([$1], [CXX], [ # C++ specific cases for pic, static, wl, etc. - if test "$GXX" = yes; then + if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. - if test "$host_cpu" = ia64; then + if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) @@ -3847,8 +4224,8 @@ m4_if([$1], [CXX], [ ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac @@ -3864,6 +4241,11 @@ m4_if([$1], [CXX], [ # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac ;; darwin* | rhapsody*) # PIC is the default on this platform @@ -3913,7 +4295,7 @@ m4_if([$1], [CXX], [ case $host_os in aix[[4-9]]*) # All AIX code is PIC. - if test "$host_cpu" = ia64; then + if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else @@ -3954,14 +4336,14 @@ m4_if([$1], [CXX], [ case $cc_basename in CC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - if test "$host_cpu" != ia64; then + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' + if test ia64 != "$host_cpu"; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' fi ;; aCC*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' case $host_cpu in hppa*64*|ia64*) # +Z the default @@ -3990,7 +4372,7 @@ m4_if([$1], [CXX], [ ;; esac ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu) + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # KAI C++ Compiler @@ -3998,7 +4380,7 @@ m4_if([$1], [CXX], [ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; ecpc* ) - # old Intel C++ for x86_64 which still supported -KPIC. + # old Intel C++ for x86_64, which still supported -KPIC. _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' @@ -4143,17 +4525,18 @@ m4_if([$1], [CXX], [ fi ], [ - if test "$GCC" = yes; then + if test yes = "$GCC"; then _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' case $host_os in aix*) # All AIX code is PIC. - if test "$host_cpu" = ia64; then + if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; amigaos*) @@ -4164,8 +4547,8 @@ m4_if([$1], [CXX], [ ;; m68k) # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. + # adding the '-m68020' flag to GCC prevents building anything better, + # like '-m68040'. _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac @@ -4182,6 +4565,11 @@ m4_if([$1], [CXX], [ # (--disable-auto-import) libraries m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac ;; darwin* | rhapsody*) @@ -4242,7 +4630,9 @@ m4_if([$1], [CXX], [ case $cc_basename in nvcc*) # Cuda Compiler Driver 2.2 _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Xlinker ' - _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Xcompiler -fPIC' + if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)="-Xcompiler $_LT_TAGVAR(lt_prog_compiler_pic, $1)" + fi ;; esac else @@ -4250,7 +4640,7 @@ m4_if([$1], [CXX], [ case $host_os in aix*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - if test "$host_cpu" = ia64; then + if test ia64 = "$host_cpu"; then # AIX 5 now supports IA64 processor _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' else @@ -4258,11 +4648,30 @@ m4_if([$1], [CXX], [ fi ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + case $cc_basename in + nagfor*) + # NAG Fortran compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,-Wl,,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + mingw* | cygwin* | pw32* | os2* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). m4_if([$1], [GCJ], [], [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + case $host_os in + os2*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-static' + ;; + esac ;; hpux9* | hpux10* | hpux11*) @@ -4278,7 +4687,7 @@ m4_if([$1], [CXX], [ ;; esac # Is there a better lt_prog_compiler_static that works with the bundled CC? - _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + _LT_TAGVAR(lt_prog_compiler_static, $1)='$wl-a ${wl}archive' ;; irix5* | irix6* | nonstopux*) @@ -4287,14 +4696,20 @@ m4_if([$1], [CXX], [ _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu) + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in - # old Intel for x86_64 which still supported -KPIC. + # old Intel for x86_64, which still supported -KPIC. ecc*) _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' ;; + # flang / f18. f95 an alias for gfortran or flang on Debian + flang* | f18* | f95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; # icc used to be incompatible with GCC. # ICC 10 doesn't accept -KPIC any more. icc* | ifort*) @@ -4314,6 +4729,12 @@ m4_if([$1], [CXX], [ _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' ;; + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; pgcc* | pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group compilers (*not* the Pentium gcc compiler, # which looks to be a dead project) @@ -4334,18 +4755,33 @@ m4_if([$1], [CXX], [ ;; *) case `$CC -V 2>&1 | sed 5q` in - *Sun\ F* | *Sun*Fortran*) + *Sun\ Ceres\ Fortran* | *Sun*Fortran*\ [[1-7]].* | *Sun*Fortran*\ 8.[[0-3]]*) # Sun Fortran 8.3 passes all unrecognized flags to the linker _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='' ;; + *Sun\ F* | *Sun*Fortran*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; *Sun\ C*) # Sun C 5.9 _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' ;; + *Intel*\ [[CF]]*Compiler*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + *Portland\ Group*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; esac ;; esac @@ -4396,7 +4832,7 @@ m4_if([$1], [CXX], [ ;; sysv4*MP*) - if test -d /usr/nec ;then + if test -d /usr/nec; then _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi @@ -4425,7 +4861,7 @@ m4_if([$1], [CXX], [ fi ]) case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: + # For platforms that do not support PIC, -DPIC is meaningless: *djgpp*) _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; @@ -4491,21 +4927,27 @@ m4_if([$1], [CXX], [ case $host_os in aix[[4-9]]*) # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - # Also, AIX nm treats weak defined symbols like other global defined - # symbols, whereas GNU nm marks them as "W". + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then - _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else - _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi ;; pw32*) - _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" + _LT_TAGVAR(export_symbols_cmds, $1)=$ltdll_cmds ;; cygwin* | mingw* | cegcc*) case $cc_basename in - cl*) ;; + cl*) + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' + ;; *) _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] @@ -4533,7 +4975,6 @@ m4_if([$1], [CXX], [ _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= - _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported @@ -4549,9 +4990,9 @@ m4_if([$1], [CXX], [ # included in the symbol list _LT_TAGVAR(include_expsyms, $1)= # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ` (' and `)$', so one must not match beginning or - # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', - # as well as any symbol that contains `d'. + # it will be wrapped by ' (' and ')$', so one must not match beginning or + # end of line. Example: 'a|bc|.*d.*' will exclude the symbols 'a' and 'bc', + # as well as any symbol that contains 'd'. _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out # platforms (ab)use it in PIC code, but their linkers get confused if @@ -4567,7 +5008,7 @@ dnl Note also adjust exclude_expsyms for C++ above. # FIXME: the MSVC++ port hasn't been tested in a loooong time # When not using gcc, we currently assume that we are using # Microsoft Visual C++. - if test "$GCC" != yes; then + if test yes != "$GCC"; then with_gnu_ld=no fi ;; @@ -4575,7 +5016,7 @@ dnl Note also adjust exclude_expsyms for C++ above. # we just hope/assume this is gcc and not c89 (= MSVC++) with_gnu_ld=yes ;; - openbsd*) + openbsd* | bitrig*) with_gnu_ld=no ;; linux* | k*bsd*-gnu | gnu*) @@ -4588,7 +5029,7 @@ dnl Note also adjust exclude_expsyms for C++ above. # On some targets, GNU ld is compatible enough with the native linker # that we're better off using the native interface for both. lt_use_gnu_ld_interface=no - if test "$with_gnu_ld" = yes; then + if test yes = "$with_gnu_ld"; then case $host_os in aix*) # The AIX port of GNU ld has always aspired to compatibility @@ -4610,24 +5051,24 @@ dnl Note also adjust exclude_expsyms for C++ above. esac fi - if test "$lt_use_gnu_ld_interface" = yes; then + if test yes = "$lt_use_gnu_ld_interface"; then # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='${wl}' + wlarc='$wl' # Set some defaults for GNU ld with shared library support. These # are reset later if shared libraries are not supported. Putting them # here allows them to be overridden if necessary. runpath_var=LD_RUN_PATH - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # ancient GNU ld didn't support --whole-archive et. al. if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then - _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi supports_anon_versioning=no - case `$LD -v 2>&1` in + case `$LD -v | $SED -e 's/([^)]\+)\s\+//' 2>&1` in *GNU\ gold*) supports_anon_versioning=yes ;; *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... @@ -4640,7 +5081,7 @@ dnl Note also adjust exclude_expsyms for C++ above. case $host_os in aix[[3-9]]*) # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then + if test ia64 != "$host_cpu"; then _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 @@ -4659,7 +5100,7 @@ _LT_EOF case $host_cpu in powerpc) # see comment about AmigaOS4 .so support - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) @@ -4675,7 +5116,7 @@ _LT_EOF _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME - _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi @@ -4685,7 +5126,7 @@ _LT_EOF # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes @@ -4693,61 +5134,89 @@ _LT_EOF _LT_TAGVAR(exclude_expsyms, $1)=['[_]+GLOBAL_OFFSET_TABLE_|[_]+GLOBAL__[FID]_.*|[_]+head_[A-Za-z0-9_]+_dll|[A-Za-z0-9_]+_dll_iname'] if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; haiku*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) tmp_diet=no - if test "$host_os" = linux-dietlibc; then + if test linux-dietlibc = "$host_os"; then case $cc_basename in diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) esac fi if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ - && test "$tmp_diet" = no + && test no = "$tmp_diet" then tmp_addflag=' $pic_flag' tmp_sharedflag='-shared' case $cc_basename,$host_cpu in pgcc*) # Portland Group C compiler - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag' ;; pgf77* | pgf90* | pgf95* | pgfortran*) # Portland Group f77 and f90 compilers - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' tmp_addflag=' $pic_flag -Mnomain' ;; ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 tmp_addflag=' -i_dynamic' ;; @@ -4758,43 +5227,47 @@ _LT_EOF lf95*) # Lahey Fortran 8.1 _LT_TAGVAR(whole_archive_flag_spec, $1)= tmp_sharedflag='--shared' ;; + nagfor*) # NAGFOR 5.3 + tmp_sharedflag='-Wl,-shared' ;; xl[[cC]]* | bgxl[[cC]]* | mpixl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) tmp_sharedflag='-qmkshrobj' tmp_addflag= ;; nvcc*) # Cuda Compiler Driver 2.2 - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes ;; esac case `$CC -V 2>&1 | sed 5q` in *Sun\ C*) # Sun C 5.9 - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes tmp_sharedflag='-G' ;; *Sun\ F*) # Sun Fortran 8.3 tmp_sharedflag='-G' ;; esac - _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' - if test "x$supports_anon_versioning" = xyes; then + if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi case $cc_basename in + tcc*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-rdynamic' + ;; xlf* | bgf* | bgxlf* | mpixlf*) # IBM XL Fortran 10.1 on PPC cannot create shared libs itself _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= - _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname -o $lib' - if test "x$supports_anon_versioning" = xyes; then + if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $linker_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' fi ;; esac @@ -4808,8 +5281,8 @@ _LT_EOF _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' wlarc= else - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' fi ;; @@ -4827,8 +5300,8 @@ _LT_EOF _LT_EOF elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi @@ -4840,7 +5313,7 @@ _LT_EOF _LT_TAGVAR(ld_shlibs, $1)=no cat <<_LT_EOF 1>&2 -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 cannot *** reliably create shared libraries on SCO systems. Therefore, libtool *** is disabling shared libraries support. We urge you to upgrade GNU *** binutils to release 2.16.91.0.3 or newer. Another option is to modify @@ -4855,9 +5328,9 @@ _LT_EOF # DT_RUNPATH tag from executables and libraries. But doing so # requires that you compile everything twice, which is a pain. if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi @@ -4874,15 +5347,15 @@ _LT_EOF *) if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi ;; esac - if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then + if test no = "$_LT_TAGVAR(ld_shlibs, $1)"; then runpath_var= _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= _LT_TAGVAR(export_dynamic_flag_spec, $1)= @@ -4898,7 +5371,7 @@ _LT_EOF # Note: this linker hardcodes the directories in LIBPATH if there # are no directories specified by -L. _LT_TAGVAR(hardcode_minus_L, $1)=yes - if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + if test yes = "$GCC" && test -z "$lt_prog_compiler_static"; then # Neither direct hardcoding nor static linking is supported with a # broken collect2. _LT_TAGVAR(hardcode_direct, $1)=unsupported @@ -4906,34 +5379,57 @@ _LT_EOF ;; aix[[4-9]]*) - if test "$host_cpu" = ia64; then + if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' - no_entry_flag="" + no_entry_flag= else # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - # Also, AIX nm treats weak defined symbols like other global - # defined symbols, whereas GNU nm marks them as "W". + # -C means demangle to GNU nm, but means don't demangle to AIX nm. + # Without the "-l" option, or with the "-B" option, AIX nm treats + # weak defined symbols like other global defined symbols, whereas + # GNU nm marks them as "W". + # While the 'weak' keyword is ignored in the Export File, we need + # it in the Import File for the 'aix-soname' feature, so we have + # to replace the "-B" option with "-P" for AIX nm. if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then - _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W")) && ([substr](\$ 3,1,1) != ".")) { if (\$ 2 == "W") { print \$ 3 " weak" } else { print \$ 3 } } }'\'' | sort -u > $export_symbols' else - _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + _LT_TAGVAR(export_symbols_cmds, $1)='`func_echo_all $NM | $SED -e '\''s/B\([[^B]]*\)$/P\1/'\''` -PCpgl $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B") || (\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) && ([substr](\$ 1,1,1) != ".")) { if ((\$ 2 == "W") || (\$ 2 == "V") || (\$ 2 == "Z")) { print \$ 1 " weak" } else { print \$ 1 } } }'\'' | sort -u > $export_symbols' fi aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + if (test x-brtl = "x$ld_flag" || test x-Wl,-brtl = "x$ld_flag"); then aix_use_runtimelinking=yes break fi done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi ;; esac @@ -4952,13 +5448,21 @@ _LT_EOF _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + _LT_TAGVAR(file_list_spec, $1)='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # traditional, no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + ;; + esac - if test "$GCC" = yes; then + if test yes = "$GCC"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` + collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then @@ -4977,62 +5481,80 @@ _LT_EOF ;; esac shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' + if test yes = "$aix_use_runtimelinking"; then + shared_flag="$shared_flag "'$wl-G' fi - _LT_TAGVAR(link_all_deplibs, $1)=no + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' else # not using gcc - if test "$host_cpu" = ia64; then + if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' else - shared_flag='${wl}-bM:SRE' + shared_flag='$wl-bM:SRE' fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' fi fi - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to export. _LT_TAGVAR(always_export_symbols, $1)=yes - if test "$aix_use_runtimelinking" = yes; then + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. _LT_TAGVAR(allow_undefined_flag, $1)='-berok' # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else - if test "$host_cpu" = ia64; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + if test ia64 = "$host_cpu"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" - _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. - _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' - if test "$with_gnu_ld" = yes; then + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' + if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes - # This is similar to how AIX traditionally builds its shared libraries. - _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; @@ -5041,7 +5563,7 @@ _LT_EOF case $host_cpu in powerpc) # see comment about AmigaOS4 .so support - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='' ;; m68k) @@ -5071,35 +5593,37 @@ _LT_EOF # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" + shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. - _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' - _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - sed -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; - else - sed -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; - fi~ - $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ - linknames=' + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(exclude_expsyms, $1)='_NULL_IMPORT_DESCRIPTOR|_IMPORT_DESCRIPTOR_.*' _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1,DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ - lt_tool_outputfile="@TOOL_OUTPUT@"~ - case $lt_outputfile in - *.exe|*.EXE) ;; - *) - lt_outputfile="$lt_outputfile.exe" - lt_tool_outputfile="$lt_tool_outputfile.exe" - ;; - esac~ - if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then - $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; - $RM "$lt_outputfile.manifest"; - fi' + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' ;; *) # Assume MSVC wrapper @@ -5108,7 +5632,7 @@ _LT_EOF # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" + shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `func_echo_all "$deplibs" | $SED '\''s/ -lc$//'\''` -link -dll~linknames=' # The linker will automatically build a .lib file if we build a DLL. @@ -5130,10 +5654,6 @@ _LT_EOF _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; - freebsd1*) - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor # support. Future versions do this automatically, but an explicit c++rt0.o # does not break anything, and helps significantly (at the cost of a little @@ -5146,7 +5666,7 @@ _LT_EOF ;; # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2*) + freebsd2.*) _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes @@ -5162,34 +5682,33 @@ _LT_EOF ;; hpux9*) - if test "$GCC" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' fi - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; hpux10*) - if test "$GCC" = yes && test "$with_gnu_ld" = no; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + if test yes,no = "$GCC,$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' fi - if test "$with_gnu_ld" = no; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. _LT_TAGVAR(hardcode_minus_L, $1)=yes @@ -5197,25 +5716,25 @@ _LT_EOF ;; hpux11*) - if test "$GCC" = yes && test "$with_gnu_ld" = no; then + if test yes,no = "$GCC,$with_gnu_ld"; then case $host_cpu in hppa*64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags' ;; esac else case $host_cpu in hppa*64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' ;; ia64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' ;; *) m4_if($1, [], [ @@ -5223,14 +5742,14 @@ _LT_EOF # (HP92453-01 A.11.01.20 doesn't, HP92453-01 B.11.X.35175-35176.GP does) _LT_LINKER_OPTION([if $CC understands -b], _LT_TAGVAR(lt_cv_prog_compiler__b, $1), [-b], - [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], + [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags'], [_LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags'])], - [_LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) + [_LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $libobjs $deplibs $compiler_flags']) ;; esac fi - if test "$with_gnu_ld" = no; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in @@ -5241,7 +5760,7 @@ _LT_EOF *) _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # hardcode_minus_L: Not really in the search PATH, # but as the default location of the library. @@ -5252,16 +5771,16 @@ _LT_EOF ;; irix5* | irix6* | nonstopux*) - if test "$GCC" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' # Try to use the -exported_symbol ld option, if it does not # work, assume that -exports_file does not work either and # implicitly export all symbols. # This should be the same for all languages, so no per-tag cache variable. AC_CACHE_CHECK([whether the $host_os linker accepts -exported_symbol], [lt_cv_irix_exported_symbol], - [save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + [save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS -shared $wl-exported_symbol ${wl}foo $wl-update_registry $wl/dev/null" AC_LINK_IFELSE( [AC_LANG_SOURCE( [AC_LANG_CASE([C], [[int foo (void) { return 0; }]], @@ -5274,21 +5793,32 @@ _LT_EOF end]])])], [lt_cv_irix_exported_symbol=yes], [lt_cv_irix_exported_symbol=no]) - LDFLAGS="$save_LDFLAGS"]) - if test "$lt_cv_irix_exported_symbol" = yes; then - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + LDFLAGS=$save_LDFLAGS]) + if test yes = "$lt_cv_irix_exported_symbol"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations $wl-exports_file $wl$export_symbols -o $lib' fi + _LT_TAGVAR(link_all_deplibs, $1)=no else - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -exports_file $export_symbols -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes _LT_TAGVAR(link_all_deplibs, $1)=yes ;; + linux*) + case $cc_basename in + tcc*) + # Fabrice Bellard et al's Tiny C Compiler + _LT_TAGVAR(ld_shlibs, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out @@ -5303,7 +5833,7 @@ _LT_EOF newsos6) _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(hardcode_direct, $1)=yes - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; @@ -5311,27 +5841,19 @@ _LT_EOF *nto* | *qnx*) ;; - openbsd*) + openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes - if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`"; then _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags $wl-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' else - case $host_os in - openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) - _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - ;; - *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - ;; - esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' fi else _LT_TAGVAR(ld_shlibs, $1)=no @@ -5342,33 +5864,53 @@ _LT_EOF _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' _LT_TAGVAR(hardcode_minus_L, $1)=yes _LT_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~echo DATA >> $output_objdir/$libname.def~echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' - _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes ;; osf3*) - if test "$GCC" = yes; then - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + if test yes = "$GCC"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' fi _LT_TAGVAR(archive_cmds_need_lc, $1)='no' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: ;; osf4* | osf5*) # as osf3* with the addition of -msym flag - if test "$GCC" = yes; then - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $pic_flag $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + if test yes = "$GCC"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $pic_flag $libobjs $deplibs $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' else _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ - $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + $CC -shared$allow_undefined_flag $wl-input $wl$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~$RM $lib.exp' # Both c and cxx compiler support -rpath directly _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' @@ -5379,24 +5921,24 @@ _LT_EOF solaris*) _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' - if test "$GCC" = yes; then - wlarc='${wl}' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + if test yes = "$GCC"; then + wlarc='$wl' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $wl-z ${wl}text $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -shared $pic_flag ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + $CC -shared $pic_flag $wl-z ${wl}text $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' else case `$CC -V 2>&1` in *"Compilers 5.0"*) wlarc='' - _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_cmds, $1)='$LD -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $linker_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + $LD -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' ;; *) - wlarc='${wl}' - _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + wlarc='$wl' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h $soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + $CC -G$allow_undefined_flag -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' ;; esac fi @@ -5406,11 +5948,11 @@ _LT_EOF solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, - # but understands `-z linker_flag'. GCC discards it without `$wl', + # but understands '-z linker_flag'. GCC discards it without '$wl', # but is careful enough not to reorder. # Supported since Solaris 2.6 (maybe 2.5.1?) - if test "$GCC" = yes; then - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + if test yes = "$GCC"; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' else _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' fi @@ -5420,10 +5962,10 @@ _LT_EOF ;; sunos4*) - if test "x$host_vendor" = xsequent; then + if test sequent = "$host_vendor"; then # Use $CC to link under sequent, because it throws in some extra .o # files that make .init and .fini sections work. - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h $soname -o $lib $libobjs $deplibs $compiler_flags' else _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' fi @@ -5472,43 +6014,43 @@ _LT_EOF ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) - _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' - if test "$GCC" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not + # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. - _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' - if test "$GCC" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + if test yes = "$GCC"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' else - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' fi ;; @@ -5523,17 +6065,17 @@ _LT_EOF ;; esac - if test x$host_vendor = xsni; then + if test sni = "$host_vendor"; then case $host in sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Blargedynsym' ;; esac fi fi ]) AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) -test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no +test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no _LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld @@ -5550,7 +6092,7 @@ x|xyes) # Assume -lc should be added _LT_TAGVAR(archive_cmds_need_lc, $1)=yes - if test "$enable_shared" = yes && test "$GCC" = yes; then + if test yes,yes = "$GCC,$enable_shared"; then case $_LT_TAGVAR(archive_cmds, $1) in *'~'*) # FIXME: we may have to deal with multi-command sequences. @@ -5627,18 +6169,15 @@ _LT_TAGDECL([], [no_undefined_flag], [1], _LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], [Flag to hardcode $libdir into a binary during linking. This must work even if $libdir does not exist]) -_LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1], - [[If ld is used when linking, flag to hardcode $libdir into a binary - during linking. This must work even if $libdir does not exist]]) _LT_TAGDECL([], [hardcode_libdir_separator], [1], [Whether we need a single "-rpath" flag with a separated argument]) _LT_TAGDECL([], [hardcode_direct], [0], - [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary]) _LT_TAGDECL([], [hardcode_direct_absolute], [0], - [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + [Set to "yes" if using DIR/libNAME$shared_ext during linking hardcodes DIR into the resulting binary and the resulting library dependency is - "absolute", i.e impossible to change by setting ${shlibpath_var} if the + "absolute", i.e impossible to change by setting $shlibpath_var if the library is relocated]) _LT_TAGDECL([], [hardcode_minus_L], [0], [Set to "yes" if using the -LDIR flag during linking hardcodes DIR @@ -5679,10 +6218,10 @@ dnl [Compiler flag to generate thread safe objects]) # ------------------------ # Ensure that the configuration variables for a C compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write -# the compiler configuration to `libtool'. +# the compiler configuration to 'libtool'. m4_defun([_LT_LANG_C_CONFIG], [m4_require([_LT_DECL_EGREP])dnl -lt_save_CC="$CC" +lt_save_CC=$CC AC_LANG_PUSH(C) # Source file extension for C test sources. @@ -5722,18 +6261,18 @@ if test -n "$compiler"; then LT_SYS_DLOPEN_SELF _LT_CMD_STRIPLIB - # Report which library types will actually be built + # Report what library types will actually be built AC_MSG_CHECKING([if libtool supports shared libraries]) AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) - test "$can_build_shared" = "no" && enable_shared=no + test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) - test "$enable_shared" = yes && enable_static=no + test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' @@ -5741,8 +6280,12 @@ if test -n "$compiler"; then ;; aix[[4-9]]*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac fi ;; esac @@ -5750,13 +6293,13 @@ if test -n "$compiler"; then AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. - test "$enable_shared" = yes || enable_static=yes + test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) _LT_CONFIG($1) fi AC_LANG_POP -CC="$lt_save_CC" +CC=$lt_save_CC ])# _LT_LANG_C_CONFIG @@ -5764,14 +6307,14 @@ CC="$lt_save_CC" # -------------------------- # Ensure that the configuration variables for a C++ compiler are suitably # defined. These variables are subsequently used by _LT_CONFIG to write -# the compiler configuration to `libtool'. +# the compiler configuration to 'libtool'. m4_defun([_LT_LANG_CXX_CONFIG], [m4_require([_LT_FILEUTILS_DEFAULTS])dnl m4_require([_LT_DECL_EGREP])dnl m4_require([_LT_PATH_MANIFEST_TOOL])dnl -if test -n "$CXX" && ( test "X$CXX" != "Xno" && - ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || - (test "X$CXX" != "Xg++"))) ; then +if test -n "$CXX" && ( test no != "$CXX" && + ( (test g++ = "$CXX" && `g++ -v >/dev/null 2>&1` ) || + (test g++ != "$CXX"))); then AC_PROG_CXXCPP else _lt_caught_CXX_error=yes @@ -5787,7 +6330,6 @@ _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= -_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported @@ -5814,7 +6356,7 @@ _LT_TAGVAR(objext, $1)=$objext # the CXX compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. -if test "$_lt_caught_CXX_error" != yes; then +if test yes != "$_lt_caught_CXX_error"; then # Code to be used in simple compile tests lt_simple_compile_test_code="int some_variable = 0;" @@ -5856,35 +6398,35 @@ if test "$_lt_caught_CXX_error" != yes; then if test -n "$compiler"; then # We don't want -fno-exception when compiling C++ code, so set the # no_builtin_flag separately - if test "$GXX" = yes; then + if test yes = "$GXX"; then _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' else _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= fi - if test "$GXX" = yes; then + if test yes = "$GXX"; then # Set up default GNU C++ configuration LT_PATH_LD # Check if GNU C++ uses GNU ld as the underlying linker, since the # archiving commands below assume that GNU ld is being used. - if test "$with_gnu_ld" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + if test yes = "$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC $pic_flag -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # If archive_cmds runs LD, not CC, wlarc should be empty # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to # investigate it a little bit more. (MM) - wlarc='${wl}' + wlarc='$wl' # ancient GNU ld didn't support --whole-archive et. al. if eval "`$CC -print-prog-name=ld` --help 2>&1" | $GREP 'no-whole-archive' > /dev/null; then - _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' else _LT_TAGVAR(whole_archive_flag_spec, $1)= fi @@ -5903,7 +6445,7 @@ if test "$_lt_caught_CXX_error" != yes; then # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else GXX=no @@ -5920,18 +6462,30 @@ if test "$_lt_caught_CXX_error" != yes; then _LT_TAGVAR(ld_shlibs, $1)=no ;; aix[[4-9]]*) - if test "$host_cpu" = ia64; then + if test ia64 = "$host_cpu"; then # On IA64, the linker does run time linking by default, so we don't # have to do anything special. aix_use_runtimelinking=no exp_sym_flag='-Bexport' - no_entry_flag="" + no_entry_flag= else aix_use_runtimelinking=no # Test if we are trying to use run time linking or normal # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. + # have runtime linking enabled, and use it for executables. + # For shared libraries, we enable/disable runtime linking + # depending on the kind of the shared library created - + # when "with_aix_soname,aix_use_runtimelinking" is: + # "aix,no" lib.a(lib.so.V) shared, rtl:no, for executables + # "aix,yes" lib.so shared, rtl:yes, for executables + # lib.a static archive + # "both,no" lib.so.V(shr.o) shared, rtl:yes + # lib.a(lib.so.V) shared, rtl:no, for executables + # "both,yes" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a(lib.so.V) shared, rtl:no + # "svr4,*" lib.so.V(shr.o) shared, rtl:yes, for executables + # lib.a static archive case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) for ld_flag in $LDFLAGS; do case $ld_flag in @@ -5941,6 +6495,13 @@ if test "$_lt_caught_CXX_error" != yes; then ;; esac done + if test svr4,no = "$with_aix_soname,$aix_use_runtimelinking"; then + # With aix-soname=svr4, we create the lib.so.V shared archives only, + # so we don't have lib.a shared libs to link our executables. + # We have to force runtime linking in this case. + aix_use_runtimelinking=yes + LDFLAGS="$LDFLAGS -Wl,-brtl" + fi ;; esac @@ -5959,13 +6520,21 @@ if test "$_lt_caught_CXX_error" != yes; then _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + _LT_TAGVAR(file_list_spec, $1)='$wl-f,' + case $with_aix_soname,$aix_use_runtimelinking in + aix,*) ;; # no import file + svr4,* | *,yes) # use import file + # The Import File defines what to hardcode. + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + ;; + esac - if test "$GXX" = yes; then + if test yes = "$GXX"; then case $host_os in aix4.[[012]]|aix4.[[012]].*) # We only want to do this on AIX 4.2 and lower, the check # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` + collect2name=`$CC -print-prog-name=collect2` if test -f "$collect2name" && strings "$collect2name" | $GREP resolve_lib_name >/dev/null then @@ -5983,64 +6552,84 @@ if test "$_lt_caught_CXX_error" != yes; then fi esac shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' + if test yes = "$aix_use_runtimelinking"; then + shared_flag=$shared_flag' $wl-G' fi + # Need to ensure runtime linking is disabled for the traditional + # shared library, or the linker may eventually find shared libraries + # /with/ Import File - we do not want to mix them. + shared_flag_aix='-shared' + shared_flag_svr4='-shared $wl-G' else # not using gcc - if test "$host_cpu" = ia64; then + if test ia64 = "$host_cpu"; then # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release # chokes on -Wl,-G. The following line is correct: shared_flag='-G' else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' + if test yes = "$aix_use_runtimelinking"; then + shared_flag='$wl-G' else - shared_flag='${wl}-bM:SRE' + shared_flag='$wl-bM:SRE' fi + shared_flag_aix='$wl-bM:SRE' + shared_flag_svr4='$wl-G' fi fi - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-bexpall' # It seems that -bexpall does not export symbols beginning with # underscore (_), so it is better to generate a list of symbols to # export. _LT_TAGVAR(always_export_symbols, $1)=yes - if test "$aix_use_runtimelinking" = yes; then + if test aix,yes = "$with_aix_soname,$aix_use_runtimelinking"; then # Warning - without using the other runtime loading flags (-brtl), # -berok will link without error, but may produce a broken library. - _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # The "-G" linker flag allows undefined symbols. + _LT_TAGVAR(no_undefined_flag, $1)='-bernotok' # Determine the default libpath from the value encoded in an empty # executable. _LT_SYS_MODULE_PATH_AIX([$1]) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then func_echo_all "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs $wl'$no_entry_flag' $compiler_flags `if test -n "$allow_undefined_flag"; then func_echo_all "$wl$allow_undefined_flag"; else :; fi` $wl'$exp_sym_flag:\$export_symbols' '$shared_flag else - if test "$host_cpu" = ia64; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + if test ia64 = "$host_cpu"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $libdir:/usr/lib:/lib' _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" - _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\$wl$no_entry_flag"' $compiler_flags $wl$allow_undefined_flag '"\$wl$exp_sym_flag:\$export_symbols" else # Determine the default libpath from the value encoded in an # empty executable. _LT_SYS_MODULE_PATH_AIX([$1]) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-blibpath:$libdir:'"$aix_libpath" # Warning - without using the other run time loading flags, # -berok will link without error, but may produce a broken library. - _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' - if test "$with_gnu_ld" = yes; then + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-berok' + if test yes = "$with_gnu_ld"; then # We only use this code for GNU lds that support --whole-archive. - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' else # Exported symbols can be pulled into shared objects from archives _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' fi _LT_TAGVAR(archive_cmds_need_lc, $1)=yes - # This is similar to how AIX traditionally builds its shared - # libraries. - _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + _LT_TAGVAR(archive_expsym_cmds, $1)='$RM -r $output_objdir/$realname.d~$MKDIR $output_objdir/$realname.d' + # -brtl affects multiple linker settings, -berok does not and is overridden later + compiler_flags_filtered='`func_echo_all "$compiler_flags " | $SED -e "s%-brtl\\([[, ]]\\)%-berok\\1%g"`' + if test svr4 != "$with_aix_soname"; then + # This is similar to how AIX traditionally builds its shared + # libraries. Need -bnortl late, we may have -brtl in LDFLAGS. + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_aix' -o $output_objdir/$realname.d/$soname $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$realname.d/$soname' + fi + if test aix != "$with_aix_soname"; then + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$CC '$shared_flag_svr4' -o $output_objdir/$realname.d/$shared_archive_member_spec.o $libobjs $deplibs $wl-bnoentry '$compiler_flags_filtered'$wl-bE:$export_symbols$allow_undefined_flag~$STRIP -e $output_objdir/$realname.d/$shared_archive_member_spec.o~( func_echo_all "#! $soname($shared_archive_member_spec.o)"; if test shr_64 = "$shared_archive_member_spec"; then func_echo_all "# 64"; else func_echo_all "# 32"; fi; cat $export_symbols ) > $output_objdir/$realname.d/$shared_archive_member_spec.imp~$AR $AR_FLAGS $output_objdir/$soname $output_objdir/$realname.d/$shared_archive_member_spec.o $output_objdir/$realname.d/$shared_archive_member_spec.imp' + else + # used by -dlpreopen to get the symbols + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$MV $output_objdir/$realname.d/$soname $output_objdir' + fi + _LT_TAGVAR(archive_expsym_cmds, $1)="$_LT_TAGVAR(archive_expsym_cmds, $1)"'~$RM -r $output_objdir/$realname.d' fi fi ;; @@ -6050,7 +6639,7 @@ if test "$_lt_caught_CXX_error" != yes; then _LT_TAGVAR(allow_undefined_flag, $1)=unsupported # Joseph Beckenbach says some releases of gcc # support --undefined. This deserves some investigation. FIXME - _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi @@ -6078,57 +6667,58 @@ if test "$_lt_caught_CXX_error" != yes; then # Tell ltmain to make .lib files, not .a files. libext=lib # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" + shrext_cmds=.dll # FIXME: Setting linknames here is a bad hack. - _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-dll~linknames=' - _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - $SED -n -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' -e '1\\\!p' < $export_symbols > $output_objdir/$soname.exp; - else - $SED -e 's/\\\\\\\(.*\\\\\\\)/-link\\\ -EXPORT:\\\\\\\1/' < $export_symbols > $output_objdir/$soname.exp; - fi~ - $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ - linknames=' + _LT_TAGVAR(archive_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $compiler_flags $deplibs -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~linknames=' + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp "$export_symbols" "$output_objdir/$soname.def"; + echo "$tool_output_objdir$soname.def" > "$output_objdir/$soname.exp"; + else + $SED -e '\''s/^/-link -EXPORT:/'\'' < $export_symbols > $output_objdir/$soname.exp; + fi~ + $CC -o $tool_output_objdir$soname $libobjs $compiler_flags $deplibs "@$tool_output_objdir$soname.exp" -Wl,-DLL,-IMPLIB:"$tool_output_objdir$libname.dll.lib"~ + linknames=' # The linker will not automatically build a static lib if we build a DLL. # _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes # Don't use ranlib _LT_TAGVAR(old_postinstall_cmds, $1)='chmod 644 $oldlib' _LT_TAGVAR(postlink_cmds, $1)='lt_outputfile="@OUTPUT@"~ - lt_tool_outputfile="@TOOL_OUTPUT@"~ - case $lt_outputfile in - *.exe|*.EXE) ;; - *) - lt_outputfile="$lt_outputfile.exe" - lt_tool_outputfile="$lt_tool_outputfile.exe" - ;; - esac~ - func_to_tool_file "$lt_outputfile"~ - if test "$MANIFEST_TOOL" != ":" && test -f "$lt_outputfile.manifest"; then - $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; - $RM "$lt_outputfile.manifest"; - fi' + lt_tool_outputfile="@TOOL_OUTPUT@"~ + case $lt_outputfile in + *.exe|*.EXE) ;; + *) + lt_outputfile=$lt_outputfile.exe + lt_tool_outputfile=$lt_tool_outputfile.exe + ;; + esac~ + func_to_tool_file "$lt_outputfile"~ + if test : != "$MANIFEST_TOOL" && test -f "$lt_outputfile.manifest"; then + $MANIFEST_TOOL -manifest "$lt_tool_outputfile.manifest" -outputresource:"$lt_tool_outputfile" || exit 1; + $RM "$lt_outputfile.manifest"; + fi' ;; *) # g++ # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, # as there is no search path for DLLs. _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-all-symbols' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-all-symbols' _LT_TAGVAR(allow_undefined_flag, $1)=unsupported _LT_TAGVAR(always_export_symbols, $1)=no _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file, use it as + # is; otherwise, prepend EXPORTS... + _LT_TAGVAR(archive_expsym_cmds, $1)='if _LT_DLL_DEF_P([$export_symbols]); then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname $wl--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' else _LT_TAGVAR(ld_shlibs, $1)=no fi @@ -6139,6 +6729,34 @@ if test "$_lt_caught_CXX_error" != yes; then _LT_DARWIN_LINKER_FEATURES($1) ;; + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + shrext_cmds=.dll + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + emxexp $libobjs | $SED /"_DLL_InitTerm"/d >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(archive_expsym_cmds, $1)='$ECHO "LIBRARY ${soname%$shared_ext} INITINSTANCE TERMINSTANCE" > $output_objdir/$libname.def~ + $ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~ + $ECHO "DATA MULTIPLE NONSHARED" >> $output_objdir/$libname.def~ + $ECHO EXPORTS >> $output_objdir/$libname.def~ + prefix_cmds="$SED"~ + if test EXPORTS = "`$SED 1q $export_symbols`"; then + prefix_cmds="$prefix_cmds -e 1d"; + fi~ + prefix_cmds="$prefix_cmds -e \"s/^\(.*\)$/_\1/g\""~ + cat $export_symbols | $prefix_cmds >> $output_objdir/$libname.def~ + $CC -Zdll -Zcrtdll -o $output_objdir/$soname $libobjs $deplibs $compiler_flags $output_objdir/$libname.def~ + emximp -o $lib $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/${libname}_dll.a $output_objdir/$libname.def' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + dgux*) case $cc_basename in ec++*) @@ -6157,7 +6775,7 @@ if test "$_lt_caught_CXX_error" != yes; then esac ;; - freebsd[[12]]*) + freebsd2.*) # C++ shared libraries reported to be fairly broken before # switch to ELF _LT_TAGVAR(ld_shlibs, $1)=no @@ -6173,18 +6791,15 @@ if test "$_lt_caught_CXX_error" != yes; then _LT_TAGVAR(ld_shlibs, $1)=yes ;; - gnu*) - ;; - haiku*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(link_all_deplibs, $1)=yes ;; hpux9*) - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, # but as the default @@ -6196,7 +6811,7 @@ if test "$_lt_caught_CXX_error" != yes; then _LT_TAGVAR(ld_shlibs, $1)=no ;; aCC*) - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. @@ -6205,11 +6820,11 @@ if test "$_lt_caught_CXX_error" != yes; then # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) - if test "$GXX" = yes; then - _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + if test yes = "$GXX"; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib $pic_flag $wl+b $wl$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test "x$output_objdir/$soname" = "x$lib" || mv $output_objdir/$soname $lib' else # FIXME: insert proper C++ library support _LT_TAGVAR(ld_shlibs, $1)=no @@ -6219,15 +6834,15 @@ if test "$_lt_caught_CXX_error" != yes; then ;; hpux10*|hpux11*) - if test $with_gnu_ld = no; then - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl+b $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: case $host_cpu in hppa*64*|ia64*) ;; *) - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' ;; esac fi @@ -6253,13 +6868,13 @@ if test "$_lt_caught_CXX_error" != yes; then aCC*) case $host_cpu in hppa*64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) - _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -b $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac # Commands to make compiler produce verbose output that lists @@ -6270,20 +6885,20 @@ if test "$_lt_caught_CXX_error" != yes; then # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP " \-L"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) - if test "$GXX" = yes; then - if test $with_gnu_ld = no; then + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then case $host_cpu in hppa*64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC $wl+h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; ia64*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $pic_flag $wl+h $wl$soname $wl+b $wl$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' ;; esac fi @@ -6298,22 +6913,22 @@ if test "$_lt_caught_CXX_error" != yes; then interix[[3-9]]*) _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. # Instead, shared libraries are loaded at an image base (0x10000000 by # default) and relocated if they conflict, which is a slow very memory # consuming and fragmenting process. To avoid this, we pick a random, # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link # time. Moving up from 0x10000000 also allows more sbrk(2) space. - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s|^|_|" $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags $wl-h,$soname $wl--retain-symbols-file,$output_objdir/$soname.expsym $wl--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' ;; irix5* | irix6*) case $cc_basename in CC*) # SGI C++ - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' # Archives containing C++ object files must be created using # "CC -ar", where "CC" is the IRIX C++ compiler. This is @@ -6322,22 +6937,22 @@ if test "$_lt_caught_CXX_error" != yes; then _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' ;; *) - if test "$GXX" = yes; then - if test "$with_gnu_ld" = no; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + if test yes = "$GXX"; then + if test no = "$with_gnu_ld"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' else - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` -o $lib' fi fi _LT_TAGVAR(link_all_deplibs, $1)=yes ;; esac - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: _LT_TAGVAR(inherit_rpath, $1)=yes ;; - linux* | k*bsd*-gnu | kopensolaris*-gnu) + linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*) case $cc_basename in KCC*) # Kuck and Associates, Inc. (KAI) C++ Compiler @@ -6345,8 +6960,8 @@ if test "$_lt_caught_CXX_error" != yes; then # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. - _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib $wl-retain-symbols-file,$export_symbols; mv \$templib $lib' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. @@ -6355,10 +6970,10 @@ if test "$_lt_caught_CXX_error" != yes; then # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. - output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' # Archives containing C++ object files must be created using # "CC -Bstatic", where "CC" is the KAI C++ compiler. @@ -6372,59 +6987,59 @@ if test "$_lt_caught_CXX_error" != yes; then # earlier do not add the objects themselves. case `$CC -V 2>&1` in *"Version 7."*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 8.0 or newer tmp_idyn= case $host_cpu in ia64*) tmp_idyn=' -i_dynamic';; esac - _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac _LT_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive$convenience $wl--no-whole-archive' ;; pgCC* | pgcpp*) # Portland Group C++ compiler case `$CC -V` in *pgCC\ [[1-5]].* | *pgcpp\ [[1-5]].*) _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ - compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | sort | $NL2SP`"' _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ - $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ - $RANLIB $oldlib' + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | sort | $NL2SP`~ + $RANLIB $oldlib' _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ - $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ - rm -rf $tpldir~ - $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ - $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | sort | $NL2SP` $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; *) # Version 6 and above use weak symbols - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname $wl-retain-symbols-file $wl$export_symbols -o $lib' ;; esac - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl--rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' ;; cxx*) # Compaq C++ - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname -o $lib $wl-retain-symbols-file $wl$export_symbols' runpath_var=LD_RUN_PATH _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' @@ -6438,18 +7053,18 @@ if test "$_lt_caught_CXX_error" != yes; then # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "X$list" | $Xsed' ;; xl* | mpixl* | bgxl*) # IBM XL 8.0 on PPC, with GNU ld - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname -o $lib' + if test yes = "$supports_anon_versioning"; then _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags $wl-soname $wl$soname $wl-version-script $wl$output_objdir/$libname.ver -o $lib' fi ;; *) @@ -6457,10 +7072,10 @@ if test "$_lt_caught_CXX_error" != yes; then *Sun\ C*) # Sun C++ 5.9 _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' - _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file $wl$export_symbols' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; func_echo_all \"$new_convenience\"` $wl--no-whole-archive' _LT_TAGVAR(compiler_needs_object, $1)=yes # Not sure whether something based on @@ -6518,22 +7133,17 @@ if test "$_lt_caught_CXX_error" != yes; then _LT_TAGVAR(ld_shlibs, $1)=yes ;; - openbsd2*) - # C++ shared libraries are fairly broken - _LT_TAGVAR(ld_shlibs, $1)=no - ;; - - openbsd*) + openbsd* | bitrig*) if test -f /usr/libexec/ld.so; then _LT_TAGVAR(hardcode_direct, $1)=yes _LT_TAGVAR(hardcode_shlibpath_var, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=yes _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)=$wlarc'--whole-archive$convenience '$wlarc'--no-whole-archive' fi output_verbose_link_cmd=func_echo_all else @@ -6549,9 +7159,9 @@ if test "$_lt_caught_CXX_error" != yes; then # KCC will only create a shared library if the output file # ends with ".so" (or ".sl" for HP-UX), so rename the library # to its proper name (with version) after linking. - _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\$tempext\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Archives containing C++ object files must be created using @@ -6569,17 +7179,17 @@ if test "$_lt_caught_CXX_error" != yes; then cxx*) case $host in osf3*) - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && func_echo_all "${wl}-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $soname `test -n "$verstring" && func_echo_all "$wl-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' ;; *) _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && func_echo_all "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ - echo "-hidden">> $lib.exp~ - $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry ${output_objdir}/so_locations -o $lib~ - $RM $lib.exp' + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname $wl-input $wl$lib.exp `test -n "$verstring" && $ECHO "-set_version $verstring"` -update_registry $output_objdir/so_locations -o $lib~ + $RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' ;; esac @@ -6594,27 +7204,27 @@ if test "$_lt_caught_CXX_error" != yes; then # explicitly linking system object files so we need to strip them # from the output so that they don't get included in the library # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`func_echo_all "$templist" | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list= ; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; func_echo_all "$list"' ;; *) - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + if test yes,no = "$GXX,$with_gnu_ld"; then + _LT_TAGVAR(allow_undefined_flag, $1)=' $wl-expect_unresolved $wl\*' case $host in osf3*) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && func_echo_all "${wl}-set_version ${wl}$verstring"` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-msym $wl-soname $wl$soname `test -n "$verstring" && func_echo_all "$wl-set_version $wl$verstring"` $wl-update_registry $wl$output_objdir/so_locations -o $lib' ;; esac - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-rpath $wl$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=: # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else # FIXME: insert proper C++ library support @@ -6654,9 +7264,9 @@ if test "$_lt_caught_CXX_error" != yes; then # Sun C++ 4.2, 5.x and Centerline C++ _LT_TAGVAR(archive_cmds_need_lc,$1)=yes _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' - _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -G$allow_undefined_flag -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + $CC -G$allow_undefined_flag $wl-M $wl$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' _LT_TAGVAR(hardcode_shlibpath_var, $1)=no @@ -6664,7 +7274,7 @@ if test "$_lt_caught_CXX_error" != yes; then solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) # The compiler driver will combine and reorder linker options, - # but understands `-z linker_flag'. + # but understands '-z linker_flag'. # Supported since Solaris 2.6 (maybe 2.5.1?) _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; @@ -6681,42 +7291,42 @@ if test "$_lt_caught_CXX_error" != yes; then ;; gcx*) # Green Hills C++ Compiler - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' # The C++ compiler must be used to create the archive. _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' ;; *) # GNU C++ compiler with Solaris linker - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' + if test yes,no = "$GXX,$with_gnu_ld"; then + _LT_TAGVAR(no_undefined_flag, $1)=' $wl-z ${wl}defs' if $CC --version | $GREP -v '^2\.7' > /dev/null; then - _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -shared $pic_flag -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + $CC -shared $pic_flag -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' else - # g++ 2.7 appears to require `-G' NOT `-shared' on this + # g++ 2.7 appears to require '-G' NOT '-shared' on this # platform. - _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags $wl-h $wl$soname -o $lib' _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ - $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + $CC -G -nostdlib $wl-M $wl$lib.exp $wl-h $wl$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' # Commands to make compiler produce verbose output that lists # what "hidden" libraries, object files and flags are used when # linking a shared library. - output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP "\-L"' + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP -v "^Configured with:" | $GREP " \-L"' fi - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R $wl$libdir' case $host_os in solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; *) - _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + _LT_TAGVAR(whole_archive_flag_spec, $1)='$wl-z ${wl}allextract$convenience $wl-z ${wl}defaultextract' ;; esac fi @@ -6725,52 +7335,52 @@ if test "$_lt_caught_CXX_error" != yes; then ;; sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) - _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no runpath_var='LD_RUN_PATH' case $cc_basename in CC*) - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not + # Note: We CANNOT use -z defs as we might desire, because we do not # link with -lc, and that would cause any symbols used from libc to # always be unresolved, which means just about no library would # ever link correctly. If we're not using GNU ld we use -z text # though, which does catch some bad symbols but isn't as heavy-handed # as -z defs. - _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(no_undefined_flag, $1)='$wl-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='$wl-z,nodefs' _LT_TAGVAR(archive_cmds_need_lc, $1)=no _LT_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='$wl-R,$libdir' _LT_TAGVAR(hardcode_libdir_separator, $1)=':' _LT_TAGVAR(link_all_deplibs, $1)=yes - _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='$wl-Bexport' runpath_var='LD_RUN_PATH' case $cc_basename in CC*) - _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -G $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' _LT_TAGVAR(old_archive_cmds, $1)='$CC -Tprelink_objects $oldobjs~ - '"$_LT_TAGVAR(old_archive_cmds, $1)" + '"$_LT_TAGVAR(old_archive_cmds, $1)" _LT_TAGVAR(reload_cmds, $1)='$CC -Tprelink_objects $reload_objs~ - '"$_LT_TAGVAR(reload_cmds, $1)" + '"$_LT_TAGVAR(reload_cmds, $1)" ;; *) - _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $wl-Bexport:$export_symbols $wl-h,$soname -o $lib $libobjs $deplibs $compiler_flags' ;; esac ;; @@ -6801,10 +7411,10 @@ if test "$_lt_caught_CXX_error" != yes; then esac AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) - test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + test no = "$_LT_TAGVAR(ld_shlibs, $1)" && can_build_shared=no - _LT_TAGVAR(GCC, $1)="$GXX" - _LT_TAGVAR(LD, $1)="$LD" + _LT_TAGVAR(GCC, $1)=$GXX + _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change @@ -6831,7 +7441,7 @@ if test "$_lt_caught_CXX_error" != yes; then lt_cv_path_LD=$lt_save_path_LD lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld -fi # test "$_lt_caught_CXX_error" != yes +fi # test yes != "$_lt_caught_CXX_error" AC_LANG_POP ])# _LT_LANG_CXX_CONFIG @@ -6853,13 +7463,14 @@ AC_REQUIRE([_LT_DECL_SED]) AC_REQUIRE([_LT_PROG_ECHO_BACKSLASH]) func_stripname_cnf () { - case ${2} in - .*) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%\\\\${2}\$%%"`;; - *) func_stripname_result=`$ECHO "${3}" | $SED "s%^${1}%%; s%${2}\$%%"`;; + case @S|@2 in + .*) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%\\\\@S|@2\$%%"`;; + *) func_stripname_result=`$ECHO "@S|@3" | $SED "s%^@S|@1%%; s%@S|@2\$%%"`;; esac } # func_stripname_cnf ])# _LT_FUNC_STRIPNAME_CNF + # _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) # --------------------------------- # Figure out "hidden" library dependencies from verbose @@ -6918,12 +7529,18 @@ public class foo { } }; _LT_EOF +], [$1], [GO], [cat > conftest.$ac_ext <<_LT_EOF +package foo +func foo() { +} +_LT_EOF ]) _lt_libdeps_save_CFLAGS=$CFLAGS case "$CC $CFLAGS " in #( *\ -flto*\ *) CFLAGS="$CFLAGS -fno-lto" ;; *\ -fwhopr*\ *) CFLAGS="$CFLAGS -fno-whopr" ;; +*\ -fuse-linker-plugin*\ *) CFLAGS="$CFLAGS -fno-use-linker-plugin" ;; esac dnl Parse the compiler output and extract the necessary @@ -6937,13 +7554,13 @@ if AC_TRY_EVAL(ac_compile); then pre_test_object_deps_done=no for p in `eval "$output_verbose_link_cmd"`; do - case ${prev}${p} in + case $prev$p in -L* | -R* | -l*) # Some compilers place space between "-{L,R}" and the path. # Remove the space. - if test $p = "-L" || - test $p = "-R"; then + if test x-L = "$p" || + test x-R = "$p"; then prev=$p continue fi @@ -6959,16 +7576,16 @@ if AC_TRY_EVAL(ac_compile); then case $p in =*) func_stripname_cnf '=' '' "$p"; p=$lt_sysroot$func_stripname_result ;; esac - if test "$pre_test_object_deps_done" = no; then - case ${prev} in + if test no = "$pre_test_object_deps_done"; then + case $prev in -L | -R) # Internal compiler library paths should come after those # provided the user. The postdeps already come after the # user supplied libs so there is no need to process them. if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then - _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" + _LT_TAGVAR(compiler_lib_search_path, $1)=$prev$p else - _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} $prev$p" fi ;; # The "-l" case would never come before the object being @@ -6976,9 +7593,9 @@ if AC_TRY_EVAL(ac_compile); then esac else if test -z "$_LT_TAGVAR(postdeps, $1)"; then - _LT_TAGVAR(postdeps, $1)="${prev}${p}" + _LT_TAGVAR(postdeps, $1)=$prev$p else - _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} $prev$p" fi fi prev= @@ -6993,15 +7610,15 @@ if AC_TRY_EVAL(ac_compile); then continue fi - if test "$pre_test_object_deps_done" = no; then + if test no = "$pre_test_object_deps_done"; then if test -z "$_LT_TAGVAR(predep_objects, $1)"; then - _LT_TAGVAR(predep_objects, $1)="$p" + _LT_TAGVAR(predep_objects, $1)=$p else _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" fi else if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then - _LT_TAGVAR(postdep_objects, $1)="$p" + _LT_TAGVAR(postdep_objects, $1)=$p else _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" fi @@ -7032,51 +7649,6 @@ interix[[3-9]]*) _LT_TAGVAR(postdep_objects,$1)= _LT_TAGVAR(postdeps,$1)= ;; - -linux*) - case `$CC -V 2>&1 | sed 5q` in - *Sun\ C*) - # Sun C++ 5.9 - - # The more standards-conforming stlport4 library is - # incompatible with the Cstd library. Avoid specifying - # it if it's in CXXFLAGS. Ignore libCrun as - # -library=stlport4 depends on it. - case " $CXX $CXXFLAGS " in - *" -library=stlport4 "*) - solaris_use_stlport4=yes - ;; - esac - - if test "$solaris_use_stlport4" != yes; then - _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' - fi - ;; - esac - ;; - -solaris*) - case $cc_basename in - CC* | sunCC*) - # The more standards-conforming stlport4 library is - # incompatible with the Cstd library. Avoid specifying - # it if it's in CXXFLAGS. Ignore libCrun as - # -library=stlport4 depends on it. - case " $CXX $CXXFLAGS " in - *" -library=stlport4 "*) - solaris_use_stlport4=yes - ;; - esac - - # Adding this requires a known-good setup of shared libraries for - # Sun compiler versions before 5.6, else PIC objects from an old - # archive will be linked into the output, leading to subtle bugs. - if test "$solaris_use_stlport4" != yes; then - _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' - fi - ;; - esac - ;; esac ]) @@ -7085,7 +7657,7 @@ case " $_LT_TAGVAR(postdeps, $1) " in esac _LT_TAGVAR(compiler_lib_search_dirs, $1)= if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then - _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | $SED -e 's! -L! !g' -e 's!^ !!'` fi _LT_TAGDECL([], [compiler_lib_search_dirs], [1], [The directories searched by this compiler when creating a shared library]) @@ -7105,10 +7677,10 @@ _LT_TAGDECL([], [compiler_lib_search_path], [1], # -------------------------- # Ensure that the configuration variables for a Fortran 77 compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. +# to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_F77_CONFIG], [AC_LANG_PUSH(Fortran 77) -if test -z "$F77" || test "X$F77" = "Xno"; then +if test -z "$F77" || test no = "$F77"; then _lt_disable_F77=yes fi @@ -7120,7 +7692,6 @@ _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= -_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no @@ -7146,7 +7717,7 @@ _LT_TAGVAR(objext, $1)=$objext # the F77 compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. -if test "$_lt_disable_F77" != yes; then +if test yes != "$_lt_disable_F77"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t @@ -7168,7 +7739,7 @@ if test "$_lt_disable_F77" != yes; then _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. - lt_save_CC="$CC" + lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${F77-"f77"} @@ -7182,21 +7753,25 @@ if test "$_lt_disable_F77" != yes; then AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) - test "$can_build_shared" = "no" && enable_shared=no + test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) - test "$enable_shared" = yes && enable_static=no + test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac fi ;; esac @@ -7204,11 +7779,11 @@ if test "$_lt_disable_F77" != yes; then AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. - test "$enable_shared" = yes || enable_static=yes + test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) - _LT_TAGVAR(GCC, $1)="$G77" - _LT_TAGVAR(LD, $1)="$LD" + _LT_TAGVAR(GCC, $1)=$G77 + _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change @@ -7225,9 +7800,9 @@ if test "$_lt_disable_F77" != yes; then fi # test -n "$compiler" GCC=$lt_save_GCC - CC="$lt_save_CC" - CFLAGS="$lt_save_CFLAGS" -fi # test "$_lt_disable_F77" != yes + CC=$lt_save_CC + CFLAGS=$lt_save_CFLAGS +fi # test yes != "$_lt_disable_F77" AC_LANG_POP ])# _LT_LANG_F77_CONFIG @@ -7237,11 +7812,11 @@ AC_LANG_POP # ------------------------- # Ensure that the configuration variables for a Fortran compiler are # suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. +# to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_FC_CONFIG], [AC_LANG_PUSH(Fortran) -if test -z "$FC" || test "X$FC" = "Xno"; then +if test -z "$FC" || test no = "$FC"; then _lt_disable_FC=yes fi @@ -7253,7 +7828,6 @@ _LT_TAGVAR(export_dynamic_flag_spec, $1)= _LT_TAGVAR(hardcode_direct, $1)=no _LT_TAGVAR(hardcode_direct_absolute, $1)=no _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= -_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= _LT_TAGVAR(hardcode_libdir_separator, $1)= _LT_TAGVAR(hardcode_minus_L, $1)=no _LT_TAGVAR(hardcode_automatic, $1)=no @@ -7279,7 +7853,7 @@ _LT_TAGVAR(objext, $1)=$objext # the FC compiler isn't working. Some variables (like enable_shared) # are currently assumed to apply to all compilers on this platform, # and will be corrupted by setting them based on a non-working compiler. -if test "$_lt_disable_FC" != yes; then +if test yes != "$_lt_disable_FC"; then # Code to be used in simple compile tests lt_simple_compile_test_code="\ subroutine t @@ -7301,7 +7875,7 @@ if test "$_lt_disable_FC" != yes; then _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. - lt_save_CC="$CC" + lt_save_CC=$CC lt_save_GCC=$GCC lt_save_CFLAGS=$CFLAGS CC=${FC-"f95"} @@ -7317,21 +7891,25 @@ if test "$_lt_disable_FC" != yes; then AC_MSG_RESULT([$can_build_shared]) AC_MSG_CHECKING([whether to build shared libraries]) - test "$can_build_shared" = "no" && enable_shared=no + test no = "$can_build_shared" && enable_shared=no # On AIX, shared libraries and static libraries use the same namespace, and # are all built from PIC. case $host_os in aix3*) - test "$enable_shared" = yes && enable_static=no + test yes = "$enable_shared" && enable_static=no if test -n "$RANLIB"; then archive_cmds="$archive_cmds~\$RANLIB \$lib" postinstall_cmds='$RANLIB $lib' fi ;; aix[[4-9]]*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no + if test ia64 != "$host_cpu"; then + case $enable_shared,$with_aix_soname,$aix_use_runtimelinking in + yes,aix,yes) ;; # shared object as lib.so file only + yes,svr4,*) ;; # shared object as lib.so archive member only + yes,*) enable_static=no ;; # shared object in lib.a archive as well + esac fi ;; esac @@ -7339,11 +7917,11 @@ if test "$_lt_disable_FC" != yes; then AC_MSG_CHECKING([whether to build static libraries]) # Make sure either enable_shared or enable_static is yes. - test "$enable_shared" = yes || enable_static=yes + test yes = "$enable_shared" || enable_static=yes AC_MSG_RESULT([$enable_static]) - _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" - _LT_TAGVAR(LD, $1)="$LD" + _LT_TAGVAR(GCC, $1)=$ac_cv_fc_compiler_gnu + _LT_TAGVAR(LD, $1)=$LD ## CAVEAT EMPTOR: ## There is no encapsulation within the following macros, do not change @@ -7363,7 +7941,7 @@ if test "$_lt_disable_FC" != yes; then GCC=$lt_save_GCC CC=$lt_save_CC CFLAGS=$lt_save_CFLAGS -fi # test "$_lt_disable_FC" != yes +fi # test yes != "$_lt_disable_FC" AC_LANG_POP ])# _LT_LANG_FC_CONFIG @@ -7373,7 +7951,7 @@ AC_LANG_POP # -------------------------- # Ensure that the configuration variables for the GNU Java Compiler compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. +# to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_GCJ_CONFIG], [AC_REQUIRE([LT_PROG_GCJ])dnl AC_LANG_SAVE @@ -7407,7 +7985,7 @@ CC=${GCJ-"gcj"} CFLAGS=$GCJFLAGS compiler=$CC _LT_TAGVAR(compiler, $1)=$CC -_LT_TAGVAR(LD, $1)="$LD" +_LT_TAGVAR(LD, $1)=$LD _LT_CC_BASENAME([$compiler]) # GCJ did not exist at the time GCC didn't implicitly link libc in. @@ -7440,11 +8018,82 @@ CFLAGS=$lt_save_CFLAGS ])# _LT_LANG_GCJ_CONFIG +# _LT_LANG_GO_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Go compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to 'libtool'. +m4_defun([_LT_LANG_GO_CONFIG], +[AC_REQUIRE([LT_PROG_GO])dnl +AC_LANG_SAVE + +# Source file extension for Go test sources. +ac_ext=go + +# Object file extension for compiled Go test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="package main; func main() { }" + +# Code to be used in simple link tests +lt_simple_link_test_code='package main; func main() { }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC=$CC +lt_save_CFLAGS=$CFLAGS +lt_save_GCC=$GCC +GCC=yes +CC=${GOC-"gccgo"} +CFLAGS=$GOFLAGS +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)=$LD +_LT_CC_BASENAME([$compiler]) + +# Go did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(reload_flag, $1)=$reload_flag +_LT_TAGVAR(reload_cmds, $1)=$reload_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC=$lt_save_CC +CFLAGS=$lt_save_CFLAGS +])# _LT_LANG_GO_CONFIG + + # _LT_LANG_RC_CONFIG([TAG]) # ------------------------- # Ensure that the configuration variables for the Windows resource compiler # are suitably defined. These variables are subsequently used by _LT_CONFIG -# to write the compiler configuration to `libtool'. +# to write the compiler configuration to 'libtool'. m4_defun([_LT_LANG_RC_CONFIG], [AC_REQUIRE([LT_PROG_RC])dnl AC_LANG_SAVE @@ -7460,7 +8109,7 @@ _LT_TAGVAR(objext, $1)=$objext lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' # Code to be used in simple link tests -lt_simple_link_test_code="$lt_simple_compile_test_code" +lt_simple_link_test_code=$lt_simple_compile_test_code # ltmain only uses $CC for tagged configurations so make sure $CC is set. _LT_TAG_COMPILER @@ -7470,7 +8119,7 @@ _LT_COMPILER_BOILERPLATE _LT_LINKER_BOILERPLATE # Allow CC to be a program name with arguments. -lt_save_CC="$CC" +lt_save_CC=$CC lt_save_CFLAGS=$CFLAGS lt_save_GCC=$GCC GCC= @@ -7499,7 +8148,7 @@ AC_DEFUN([LT_PROG_GCJ], [m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], [AC_CHECK_TOOL(GCJ, gcj,) - test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" + test set = "${GCJFLAGS+set}" || GCJFLAGS="-g -O2" AC_SUBST(GCJFLAGS)])])[]dnl ]) @@ -7509,6 +8158,13 @@ dnl aclocal-1.4 backwards compatibility: dnl AC_DEFUN([LT_AC_PROG_GCJ], []) +# LT_PROG_GO +# ---------- +AC_DEFUN([LT_PROG_GO], +[AC_CHECK_TOOL(GOC, gccgo,) +]) + + # LT_PROG_RC # ---------- AC_DEFUN([LT_PROG_RC], @@ -7603,7 +8259,7 @@ lt_ac_count=0 # Add /usr/xpg4/bin/sed as it is typically found on Solaris # along with /bin/sed that truncates output. for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do - test ! -f $lt_ac_sed && continue + test ! -f "$lt_ac_sed" && continue cat /dev/null > conftest.in lt_ac_count=0 echo $ECHO_N "0123456789$ECHO_C" >conftest.in @@ -7620,9 +8276,9 @@ for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break cmp -s conftest.out conftest.nl || break # 10000 chars as input seems more than enough - test $lt_ac_count -gt 10 && break + test 10 -lt "$lt_ac_count" && break lt_ac_count=`expr $lt_ac_count + 1` - if test $lt_ac_count -gt $lt_ac_max; then + if test "$lt_ac_count" -gt "$lt_ac_max"; then lt_ac_max=$lt_ac_count lt_cv_path_SED=$lt_ac_sed fi @@ -7646,27 +8302,7 @@ dnl AC_DEFUN([LT_AC_PROG_SED], []) # Find out whether the shell is Bourne or XSI compatible, # or has some other useful features. m4_defun([_LT_CHECK_SHELL_FEATURES], -[AC_MSG_CHECKING([whether the shell understands some XSI constructs]) -# Try some XSI features -xsi_shell=no -( _lt_dummy="a/b/c" - test "${_lt_dummy##*/},${_lt_dummy%/*},${_lt_dummy#??}"${_lt_dummy%"$_lt_dummy"}, \ - = c,a/b,b/c, \ - && eval 'test $(( 1 + 1 )) -eq 2 \ - && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ - && xsi_shell=yes -AC_MSG_RESULT([$xsi_shell]) -_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) - -AC_MSG_CHECKING([whether the shell understands "+="]) -lt_shell_append=no -( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ - >/dev/null 2>&1 \ - && lt_shell_append=yes -AC_MSG_RESULT([$lt_shell_append]) -_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) - -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then +[if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then lt_unset=unset else lt_unset=false @@ -7690,102 +8326,9 @@ _LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl ])# _LT_CHECK_SHELL_FEATURES -# _LT_PROG_FUNCTION_REPLACE (FUNCNAME, REPLACEMENT-BODY) -# ------------------------------------------------------ -# In `$cfgfile', look for function FUNCNAME delimited by `^FUNCNAME ()$' and -# '^} FUNCNAME ', and replace its body with REPLACEMENT-BODY. -m4_defun([_LT_PROG_FUNCTION_REPLACE], -[dnl { -sed -e '/^$1 ()$/,/^} # $1 /c\ -$1 ()\ -{\ -m4_bpatsubsts([$2], [$], [\\], [^\([ ]\)], [\\\1]) -} # Extended-shell $1 implementation' "$cfgfile" > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") -test 0 -eq $? || _lt_function_replace_fail=: -]) - - -# _LT_PROG_REPLACE_SHELLFNS -# ------------------------- -# Replace existing portable implementations of several shell functions with -# equivalent extended shell implementations where those features are available.. -m4_defun([_LT_PROG_REPLACE_SHELLFNS], -[if test x"$xsi_shell" = xyes; then - _LT_PROG_FUNCTION_REPLACE([func_dirname], [dnl - case ${1} in - */*) func_dirname_result="${1%/*}${2}" ;; - * ) func_dirname_result="${3}" ;; - esac]) - - _LT_PROG_FUNCTION_REPLACE([func_basename], [dnl - func_basename_result="${1##*/}"]) - - _LT_PROG_FUNCTION_REPLACE([func_dirname_and_basename], [dnl - case ${1} in - */*) func_dirname_result="${1%/*}${2}" ;; - * ) func_dirname_result="${3}" ;; - esac - func_basename_result="${1##*/}"]) - - _LT_PROG_FUNCTION_REPLACE([func_stripname], [dnl - # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are - # positional parameters, so assign one to ordinary parameter first. - func_stripname_result=${3} - func_stripname_result=${func_stripname_result#"${1}"} - func_stripname_result=${func_stripname_result%"${2}"}]) - - _LT_PROG_FUNCTION_REPLACE([func_split_long_opt], [dnl - func_split_long_opt_name=${1%%=*} - func_split_long_opt_arg=${1#*=}]) - - _LT_PROG_FUNCTION_REPLACE([func_split_short_opt], [dnl - func_split_short_opt_arg=${1#??} - func_split_short_opt_name=${1%"$func_split_short_opt_arg"}]) - - _LT_PROG_FUNCTION_REPLACE([func_lo2o], [dnl - case ${1} in - *.lo) func_lo2o_result=${1%.lo}.${objext} ;; - *) func_lo2o_result=${1} ;; - esac]) - - _LT_PROG_FUNCTION_REPLACE([func_xform], [ func_xform_result=${1%.*}.lo]) - - _LT_PROG_FUNCTION_REPLACE([func_arith], [ func_arith_result=$(( $[*] ))]) - - _LT_PROG_FUNCTION_REPLACE([func_len], [ func_len_result=${#1}]) -fi - -if test x"$lt_shell_append" = xyes; then - _LT_PROG_FUNCTION_REPLACE([func_append], [ eval "${1}+=\\${2}"]) - - _LT_PROG_FUNCTION_REPLACE([func_append_quoted], [dnl - func_quote_for_eval "${2}" -dnl m4 expansion turns \\\\ into \\, and then the shell eval turns that into \ - eval "${1}+=\\\\ \\$func_quote_for_eval_result"]) - - # Save a `func_append' function call where possible by direct use of '+=' - sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1+="%g' $cfgfile > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") - test 0 -eq $? || _lt_function_replace_fail=: -else - # Save a `func_append' function call even when '+=' is not available - sed -e 's%func_append \([[a-zA-Z_]]\{1,\}\) "%\1="$\1%g' $cfgfile > $cfgfile.tmp \ - && mv -f "$cfgfile.tmp" "$cfgfile" \ - || (rm -f "$cfgfile" && cp "$cfgfile.tmp" "$cfgfile" && rm -f "$cfgfile.tmp") - test 0 -eq $? || _lt_function_replace_fail=: -fi - -if test x"$_lt_function_replace_fail" = x":"; then - AC_MSG_WARN([Unable to substitute extended shell functions in $ofile]) -fi -]) - # _LT_PATH_CONVERSION_FUNCTIONS # ----------------------------- -# Determine which file name conversion functions should be used by +# Determine what file name conversion functions should be used by # func_to_host_file (and, implicitly, by func_to_host_path). These are needed # for certain cross-compile configurations and native mingw. m4_defun([_LT_PATH_CONVERSION_FUNCTIONS], diff --git a/m4/ltoptions.m4 b/m4/ltoptions.m4 index 17cfd51..94b0829 100644 --- a/m4/ltoptions.m4 +++ b/m4/ltoptions.m4 @@ -1,14 +1,14 @@ # Helper functions for option handling. -*- Autoconf -*- # -# Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 2004-2005, 2007-2009, 2011-2015 Free Software +# Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives # unlimited permission to copy and/or distribute it, with or without # modifications, as long as this notice is preserved. -# serial 7 ltoptions.m4 +# serial 8 ltoptions.m4 # This is to help aclocal find these macros, as it can't see m4_define. AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) @@ -29,7 +29,7 @@ m4_define([_LT_SET_OPTION], [m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), _LT_MANGLE_DEFUN([$1], [$2]), - [m4_warning([Unknown $1 option `$2'])])[]dnl + [m4_warning([Unknown $1 option '$2'])])[]dnl ]) @@ -75,13 +75,15 @@ m4_if([$1],[LT_INIT],[ dnl dnl If no reference was made to various pairs of opposing options, then dnl we run the default mode handler for the pair. For example, if neither - dnl `shared' nor `disable-shared' was passed, we enable building of shared + dnl 'shared' nor 'disable-shared' was passed, we enable building of shared dnl archives by default: _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], - [_LT_ENABLE_FAST_INSTALL]) + [_LT_ENABLE_FAST_INSTALL]) + _LT_UNLESS_OPTIONS([LT_INIT], [aix-soname=aix aix-soname=both aix-soname=svr4], + [_LT_WITH_AIX_SONAME([aix])]) ]) ])# _LT_SET_OPTIONS @@ -112,7 +114,7 @@ AU_DEFUN([AC_LIBTOOL_DLOPEN], [_LT_SET_OPTION([LT_INIT], [dlopen]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you -put the `dlopen' option into LT_INIT's first parameter.]) +put the 'dlopen' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: @@ -148,7 +150,7 @@ AU_DEFUN([AC_LIBTOOL_WIN32_DLL], _LT_SET_OPTION([LT_INIT], [win32-dll]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you -put the `win32-dll' option into LT_INIT's first parameter.]) +put the 'win32-dll' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: @@ -157,9 +159,9 @@ dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) # _LT_ENABLE_SHARED([DEFAULT]) # ---------------------------- -# implement the --enable-shared flag, and supports the `shared' and -# `disable-shared' LT_INIT options. -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +# implement the --enable-shared flag, and supports the 'shared' and +# 'disable-shared' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_SHARED], [m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([shared], @@ -172,14 +174,14 @@ AC_ARG_ENABLE([shared], *) enable_shared=no # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do - IFS="$lt_save_ifs" + IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_shared=yes fi done - IFS="$lt_save_ifs" + IFS=$lt_save_ifs ;; esac], [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) @@ -211,9 +213,9 @@ dnl AC_DEFUN([AM_DISABLE_SHARED], []) # _LT_ENABLE_STATIC([DEFAULT]) # ---------------------------- -# implement the --enable-static flag, and support the `static' and -# `disable-static' LT_INIT options. -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +# implement the --enable-static flag, and support the 'static' and +# 'disable-static' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_STATIC], [m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([static], @@ -226,14 +228,14 @@ AC_ARG_ENABLE([static], *) enable_static=no # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do - IFS="$lt_save_ifs" + IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_static=yes fi done - IFS="$lt_save_ifs" + IFS=$lt_save_ifs ;; esac], [enable_static=]_LT_ENABLE_STATIC_DEFAULT) @@ -265,9 +267,9 @@ dnl AC_DEFUN([AM_DISABLE_STATIC], []) # _LT_ENABLE_FAST_INSTALL([DEFAULT]) # ---------------------------------- -# implement the --enable-fast-install flag, and support the `fast-install' -# and `disable-fast-install' LT_INIT options. -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +# implement the --enable-fast-install flag, and support the 'fast-install' +# and 'disable-fast-install' LT_INIT options. +# DEFAULT is either 'yes' or 'no'. If omitted, it defaults to 'yes'. m4_define([_LT_ENABLE_FAST_INSTALL], [m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl AC_ARG_ENABLE([fast-install], @@ -280,14 +282,14 @@ AC_ARG_ENABLE([fast-install], *) enable_fast_install=no # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, for pkg in $enableval; do - IFS="$lt_save_ifs" + IFS=$lt_save_ifs if test "X$pkg" = "X$p"; then enable_fast_install=yes fi done - IFS="$lt_save_ifs" + IFS=$lt_save_ifs ;; esac], [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) @@ -304,14 +306,14 @@ AU_DEFUN([AC_ENABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put -the `fast-install' option into LT_INIT's first parameter.]) +the 'fast-install' option into LT_INIT's first parameter.]) ]) AU_DEFUN([AC_DISABLE_FAST_INSTALL], [_LT_SET_OPTION([LT_INIT], [disable-fast-install]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you put -the `disable-fast-install' option into LT_INIT's first parameter.]) +the 'disable-fast-install' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: @@ -319,19 +321,85 @@ dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) +# _LT_WITH_AIX_SONAME([DEFAULT]) +# ---------------------------------- +# implement the --with-aix-soname flag, and support the `aix-soname=aix' +# and `aix-soname=both' and `aix-soname=svr4' LT_INIT options. DEFAULT +# is either `aix', `both' or `svr4'. If omitted, it defaults to `aix'. +m4_define([_LT_WITH_AIX_SONAME], +[m4_define([_LT_WITH_AIX_SONAME_DEFAULT], [m4_if($1, svr4, svr4, m4_if($1, both, both, aix))])dnl +shared_archive_member_spec= +case $host,$enable_shared in +power*-*-aix[[5-9]]*,yes) + AC_MSG_CHECKING([which variant of shared library versioning to provide]) + AC_ARG_WITH([aix-soname], + [AS_HELP_STRING([--with-aix-soname=aix|svr4|both], + [shared library versioning (aka "SONAME") variant to provide on AIX, @<:@default=]_LT_WITH_AIX_SONAME_DEFAULT[@:>@.])], + [case $withval in + aix|svr4|both) + ;; + *) + AC_MSG_ERROR([Unknown argument to --with-aix-soname]) + ;; + esac + lt_cv_with_aix_soname=$with_aix_soname], + [AC_CACHE_VAL([lt_cv_with_aix_soname], + [lt_cv_with_aix_soname=]_LT_WITH_AIX_SONAME_DEFAULT) + with_aix_soname=$lt_cv_with_aix_soname]) + AC_MSG_RESULT([$with_aix_soname]) + if test aix != "$with_aix_soname"; then + # For the AIX way of multilib, we name the shared archive member + # based on the bitwidth used, traditionally 'shr.o' or 'shr_64.o', + # and 'shr.imp' or 'shr_64.imp', respectively, for the Import File. + # Even when GNU compilers ignore OBJECT_MODE but need '-maix64' flag, + # the AIX toolchain works better with OBJECT_MODE set (default 32). + if test 64 = "${OBJECT_MODE-32}"; then + shared_archive_member_spec=shr_64 + else + shared_archive_member_spec=shr + fi + fi + ;; +*) + with_aix_soname=aix + ;; +esac + +_LT_DECL([], [shared_archive_member_spec], [0], + [Shared archive member basename, for filename based shared library versioning on AIX])dnl +])# _LT_WITH_AIX_SONAME + +LT_OPTION_DEFINE([LT_INIT], [aix-soname=aix], [_LT_WITH_AIX_SONAME([aix])]) +LT_OPTION_DEFINE([LT_INIT], [aix-soname=both], [_LT_WITH_AIX_SONAME([both])]) +LT_OPTION_DEFINE([LT_INIT], [aix-soname=svr4], [_LT_WITH_AIX_SONAME([svr4])]) + + # _LT_WITH_PIC([MODE]) # -------------------- -# implement the --with-pic flag, and support the `pic-only' and `no-pic' +# implement the --with-pic flag, and support the 'pic-only' and 'no-pic' # LT_INIT options. -# MODE is either `yes' or `no'. If omitted, it defaults to `both'. +# MODE is either 'yes' or 'no'. If omitted, it defaults to 'both'. m4_define([_LT_WITH_PIC], [AC_ARG_WITH([pic], - [AS_HELP_STRING([--with-pic], + [AS_HELP_STRING([--with-pic@<:@=PKGS@:>@], [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], - [pic_mode="$withval"], - [pic_mode=default]) - -test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) + [lt_p=${PACKAGE-default} + case $withval in + yes|no) pic_mode=$withval ;; + *) + pic_mode=default + # Look at the argument we got. We use all the common list separators. + lt_save_ifs=$IFS; IFS=$IFS$PATH_SEPARATOR, + for lt_pkg in $withval; do + IFS=$lt_save_ifs + if test "X$lt_pkg" = "X$lt_p"; then + pic_mode=yes + fi + done + IFS=$lt_save_ifs + ;; + esac], + [pic_mode=m4_default([$1], [default])]) _LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl ])# _LT_WITH_PIC @@ -344,7 +412,7 @@ AU_DEFUN([AC_LIBTOOL_PICMODE], [_LT_SET_OPTION([LT_INIT], [pic-only]) AC_DIAGNOSE([obsolete], [$0: Remove this warning and the call to _LT_SET_OPTION when you -put the `pic-only' option into LT_INIT's first parameter.]) +put the 'pic-only' option into LT_INIT's first parameter.]) ]) dnl aclocal-1.4 backwards compatibility: diff --git a/m4/ltsugar.m4 b/m4/ltsugar.m4 index 9000a05..48bc934 100644 --- a/m4/ltsugar.m4 +++ b/m4/ltsugar.m4 @@ -1,6 +1,7 @@ # ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- # -# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. +# Copyright (C) 2004-2005, 2007-2008, 2011-2015 Free Software +# Foundation, Inc. # Written by Gary V. Vaughan, 2004 # # This file is free software; the Free Software Foundation gives @@ -33,7 +34,7 @@ m4_define([_lt_join], # ------------ # Manipulate m4 lists. # These macros are necessary as long as will still need to support -# Autoconf-2.59 which quotes differently. +# Autoconf-2.59, which quotes differently. m4_define([lt_car], [[$1]]) m4_define([lt_cdr], [m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], @@ -44,7 +45,7 @@ m4_define([lt_unquote], $1) # lt_append(MACRO-NAME, STRING, [SEPARATOR]) # ------------------------------------------ -# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. +# Redefine MACRO-NAME to hold its former content plus 'SEPARATOR''STRING'. # Note that neither SEPARATOR nor STRING are expanded; they are appended # to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). # No SEPARATOR is output if MACRO-NAME was previously undefined (different diff --git a/m4/ltversion.m4 b/m4/ltversion.m4 index 9c7b5d4..fa04b52 100644 --- a/m4/ltversion.m4 +++ b/m4/ltversion.m4 @@ -1,6 +1,6 @@ # ltversion.m4 -- version numbers -*- Autoconf -*- # -# Copyright (C) 2004 Free Software Foundation, Inc. +# Copyright (C) 2004, 2011-2015 Free Software Foundation, Inc. # Written by Scott James Remnant, 2004 # # This file is free software; the Free Software Foundation gives @@ -9,15 +9,15 @@ # @configure_input@ -# serial 3293 ltversion.m4 +# serial 4179 ltversion.m4 # This file is part of GNU Libtool -m4_define([LT_PACKAGE_VERSION], [2.4]) -m4_define([LT_PACKAGE_REVISION], [1.3293]) +m4_define([LT_PACKAGE_VERSION], [2.4.6]) +m4_define([LT_PACKAGE_REVISION], [2.4.6]) AC_DEFUN([LTVERSION_VERSION], -[macro_version='2.4' -macro_revision='1.3293' +[macro_version='2.4.6' +macro_revision='2.4.6' _LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) _LT_DECL(, macro_revision, 0) ]) diff --git a/m4/lt~obsolete.m4 b/m4/lt~obsolete.m4 index c573da9..c6b26f8 100644 --- a/m4/lt~obsolete.m4 +++ b/m4/lt~obsolete.m4 @@ -1,6 +1,7 @@ # lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- # -# Copyright (C) 2004, 2005, 2007, 2009 Free Software Foundation, Inc. +# Copyright (C) 2004-2005, 2007, 2009, 2011-2015 Free Software +# Foundation, Inc. # Written by Scott James Remnant, 2004. # # This file is free software; the Free Software Foundation gives @@ -11,7 +12,7 @@ # These exist entirely to fool aclocal when bootstrapping libtool. # -# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN), # which have later been changed to m4_define as they aren't part of the # exported API, or moved to Autoconf or Automake where they belong. # @@ -25,7 +26,7 @@ # included after everything else. This provides aclocal with the # AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything # because those macros already exist, or will be overwritten later. -# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. # # Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. # Yes, that means every name once taken will need to remain here until diff --git a/m4/nut_arg_with.m4 b/m4/nut_arg_with.m4 index 9d1c36b..686306b 100644 --- a/m4/nut_arg_with.m4 +++ b/m4/nut_arg_with.m4 @@ -1,9 +1,21 @@ dnl simplified declaration of some feature options +dnl Working With External Software (might name a variant or other contextual arg) +dnl https://www.gnu.org/software/autoconf/manual/autoconf-2.66/html_node/External-Software.html#External-Software AC_DEFUN([NUT_ARG_WITH], [ AC_ARG_WITH($1, - AC_HELP_STRING([--with-$1], [$2 ($3)]), + AS_HELP_STRING([--with-$1], [$2 ($3)]), [nut_with_$1="${withval}"], [nut_with_$1="$3"] ) ]) + +dnl Enable a feature (might name a variant), or yes/no +dnl https://www.gnu.org/software/autoconf/manual/autoconf-2.66/html_node/Package-Options.html +AC_DEFUN([NUT_ARG_ENABLE], +[ AC_ARG_ENABLE($1, + AS_HELP_STRING([--enable-$1], [$2 ($3)]), + [nut_enable_$1="${enableval}"], + [nut_enable_$1="$3"] + ) +]) diff --git a/m4/nut_check_asciidoc.m4 b/m4/nut_check_asciidoc.m4 index 944870e..b83381e 100644 --- a/m4/nut_check_asciidoc.m4 +++ b/m4/nut_check_asciidoc.m4 @@ -1,12 +1,24 @@ -dnl Check for LIBUSB compiler flags. On success, set nut_have_libusb="yes" -dnl and set LIBUSB_CFLAGS and LIBUSB_LDFLAGS. On failure, set -dnl nut_have_libusb="no". This macro can be run multiple times, but will -dnl do the checking only once. +dnl Check for tools used in generation of documentation final-format files +dnl such as the basically required MAN, and optional HTML (single or chunked). +dnl On success, set nut_have_asciidoc="yes" (meaning we can do at least some +dnl documentation generation) and lots of automake macros and configure vars. +dnl On failure, set nut_have_asciidoc="no" (meaning we can't generate even a +dnl manpage, which is a requirement for proceeding to the other formats). +dnl This macro can be run multiple times, but will do the checking only once. +dnl Also note that this routine currently only checks the basic presence (and +dnl versions) of the required software; the configure script has additional +dnl functional checks (ability to build doc files) based on --with-doc request +dnl so as to not waste time on doc-types not requested by maintainer, and done +dnl in ways more intimate to NUT (using its tracked docs). -AC_DEFUN([NUT_CHECK_ASCIIDOC], +AC_DEFUN([NUT_CHECK_ASCIIDOC], [ if test -z "${nut_have_asciidoc_seen}"; then nut_have_asciidoc_seen=yes + # Note: this is for both asciidoc and a2x at this time + ASCIIDOC_MIN_VERSION="8.6.3" + # Note: this is checked in the configure script if PDF is of interest at all + DBLATEX_MIN_VERSION="0.2.5" AC_PATH_PROGS([ASCIIDOC], [asciidoc]) if test -n "${ASCIIDOC}"; then @@ -16,6 +28,7 @@ if test -z "${nut_have_asciidoc_seen}"; then ASCIIDOC_VERSION="${ASCIIDOC_VERSION##* }" AC_MSG_RESULT(${ASCIIDOC_VERSION} found) fi + AM_CONDITIONAL([MANUALUPDATE], [test -n "${ASCIIDOC}"]) AC_PATH_PROGS([A2X], [a2x]) if test -n "${A2X}"; then @@ -35,6 +48,89 @@ if test -z "${nut_have_asciidoc_seen}"; then AC_MSG_RESULT(${DBLATEX_VERSION} found) fi - dnl FIXME check for xsltproc, xmlllint, etc for chunked HTML and man pages + AC_PATH_PROGS([XSLTPROC], [xsltproc]) + if test -n "${XSLTPROC}"; then + AC_MSG_CHECKING([for xsltproc version]) + XSLTPROC_VERSION="`${XSLTPROC} --version 2>/dev/null`" + dnl strip 'xsltproc version ' from version string + XSLTPROC_VERSION="${XSLTPROC_VERSION##* }" + AC_MSG_RESULT(${XSLTPROC_VERSION} found) + fi + + AC_PATH_PROGS([XMLLINT], [xmllint]) + if test -n "${XMLLINT}"; then + AC_MSG_CHECKING([for xmllint version]) + XMLLINT_VERSION="`${XMLLINT} --version 2>/dev/null`" + dnl strip 'xmllint version ' from version string + XMLLINT_VERSION="${XMLLINT_VERSION##* }" + if test -z "${XMLLINT_VERSION}" ; then + dnl Some releases also report what flags they were compiled with as + dnl part of the version info, so the last-line match finds nothing. + dnl Also some builds return version data to stderr. + XMLLINT_VERSION="`${XMLLINT} --version 2>&1 | grep version`" + XMLLINT_VERSION="${XMLLINT_VERSION##* }" + fi + AC_MSG_RESULT(${XMLLINT_VERSION} found) + fi + + AC_PATH_PROGS([SOURCE_HIGHLIGHT], [source-highlight]) + AM_CONDITIONAL([HAVE_SOURCE_HIGHLIGHT], [test -n "$SOURCE_HIGHLIGHT"]) + + dnl check for spell checking deps + AC_PATH_PROGS([ASPELL], [aspell]) + AM_CONDITIONAL([HAVE_ASPELL], [test -n "$ASPELL"]) + + dnl Note that a common "nut_have_asciidoc" variable is in fact a flag + dnl that we have several tools needed for the documentation generation + dnl TODO? Rename the script variable and makefile flags to reflect this? + AC_MSG_CHECKING([if asciidoc version can build manpages (minimum required ${ASCIIDOC_MIN_VERSION})]) + AX_COMPARE_VERSION([${ASCIIDOC_VERSION}], [ge], [${ASCIIDOC_MIN_VERSION}], [ + AC_MSG_RESULT(yes) + nut_have_asciidoc="yes" + ], [ + AC_MSG_RESULT(no) + nut_have_asciidoc="no" + ]) + + AC_MSG_CHECKING([if a2x version can build manpages (minimum required ${ASCIIDOC_MIN_VERSION})]) + AX_COMPARE_VERSION([${A2X_VERSION}], [ge], [${ASCIIDOC_MIN_VERSION}], [ + AC_MSG_RESULT(yes) + ], [ + AC_MSG_RESULT(no) + nut_have_asciidoc="no" + ]) + + dnl TODO: test for docbook-xsl files (maybe build a test man page?) + dnl https://github.com/networkupstools/nut/issues/162 + AC_MSG_CHECKING([if xsltproc is present (mandatory for man page regeneration)]) + if test -n "${XSLTPROC}"; then + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + nut_have_asciidoc="no" + fi + + AC_MSG_CHECKING([if xmllint is present (mandatory for man page regeneration)]) + if test -n "${XMLLINT}"; then + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + nut_have_asciidoc="no" + fi + + dnl Notes: we also keep HAVE_ASCIIDOC for implicit targets, such as manpage + dnl building + AM_CONDITIONAL([HAVE_ASCIIDOC], [test "${nut_have_asciidoc}" = "yes"]) + + AC_MSG_CHECKING([if we have all the tools mandatory for man page regeneration]) + AC_MSG_RESULT([${nut_have_asciidoc}]) + + AC_MSG_CHECKING([if source-highlight is present (preferable for documentation generation)]) + if test -n "${SOURCE_HIGHLIGHT}"; then + AC_MSG_RESULT(yes) + else + AC_MSG_RESULT(no) + fi + fi ]) diff --git a/m4/nut_check_cppcheck.m4 b/m4/nut_check_cppcheck.m4 new file mode 100644 index 0000000..5a85758 --- /dev/null +++ b/m4/nut_check_cppcheck.m4 @@ -0,0 +1,34 @@ +dnl Check for "cppcheck" analysis tools + +AC_DEFUN([NUT_CHECK_CPPCHECK], +[ +if test -z "${nut_have_cppcheck_seen}"; then + dnl NOTE: Did not really investigate suitable CPPCHECK_MIN_VERSION + dnl values, just using current available as the baseline; maybe + dnl older releases are also okay (if someone proves so). + dnl The oldest available on the NUT CI build farm was 1.6 that worked. + nut_have_cppcheck_seen=yes + CPPCHECK_MIN_VERSION="1.0" + + AC_PATH_PROGS([CPPCHECK], [cppcheck]) + if test -n "${CPPCHECK}"; then + AC_MSG_CHECKING([for cppcheck version]) + CPPCHECK_VERSION="`${CPPCHECK} --version 2>/dev/null`" + dnl strip 'cppcheck ' from version string + CPPCHECK_VERSION="${CPPCHECK_VERSION##* }" + AC_MSG_RESULT(${CPPCHECK_VERSION} found) + fi + + AC_MSG_CHECKING([if cppcheck version is okay (minimum required ${CPPCHECK_MIN_VERSION})]) + AX_COMPARE_VERSION([${CPPCHECK_VERSION}], [ge], [${CPPCHECK_MIN_VERSION}], [ + AC_MSG_RESULT(yes) + nut_have_cppcheck="yes" + ], [ + AC_MSG_RESULT(no) + nut_have_cppcheck="no" + ]) + + dnl Notes: we also keep HAVE_CPPCHECK for implicit targets + AM_CONDITIONAL([HAVE_CPPCHECK], [test "${nut_have_cppcheck}" = "yes"]) +fi +]) diff --git a/m4/nut_check_headers_windows.m4 b/m4/nut_check_headers_windows.m4 new file mode 100644 index 0000000..6f30c92 --- /dev/null +++ b/m4/nut_check_headers_windows.m4 @@ -0,0 +1,193 @@ +dnl This code was lifted and adapted for NUT from cURL project: +dnl https://github.com/curl/curl/blob/83245d9ff352b742753cff1216781ff041e4e987/acinclude.m4#L172 +dnl https://github.com/curl/curl/blob/83245d9ff352b742753cff1216781ff041e4e987/acinclude.m4#L207 +dnl https://github.com/curl/curl/blob/83245d9ff352b742753cff1216781ff041e4e987/acinclude.m4#L238 +dnl https://github.com/curl/curl/blob/83245d9ff352b742753cff1216781ff041e4e987/acinclude.m4#L275 +dnl https://github.com/curl/curl/blob/83245d9ff352b742753cff1216781ff041e4e987/acinclude.m4#L312 + +dnl NUT_CHECK_HEADER_WINDOWS +dnl ------------------------------------------------- +dnl Check for compilable and valid windows.h header + +AC_DEFUN([NUT_CHECK_HEADER_WINDOWS], [ + AC_CACHE_CHECK([for windows.h], [nut_cv_header_windows_h], [ + AC_LANG_PUSH([C]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ +#undef inline +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include + ]],[[ +#if defined(__CYGWIN__) || defined(__CEGCC__) + HAVE_WINDOWS_H shall not be defined. +#else + int dummy=2*WINVER; +#endif + ]]) + ],[ + nut_cv_header_windows_h="yes" + ],[ + nut_cv_header_windows_h="no" + ]) + AC_LANG_POP([C]) + ]) + case "$nut_cv_header_windows_h" in + yes) + AC_DEFINE_UNQUOTED(HAVE_WINDOWS_H, 1, + [Define to 1 if you have the windows.h header file.]) + ;; + esac +]) + + +dnl NUT_CHECK_NATIVE_WINDOWS +dnl ------------------------------------------------- +dnl Check if building a native Windows target + +AC_DEFUN([NUT_CHECK_NATIVE_WINDOWS], [ + AC_REQUIRE([NUT_CHECK_HEADER_WINDOWS])dnl + AC_CACHE_CHECK([whether build target is a native Windows one], [nut_cv_native_windows], [ + if test "$nut_cv_header_windows_h" = "no"; then + nut_cv_native_windows="no" + else + AC_LANG_PUSH([C]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ + ]],[[ +#if defined(__MINGW32__) || defined(__MINGW32CE__) || \ + (defined(_MSC_VER) && (defined(_WIN32) || defined(_WIN64))) + int dummy=1; +#else + Not a native Windows build target. +#endif + ]]) + ],[ + nut_cv_native_windows="yes" + ],[ + nut_cv_native_windows="no" + ]) + AC_LANG_POP([C]) + fi + ]) + AM_CONDITIONAL(DOING_NATIVE_WINDOWS, test "x$nut_cv_native_windows" = xyes) +]) + + +dnl NUT_CHECK_HEADER_WINSOCK +dnl ------------------------------------------------- +dnl Check for compilable and valid winsock.h header + +AC_DEFUN([NUT_CHECK_HEADER_WINSOCK], [ + AC_REQUIRE([NUT_CHECK_HEADER_WINDOWS])dnl + AC_CACHE_CHECK([for winsock.h], [nut_cv_header_winsock_h], [ + AC_LANG_PUSH([C]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ +#undef inline +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include + ]],[[ +#if defined(__CYGWIN__) || defined(__CEGCC__) + HAVE_WINSOCK_H shall not be defined. +#else + int dummy=WSACleanup(); +#endif + ]]) + ],[ + nut_cv_header_winsock_h="yes" + ],[ + nut_cv_header_winsock_h="no" + ]) + AC_LANG_POP([C]) + ]) + case "$nut_cv_header_winsock_h" in + yes) + AC_DEFINE_UNQUOTED(HAVE_WINSOCK_H, 1, + [Define to 1 if you have the winsock.h header file.]) + ;; + esac +]) + + +dnl NUT_CHECK_HEADER_WINSOCK2 +dnl ------------------------------------------------- +dnl Check for compilable and valid winsock2.h header + +AC_DEFUN([NUT_CHECK_HEADER_WINSOCK2], [ + AC_REQUIRE([NUT_CHECK_HEADER_WINDOWS])dnl + AC_CACHE_CHECK([for winsock2.h], [nut_cv_header_winsock2_h], [ + AC_LANG_PUSH([C]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ +#undef inline +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include + ]],[[ +#if defined(__CYGWIN__) || defined(__CEGCC__) || defined(__MINGW32CE__) + HAVE_WINSOCK2_H shall not be defined. +#else + int dummy=2*IPPROTO_ESP; +#endif + ]]) + ],[ + nut_cv_header_winsock2_h="yes" + ],[ + nut_cv_header_winsock2_h="no" + ]) + AC_LANG_POP([C]) + ]) + case "$nut_cv_header_winsock2_h" in + yes) + AC_DEFINE_UNQUOTED(HAVE_WINSOCK2_H, 1, + [Define to 1 if you have the winsock2.h header file.]) + ;; + esac +]) + + +dnl NUT_CHECK_HEADER_WS2TCPIP +dnl ------------------------------------------------- +dnl Check for compilable and valid ws2tcpip.h header + +AC_DEFUN([NUT_CHECK_HEADER_WS2TCPIP], [ + AC_REQUIRE([NUT_CHECK_HEADER_WINSOCK2])dnl + AC_CACHE_CHECK([for ws2tcpip.h], [nut_cv_header_ws2tcpip_h], [ + AC_LANG_PUSH([C]) + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([[ +#undef inline +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include + ]],[[ +#if defined(__CYGWIN__) || defined(__CEGCC__) || defined(__MINGW32CE__) + HAVE_WS2TCPIP_H shall not be defined. +#else + int dummy=2*IP_PKTINFO; +#endif + ]]) + ],[ + nut_cv_header_ws2tcpip_h="yes" + ],[ + nut_cv_header_ws2tcpip_h="no" + ]) + AC_LANG_POP([C]) + ]) + case "$nut_cv_header_ws2tcpip_h" in + yes) + AC_DEFINE_UNQUOTED(HAVE_WS2TCPIP_H, 1, + [Define to 1 if you have the ws2tcpip.h header file.]) + ;; + esac +]) diff --git a/m4/nut_check_libavahi.m4 b/m4/nut_check_libavahi.m4 index 034d31a..d0c4989 100644 --- a/m4/nut_check_libavahi.m4 +++ b/m4/nut_check_libavahi.m4 @@ -1,4 +1,4 @@ -dnl Check for LIBAVAHI compiler flags. On success, set nut_have_neon="yes" +dnl Check for LIBAVAHI compiler flags. On success, set nut_have_avahi="yes" dnl and set LIBAVAHI_CFLAGS and LIBAVAHI_LIBS. On failure, set dnl nut_have_avahi="no". This macro can be run multiple times, but will dnl do the checking only once. @@ -7,25 +7,29 @@ AC_DEFUN([NUT_CHECK_LIBAVAHI], [ if test -z "${nut_have_avahi_seen}"; then nut_have_avahi_seen=yes + NUT_CHECK_PKGCONFIG dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" - dnl See which version of the avahi library (if any) is installed - AC_MSG_CHECKING(for avahi-core version via pkg-config (0.6.30 minimum required)) - AVAHI_CORE_VERSION="`pkg-config --silence-errors --modversion avahi-core 2>/dev/null`" - if test "$?" != "0" -o -z "${AVAHI_CORE_VERSION}"; then - AVAHI_CORE_VERSION="none" - fi - AC_MSG_RESULT(${AVAHI_CORE_VERSION} found) + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [dnl See which version of the avahi library (if any) is installed + AC_MSG_CHECKING(for avahi-core version via pkg-config (0.6.30 minimum required)) + AVAHI_CORE_VERSION="`$PKG_CONFIG --silence-errors --modversion avahi-core 2>/dev/null`" + if test "$?" != "0" -o -z "${AVAHI_CORE_VERSION}"; then + AVAHI_CORE_VERSION="none" + fi + AC_MSG_RESULT(${AVAHI_CORE_VERSION} found) - AC_MSG_CHECKING(for avahi-client version via pkg-config (0.6.30 minimum required)) - AVAHI_CLIENT_VERSION="`pkg-config --silence-errors --modversion avahi-client 2>/dev/null`" - if test "$?" != "0" -o -z "${AVAHI_CLIENT_VERSION}"; then - AVAHI_CLIENT_VERSION="none" - fi - AC_MSG_RESULT(${AVAHI_CLIENT_VERSION} found) + AC_MSG_CHECKING(for avahi-client version via pkg-config (0.6.30 minimum required)) + AVAHI_CLIENT_VERSION="`$PKG_CONFIG --silence-errors --modversion avahi-client 2>/dev/null`" + if test "$?" != "0" -o -z "${AVAHI_CLIENT_VERSION}"; then + AVAHI_CLIENT_VERSION="none" + fi + AC_MSG_RESULT(${AVAHI_CLIENT_VERSION} found) + ], [AC_MSG_NOTICE([can not check avahi settings via pkg-config])] + ) AC_MSG_CHECKING(for avahi cflags) AC_ARG_WITH(avahi-includes, @@ -39,7 +43,12 @@ if test -z "${nut_have_avahi_seen}"; then CFLAGS="${withval}" ;; esac - ], [CFLAGS="`pkg-config --silence-errors --cflags avahi-core avahi-client 2>/dev/null`"]) + ], [ + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [CFLAGS="`$PKG_CONFIG --silence-errors --cflags avahi-core avahi-client 2>/dev/null`" || CFLAGS="-I/usr/local/include -I/usr/include -L/usr/local/lib -L/usr/lib"], + [CFLAGS="-I/usr/local/include -I/usr/include -L/usr/local/lib -L/usr/lib"] + )] + ) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for avahi ldflags) @@ -54,7 +63,12 @@ if test -z "${nut_have_avahi_seen}"; then LIBS="${withval}" ;; esac - ], [LIBS="`pkg-config --silence-errors --libs avahi-core avahi-client 2>/dev/null`"]) + ], [ + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [LIBS="`$PKG_CONFIG --silence-errors --libs avahi-core avahi-client 2>/dev/null`" || LIBS="-lavahi-core -lavahi-client"], + [LIBS="-lavahi-core -lavahi-client"] + )] + ) AC_MSG_RESULT([${LIBS}]) dnl check if avahi-core is usable diff --git a/m4/nut_check_libfreeipmi.m4 b/m4/nut_check_libfreeipmi.m4 index c6d4163..5fad0c6 100644 --- a/m4/nut_check_libfreeipmi.m4 +++ b/m4/nut_check_libfreeipmi.m4 @@ -8,25 +8,36 @@ AC_DEFUN([NUT_CHECK_LIBFREEIPMI], [ if test -z "${nut_have_libfreeipmi_seen}"; then nut_have_libfreeipmi_seen=yes + NUT_CHECK_PKGCONFIG dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" - AC_MSG_CHECKING(for FreeIPMI version via pkg-config) - dnl pkg-config support requires Freeipmi 1.0.5, released on Thu Jun 30 2011 - dnl but NUT should only require 0.8.5 (for nut-scanner) and 1.0.1 (for - dnl nut-ipmipsu) (comment from upstream Al Chu) - FREEIPMI_VERSION="`pkg-config --silence-errors --modversion libfreeipmi 2>/dev/null`" - if test "$?" = "0" -a -n "${FREEIPMI_VERSION}"; then - CFLAGS="`pkg-config --silence-errors --cflags libfreeipmi libipmimonitoring 2>/dev/null`" - LIBS="`pkg-config --silence-errors --libs libfreeipmi libipmimonitoring 2>/dev/null`" - else - FREEIPMI_VERSION="none" - CFLAGS="" - LIBS="-lfreeipmi -lipmimonitoring" - fi - AC_MSG_RESULT(${FREEIPMI_VERSION} found) + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [dnl pkg-config support requires Freeipmi 1.0.5, released on Thu Jun 30 2011 + dnl but NUT should only require 0.8.5 (for nut-scanner) and 1.0.1 (for + dnl nut-ipmipsu) (comment from upstream Al Chu) + AC_MSG_CHECKING(for FreeIPMI version via pkg-config) + FREEIPMI_VERSION="`$PKG_CONFIG --silence-errors --modversion libfreeipmi 2>/dev/null`" + if test "$?" != "0" -o -z "${FREEIPMI_VERSION}"; then + FREEIPMI_VERSION="none" + fi + AC_MSG_RESULT(${FREEIPMI_VERSION} found) + ], + [FREEIPMI_VERSION="none" + AC_MSG_NOTICE([can not check FreeIPMI settings via pkg-config]) + ] + ) + + AS_IF([test x"$FREEIPMI_VERSION" != xnone], + [CFLAGS="`$PKG_CONFIG --silence-errors --cflags libfreeipmi libipmimonitoring 2>/dev/null`" + LIBS="`$PKG_CONFIG --silence-errors --libs libfreeipmi libipmimonitoring 2>/dev/null`" + ], + [CFLAGS="" + LIBS="-lfreeipmi -lipmimonitoring" + ] + ) dnl allow overriding FreeIPMI settings if the user knows best AC_MSG_CHECKING(for FreeIPMI cflags) @@ -66,11 +77,14 @@ if test -z "${nut_have_libfreeipmi_seen}"; then dnl when version cannot be tested (prior to 1.0.5, with no pkg-config) dnl we have to check for some specific functions AC_SEARCH_LIBS([ipmi_ctx_find_inband], [freeipmi], [], [nut_have_freeipmi=no]) - AC_SEARCH_LIBS([ipmi_fru_parse_ctx_create], [freeipmi], [], [nut_have_freeipmi=no]) AC_SEARCH_LIBS([ipmi_monitoring_init], [ipmimonitoring], [nut_have_freeipmi_monitoring=yes], [nut_have_freeipmi_monitoring=no]) AC_SEARCH_LIBS([ipmi_monitoring_sensor_read_record_id], [ipmimonitoring], [], [nut_have_freeipmi_monitoring=no]) + dnl Check for FreeIPMI 1.1.X / 1.2.X which implies API changes! + AC_SEARCH_LIBS([ipmi_sdr_cache_ctx_destroy], [freeipmi], [nut_have_freeipmi_11x_12x=no], []) + AC_SEARCH_LIBS([ipmi_sdr_ctx_destroy], [freeipmi], [nut_have_freeipmi_11x_12x=yes], [nut_have_freeipmi_11x_12x=no]) + if test "${nut_have_freeipmi}" = "yes"; then nut_with_ipmi="yes" nut_ipmi_lib="(FreeIPMI)" @@ -80,6 +94,10 @@ if test -z "${nut_have_libfreeipmi_seen}"; then LIBIPMI_LIBS="${LIBS}" fi + if test "${nut_have_freeipmi_11x_12x}" = "yes"; then + AC_DEFINE(HAVE_FREEIPMI_11X_12X, 1, [Define if FreeIPMI 1.1.X / 1.2.X support is available]) + fi + if test "${nut_have_freeipmi_monitoring}" = "yes"; then AC_DEFINE(HAVE_FREEIPMI_MONITORING, 1, [Define if FreeIPMI monitoring support is available]) fi diff --git a/m4/nut_check_libgd.m4 b/m4/nut_check_libgd.m4 index 01cc882..b6f58ef 100644 --- a/m4/nut_check_libgd.m4 +++ b/m4/nut_check_libgd.m4 @@ -1,44 +1,87 @@ dnl Check for LIBGD compiler flags. On success, set nut_have_libgd="yes" dnl and set LIBGD_CFLAGS and LIBGD_LDFLAGS. On failure, set dnl nut_have_libgd="no". This macro can be run multiple times, but will -dnl do the checking only once. +dnl do the checking only once. -AC_DEFUN([NUT_CHECK_LIBGD], +AC_DEFUN([NUT_CHECK_LIBGD], [ if test -z "${nut_have_libgd_seen}"; then nut_have_libgd_seen=yes + NUT_CHECK_PKGCONFIG CFLAGS_ORIG="${CFLAGS}" LDFLAGS_ORIG="${LDFLAGS}" LIBS_ORIG="${LIBS}" - - dnl Initial defaults. These are only used if gdlib-config is - dnl unusable and the user fails to pass better values in --with - dnl arguments CFLAGS="" - LDFLAGS="-L/usr/X11R6/lib" - LIBS="-lgd -lpng -lz -ljpeg -lfreetype -lm -lXpm -lX11" + LDFLAGS="" + LIBS="" - AC_MSG_CHECKING(for gd version via gdlib-config) - GD_VERSION=`gdlib-config --version 2>/dev/null` - if test "$?" != "0" -o -z "${GD_VERSION}"; then - GD_VERSION="none" - fi - AC_MSG_RESULT(${GD_VERSION} found) + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [AC_MSG_CHECKING(for gd version via pkg-config) + GD_VERSION="`$PKG_CONFIG --silence-errors --modversion gdlib 2>/dev/null`" + if test "$?" != "0" -o -z "${GD_VERSION}"; then + GD_VERSION="none" + fi + AC_MSG_RESULT(${GD_VERSION} found) + ], + [GD_VERSION="none" + AC_MSG_NOTICE([can not check libgd settings via pkg-config]) + ] + ) - case "${GD_VERSION}" in - none) - ;; - 2.0.5 | 2.0.6 | 2.0.7) - AC_MSG_WARN([[gd ${GD_VERSION} detected, unable to use gdlib-config script]]) - AC_MSG_WARN([[If gd detection fails, upgrade gd or use --with-gd-includes and --with-gd-libs]]) - ;; - *) - CFLAGS="`gdlib-config --includes 2>/dev/null`" - LDFLAGS="`gdlib-config --ldflags 2>/dev/null`" - LIBS="`gdlib-config --libs 2>/dev/null`" - ;; - esac + AS_IF([test x"$GD_VERSION" != xnone], + [CFLAGS="`$PKG_CONFIG --silence-errors --cflags gdlib 2>/dev/null`" + LIBS="`$PKG_CONFIG --silence-errors --libs gdlib 2>/dev/null`" + ], + [dnl Initial defaults. These are only used if gdlib-config is + dnl unusable and the user fails to pass better values in --with + dnl arguments + CFLAGS="" + LDFLAGS="-L/usr/X11R6/lib" + LIBS="-lgd -lpng -lz -ljpeg -lfreetype -lm -lXpm -lX11" + + dnl By default seek in PATH + AC_PATH_PROGS([GDLIB_CONFIG], [gdlib-config], [none]) + AC_ARG_WITH(gdlib-config, + AS_HELP_STRING([@<:@--with-gdlib-config=/path/to/gdlib-config@:>@], + [path to program that reports GDLIB configuration]), + [ + case "${withval}" in + "") ;; + yes|no) + AC_MSG_ERROR(invalid option --with(out)-gdlib-config - see docs/configure.txt) + ;; + *) + GDLIB_CONFIG="${withval}" + ;; + esac + ]) + + AS_IF([test x"$GDLIB_CONFIG" != xnone], + [AC_MSG_CHECKING(for gd version via ${GDLIB_CONFIG}) + GD_VERSION="`${GDLIB_CONFIG} --version 2>/dev/null`" + if test "$?" != "0" -o -z "${GD_VERSION}"; then + GD_VERSION="none" + fi + AC_MSG_RESULT(${GD_VERSION} found) + ], [GD_VERSION="none"] + ) + + case "${GD_VERSION}" in + none) + ;; + 2.0.5 | 2.0.6 | 2.0.7) + AC_MSG_WARN([[gd ${GD_VERSION} detected, unable to use ${GDLIB_CONFIG} script]]) + AC_MSG_WARN([[If gd detection fails, upgrade gd or use --with-gd-includes and --with-gd-libs]]) + ;; + *) + CFLAGS="`${GDLIB_CONFIG} --includes 2>/dev/null`" + LDFLAGS="`${GDLIB_CONFIG} --ldflags 2>/dev/null`" + LIBS="`${GDLIB_CONFIG} --libs 2>/dev/null`" + ;; + esac + ] + ) dnl Now allow overriding gd settings if the user knows best AC_MSG_CHECKING(for gd include flags) @@ -74,7 +117,21 @@ if test -z "${nut_have_libgd_seen}"; then dnl check if gd is usable AC_CHECK_HEADERS(gd.h gdfontmb.h, [nut_have_libgd=yes], [nut_have_libgd=no], [AC_INCLUDES_DEFAULT]) - AC_SEARCH_LIBS(gdImagePng, gd, [], [nut_have_libgd=no]) + AC_SEARCH_LIBS(gdImagePng, gd, [], [ + dnl If using pkg-config, query additionally for Libs.private + dnl to pull -L/usr/X11R6/lib or whatever current OS wants + AC_MSG_CHECKING([for more gd library flags]) + AS_IF([test -n "${with_gd_libs}" || test x"$have_PKG_CONFIG" != xyes], [nut_have_libgd=no], [ + _LIBS_PRIVATE="`$PKG_CONFIG --silence-errors --libs gdlib --static 2>/dev/null`" + AS_IF([test -z "${_LIBS_PRIVATE}"], [nut_have_libgd=no], [ + AC_MSG_CHECKING([with gdlib.pc Libs.private]) + LDFLAGS="$LDFLAGS $_LIBS_PRIVATE" + unset ac_cv_search_gdImagePng + AC_SEARCH_LIBS(gdImagePng, gd, [], [nut_have_libgd=no]) + ]) + unset _LIBS_PRIVATE + ]) + ]) if test "${nut_have_libgd}" = "yes"; then AC_DEFINE(HAVE_LIBGD, 1, [Define if you have Boutell's libgd installed]) diff --git a/m4/nut_check_libhal.m4 b/m4/nut_check_libhal.m4 deleted file mode 100644 index ec54068..0000000 --- a/m4/nut_check_libhal.m4 +++ /dev/null @@ -1,81 +0,0 @@ -dnl Check for LIBHAL compiler flags. On success, set nut_have_libhal="yes" -dnl and set LIBHAL_CFLAGS and LIBHAL_LIBS. On failure, set -dnl nut_have_libhal="no". This macro can be run multiple times, but will -dnl do the checking only once. -dnl NUT requires HAL version 0.5.8 at least - -AC_DEFUN([NUT_CHECK_LIBHAL], -[ -if test -z "${nut_have_libhal_seen}"; then - nut_have_libhal_seen=yes - - CFLAGS_ORIG="${CFLAGS}" - LIBS_ORIG="${LIBS}" - - AC_MSG_CHECKING(for libhal version via pkg-config (0.5.8 minimum required)) - HAL_VERSION="`pkg-config --silence-errors --modversion hal 2>/dev/null`" - if test "$?" != "0" -o -z "${HAL_VERSION}"; then - AC_MSG_RESULT(none found) - elif pkg-config --silence-errors --atleast-version=0.5.8 hal 2>/dev/null; then - AC_MSG_RESULT(${HAL_VERSION} found) - else - AC_MSG_WARN(${HAL_VERSION} is too old) - fi - - AC_MSG_CHECKING(for libhal cflags) - AC_ARG_WITH(hal-includes, - AS_HELP_STRING([@<:@--with-hal-includes=CFLAGS@:>@], [include flags for the HAL library]), - [ - case "${withval}" in - yes|no) - AC_MSG_ERROR(invalid option --with(out)-hal-includes - see docs/configure.txt) - ;; - *) - CFLAGS="${withval}" - ;; - esac - ], [ - dnl also get cflags from glib-2.0 to workaround a bug in dbus-glib - CFLAGS="`pkg-config --silence-errors --cflags hal dbus-glib-1 2>/dev/null`" - if test "$?" != "0"; then - CFLAGS="-DDBUS_API_SUBJECT_TO_CHANGE -I/usr/include/hal -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include" - fi - ]) - AC_MSG_RESULT([${CFLAGS}]) - - AC_MSG_CHECKING(for libhal ldflags) - AC_ARG_WITH(hal-libs, - AS_HELP_STRING([@<:@--with-hal-libs=LIBS@:>@], [linker flags for the HAL library]), - [ - case "${withval}" in - yes|no) - AC_MSG_ERROR(invalid option --with(out)-hal-libs - see docs/configure.txt) - ;; - *) - LIBS="${withval}" - ;; - esac - ], [ - dnl also get libs from glib-2.0 to workaround a bug in dbus-glib - LIBS="`pkg-config --silence-errors --libs hal dbus-glib-1 2>/dev/null`" - if test "$?" != "0"; then - LIBS="-lhal -ldbus-1 -lpthread" - fi - ]) - AC_MSG_RESULT([${LIBS}]) - - dnl check if HAL is usable - AC_CHECK_HEADERS(libhal.h, [nut_have_libhal=yes], [nut_have_libhal=no], [AC_INCLUDES_DEFAULT]) - AC_CHECK_HEADERS(glib.h dbus/dbus-glib.h, [], [nut_have_libhal=no], [AC_INCLUDES_DEFAULT]) - AC_CHECK_FUNCS(libhal_device_new_changeset, [], [nut_have_libhal=no]) - - if test "${nut_have_libhal}" = "yes"; then - AC_CHECK_FUNCS(g_timeout_add_seconds) - LIBHAL_CFLAGS="${CFLAGS}" - LIBHAL_LIBS="${LIBS}" - fi - - CFLAGS="${CFLAGS_ORIG}" - LIBS="${LIBS_ORIG}" -fi -]) diff --git a/m4/nut_check_libltdl.m4 b/m4/nut_check_libltdl.m4 index e1058bb..4c5f6e7 100644 --- a/m4/nut_check_libltdl.m4 +++ b/m4/nut_check_libltdl.m4 @@ -12,6 +12,36 @@ if test -z "${nut_have_libltdl_seen}"; then LIBS_ORIG="${LIBS}" LIBS="" + AC_MSG_CHECKING(for libltdl cflags) + AC_ARG_WITH(libltdl-includes, + AS_HELP_STRING([@<:@--with-libltdl-includes=CFLAGS@:>@], [include flags for the libltdl library]), + [ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-libltdl-includes - see docs/configure.txt) + ;; + *) + CFLAGS="${withval}" + ;; + esac + ], []) + AC_MSG_RESULT([${CFLAGS}]) + + AC_MSG_CHECKING(for libltdl ldflags) + AC_ARG_WITH(libltdl-libs, + AS_HELP_STRING([@<:@--with-libltdl-libs=LIBS@:>@], [linker flags for the libltdl library]), + [ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-libltdl-libs - see docs/configure.txt) + ;; + *) + LIBS="${withval}" + ;; + esac + ], []) + AC_MSG_RESULT([${LIBS}]) + AC_CHECK_HEADERS(ltdl.h, [nut_have_libltdl=yes], [nut_have_libltdl=no], [AC_INCLUDES_DEFAULT]) AC_SEARCH_LIBS(lt_dlinit, ltdl, [], [nut_have_libltdl=no]) diff --git a/m4/nut_check_libmodbus.m4 b/m4/nut_check_libmodbus.m4 new file mode 100644 index 0000000..ea08dd3 --- /dev/null +++ b/m4/nut_check_libmodbus.m4 @@ -0,0 +1,187 @@ +dnl Check for LIBMODBUS compiler flags. On success, set nut_have_libmodbus="yes" +dnl and set LIBMODBUS_CFLAGS and LIBMODBUS_LIBS. On failure, set +dnl nut_have_libmodbus="no". This macro can be run multiple times, but will +dnl do the checking only once. + +AC_DEFUN([NUT_CHECK_LIBMODBUS], +[ +if test -z "${nut_have_libmodbus_seen}"; then + nut_have_libmodbus_seen=yes + + dnl save CFLAGS and LIBS + CFLAGS_ORIG="${CFLAGS}" + LIBS_ORIG="${LIBS}" + NUT_CHECK_PKGCONFIG + + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [AC_MSG_CHECKING(for libmodbus version via pkg-config) + LIBMODBUS_VERSION="`$PKG_CONFIG --silence-errors --modversion libmodbus 2>/dev/null`" + if test "$?" != "0" -o -z "${LIBMODBUS_VERSION}"; then + LIBMODBUS_VERSION="none" + fi + AC_MSG_RESULT(${LIBMODBUS_VERSION} found) + ], + [LIBMODBUS_VERSION="none" + AC_MSG_NOTICE([can not check libmodbus settings via pkg-config]) + ] + ) + + AS_IF([test x"$LIBMODBUS_VERSION" != xnone], + [CFLAGS="`$PKG_CONFIG --silence-errors --cflags libmodbus 2>/dev/null`" + LIBS="`$PKG_CONFIG --silence-errors --libs libmodbus 2>/dev/null`" + ], + [CFLAGS="-I/usr/include/modbus" + LIBS="-lmodbus" + ] + ) + + AC_MSG_CHECKING(for libmodbus cflags) + AC_ARG_WITH(modbus-includes, + AS_HELP_STRING([@<:@--with-modbus-includes=CFLAGS@:>@], [include flags for the libmodbus library]), + [ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-modbus-includes - see docs/configure.txt) + ;; + *) + CFLAGS="${withval}" + ;; + esac + ], []) + AC_MSG_RESULT([${CFLAGS}]) + + AC_MSG_CHECKING(for libmodbus ldflags) + AC_ARG_WITH(modbus-libs, + AS_HELP_STRING([@<:@--with-modbus-libs=LIBS@:>@], [linker flags for the libmodbus library]), + [ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-modbus-libs - see docs/configure.txt) + ;; + *) + LIBS="${withval}" + ;; + esac + ], []) + AC_MSG_RESULT([${LIBS}]) + + dnl check if libmodbus is usable + AC_CHECK_HEADERS(modbus.h, [nut_have_libmodbus=yes], [nut_have_libmodbus=no], [AC_INCLUDES_DEFAULT]) + AC_CHECK_FUNCS(modbus_new_rtu, [], [nut_have_libmodbus=no]) + AC_CHECK_FUNCS(modbus_new_tcp, [], [nut_have_libmodbus=no]) + AC_CHECK_FUNCS(modbus_set_byte_timeout, [], [nut_have_libmodbus=no]) + AC_CHECK_FUNCS(modbus_set_response_timeout, [], [nut_have_libmodbus=no]) + + dnl modbus_set_byte_timeout() and modbus_set_response_timeout() + dnl in 3.0.x and 3.1.x have different args (since ~2013): the + dnl older version used to accept timeout as a struct timeval + dnl instead of seconds and microseconds. Detect which we use?.. + AS_IF([test x"$nut_have_libmodbus" = xyes], + [dnl Do not rely on versions if we can test actual API + AX_C_PRAGMAS + AC_LANG_PUSH([C]) + AC_CACHE_CHECK([types of arguments for modbus_set_byte_timeout], + [nut_cv_func_modbus_set_byte_timeout_args], + [nut_cv_func_modbus_set_byte_timeout_args="unknown" + AC_COMPILE_IFELSE( + [dnl Try purely the old API (timeval) + AC_LANG_PROGRAM([ +#include +#include +], [modbus_t *ctx; struct timeval to = (struct timeval){0}; +modbus_set_byte_timeout(ctx, &to);]) + ], [nut_cv_func_modbus_set_byte_timeout_args="timeval" + dnl Try the old API in more detail: check + dnl if we can just assign uint32's for new + dnl code into timeval fields (exist+numeric)? + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([ +#include +#include +#include +], [modbus_t *ctx; uint32_t to_sec = 10, to_usec = 50; +struct timeval to = (struct timeval){0}; +/* TODO: Clarify and detect warning names and + * so pragmas for signed/unsigned assignment (e.g. + * for timeval definitions that have "long" fields) + */ +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_COMPARE +# pragma GCC diagnostic ignored "-Wsign-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION +# pragma GCC diagnostic ignored "-Wsign-conversion" +#endif +to.tv_sec = to_sec; +to.tv_usec = to_usec; +modbus_set_byte_timeout(ctx, &to); +]) + ], [nut_cv_func_modbus_set_byte_timeout_args="timeval_numeric_fields"]) + ], + + [dnl Try another API variant: new API with + dnl fields of struct timeval as numbers + dnl (checks they exist, and are compatible + dnl numeric types so compiler can convert) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([ +#include +#include +#include +], [modbus_t *ctx; struct timeval to = (struct timeval){0}; +/* TODO: Clarify and detect warning names and + * so pragmas for signed/unsigned assignment (e.g. + * for timeval definitions that have "long" fields) + */ +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_COMPARE +# pragma GCC diagnostic ignored "-Wsign-compare" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_SIGN_CONVERSION +# pragma GCC diagnostic ignored "-Wsign-conversion" +#endif +uint32_t to_sec = to.tv_sec, to_usec = to.tv_usec; +modbus_set_byte_timeout(ctx, to_sec, to_usec); +]) + ], [nut_cv_func_modbus_set_byte_timeout_args="sec_usec_uint32_cast_timeval_fields"], + [dnl Try another API variant: new API purely (two uint32's) + AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([ +#include +#include +], [modbus_t *ctx; uint32_t to_sec = 0, to_usec = 0; +modbus_set_byte_timeout(ctx, to_sec, to_usec);]) + ], [nut_cv_func_modbus_set_byte_timeout_args="sec_usec_uint32"]) + ]) + ]) + ]) + + dnl NOTE: We could add similar tests to above for + dnl other time-related methods, but for now keep + dnl it simple -- and assume some same approach + dnl applies to the same generation of the library. + AC_LANG_POP([C]) + AC_MSG_RESULT([Found types to use for modbus_set_byte_timeout: ${nut_cv_func_modbus_set_byte_timeout_args}]) + dnl NOTE: code should check for having a token name defined e.g.: + dnl #ifdef NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32 + dnl Alas, we can't pass variables as macro name to AC_DEFINE + COMMENT="Define to specify timeout method args approach for libmodbus" + AS_CASE(["${nut_cv_func_modbus_set_byte_timeout_args}"], + [timeval_numeric_fields], [AC_DEFINE([NUT_MODBUS_TIMEOUT_ARG_timeval_numeric_fields], 1, [${COMMENT}])], + [timeval], [AC_DEFINE([NUT_MODBUS_TIMEOUT_ARG_timeval], 1, [${COMMENT}])], + [sec_usec_uint32_cast_timeval_fields], [AC_DEFINE([NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32_cast_timeval_fields], 1, [${COMMENT}])], + [sec_usec_uint32], [AC_DEFINE([NUT_MODBUS_TIMEOUT_ARG_sec_usec_uint32], 1, [${COMMENT}])], + [dnl default + AC_MSG_WARN([Cannot find proper types to use for modbus_set_byte_timeout]) + nut_have_libmodbus=no] + ) + ]) + + AS_IF([test x"${nut_have_libmodbus}" = x"yes"], + [LIBMODBUS_CFLAGS="${CFLAGS}" + LIBMODBUS_LIBS="${LIBS}"] + ) + + dnl restore original CFLAGS and LIBS + CFLAGS="${CFLAGS_ORIG}" + LIBS="${LIBS_ORIG}" +fi +]) diff --git a/m4/nut_check_libneon.m4 b/m4/nut_check_libneon.m4 index 4fd290b..944f7a3 100644 --- a/m4/nut_check_libneon.m4 +++ b/m4/nut_check_libneon.m4 @@ -7,18 +7,27 @@ AC_DEFUN([NUT_CHECK_LIBNEON], [ if test -z "${nut_have_neon_seen}"; then nut_have_neon_seen=yes + NUT_CHECK_PKGCONFIG dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" - dnl See which version of the neon library (if any) is installed - AC_MSG_CHECKING(for libneon version via pkg-config (0.25.0 minimum required)) - NEON_VERSION="`pkg-config --silence-errors --modversion neon 2>/dev/null`" - if test "$?" != "0" -o -z "${NEON_VERSION}"; then - NEON_VERSION="none" - fi - AC_MSG_RESULT(${NEON_VERSION} found) + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [dnl See which version of the neon library (if any) is installed + dnl FIXME : Support detection of cflags/ldflags below by legacy + dnl discovery if pkgconfig is not there + AC_MSG_CHECKING(for libneon version via pkg-config (0.25.0 minimum required)) + NEON_VERSION="`$PKG_CONFIG --silence-errors --modversion neon 2>/dev/null`" + if test "$?" != "0" -o -z "${NEON_VERSION}"; then + NEON_VERSION="none" + fi + AC_MSG_RESULT(${NEON_VERSION} found) + ], + [NEON_VERSION="none" + AC_MSG_NOTICE([can not check libneon settings via pkg-config]) + ] + ) AC_MSG_CHECKING(for libneon cflags) AC_ARG_WITH(neon-includes, @@ -32,7 +41,12 @@ if test -z "${nut_have_neon_seen}"; then CFLAGS="${withval}" ;; esac - ], [CFLAGS="`pkg-config --silence-errors --cflags neon 2>/dev/null`"]) + ], [ + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [CFLAGS="`$PKG_CONFIG --silence-errors --cflags neon 2>/dev/null`" || CFLAGS="-I/usr/include/neon -I/usr/local/include/neon"], + [CFLAGS="-I/usr/include/neon -I/usr/local/include/neon"] + )] + ) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for libneon ldflags) @@ -47,7 +61,12 @@ if test -z "${nut_have_neon_seen}"; then LIBS="${withval}" ;; esac - ], [LIBS="`pkg-config --silence-errors --libs neon 2>/dev/null`"]) + ], [ + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [LIBS="`$PKG_CONFIG --silence-errors --libs neon 2>/dev/null`" || LIBS="-lneon"], + [LIBS="-lneon"] + )] + ) AC_MSG_RESULT([${LIBS}]) dnl check if neon is usable diff --git a/m4/nut_check_libnetsnmp.m4 b/m4/nut_check_libnetsnmp.m4 index e1c1426..1f89b5a 100644 --- a/m4/nut_check_libnetsnmp.m4 +++ b/m4/nut_check_libnetsnmp.m4 @@ -1,25 +1,83 @@ dnl Check for LIBNETSNMP compiler flags. On success, set dnl nut_have_libnetsnmp="yes" and set LIBNETSNMP_CFLAGS and dnl LIBNETSNMP_LIBS. On failure, set nut_have_libnetsnmp="no". -dnl This macro can be run multiple times, but will do the checking only -dnl once. +dnl This macro can be run multiple times, but will do the +dnl checking only once. AC_DEFUN([NUT_CHECK_LIBNETSNMP], [ if test -z "${nut_have_libnetsnmp_seen}"; then nut_have_libnetsnmp_seen=yes + NUT_CHECK_PKGCONFIG + AC_LANG_PUSH([C]) dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" - dnl See which version of the Net-SNMP library (if any) is installed - AC_MSG_CHECKING(for Net-SNMP version via net-snmp-config) - SNMP_VERSION=`net-snmp-config --version 2>/dev/null` - if test "$?" != "0" -o -z "${SNMP_VERSION}"; then - SNMP_VERSION="none" + dnl We prefer to get info from pkg-config (for suitable arch/bitness as + dnl specified in args for that mechanism), unless (legacy) a particular + dnl --with-net-snmp-config=... was requested. If there is no pkg-config + dnl info, we fall back to detecting and running a NET_SNMP_CONFIG as well. + + dnl By default seek in PATH, but which variant (if several are provided)? + AC_CHECK_SIZEOF([void *]) + NET_SNMP_CONFIG="none" + AS_CASE(["${ac_cv_sizeof_void_p}"], + [4],[AC_PATH_PROGS([NET_SNMP_CONFIG], [net-snmp-config-32 net-snmp-config], [none])], + [8],[AC_PATH_PROGS([NET_SNMP_CONFIG], [net-snmp-config-64 net-snmp-config], [none])], + [AC_PATH_PROGS([NET_SNMP_CONFIG], [net-snmp-config], [none])] + ) + + prefer_NET_SNMP_CONFIG=false + AC_ARG_WITH(net-snmp-config, + AS_HELP_STRING([@<:@--with-net-snmp-config=/path/to/net-snmp-config@:>@], + [path to program that reports Net-SNMP configuration]), + [ + case "${withval}" in + ""|yes) prefer_NET_SNMP_CONFIG=true ;; + no) + dnl AC_MSG_ERROR(invalid option --with(out)-net-snmp-config - see docs/configure.txt) + prefer_NET_SNMP_CONFIG=false + ;; + *) + NET_SNMP_CONFIG="${withval}" + prefer_NET_SNMP_CONFIG=true + ;; + esac + ]) + + if test x"$have_PKG_CONFIG" = xyes && ! "${prefer_NET_SNMP_CONFIG}" ; then + AC_MSG_CHECKING(for Net-SNMP version via pkg-config) + dnl TODO? Loop over possible/historic pkg names, like + dnl netsnmp, net-snmp, ucd-snmp, libsnmp, snmp... + SNMP_VERSION="`$PKG_CONFIG --silence-errors --modversion netsnmp 2>/dev/null`" + if test "$?" = "0" -a -n "${SNMP_VERSION}" ; then + AC_MSG_RESULT(${SNMP_VERSION} found) + else + AC_MSG_RESULT(none found) + prefer_NET_SNMP_CONFIG=true + fi + fi + + if test "$NET_SNMP_CONFIG" = none ; then + prefer_NET_SNMP_CONFIG=false + fi + + if "${prefer_NET_SNMP_CONFIG}" ; then + dnl See which version of the Net-SNMP library (if any) is installed + AC_MSG_CHECKING(for Net-SNMP version via ${NET_SNMP_CONFIG}) + SNMP_VERSION="`${NET_SNMP_CONFIG} --version 2>/dev/null`" + if test "$?" != "0" -o -z "${SNMP_VERSION}"; then + SNMP_VERSION="none" + prefer_NET_SNMP_CONFIG=false + fi + AC_MSG_RESULT(${SNMP_VERSION} found) + fi + + if test x"$have_PKG_CONFIG" != xyes && ! "${prefer_NET_SNMP_CONFIG}" ; then + AC_MSG_WARN([did not find either net-snmp-config or pkg-config for net-snmp]) fi - AC_MSG_RESULT(${SNMP_VERSION} found) AC_MSG_CHECKING(for Net-SNMP cflags) AC_ARG_WITH(snmp-includes, @@ -33,7 +91,13 @@ if test -z "${nut_have_libnetsnmp_seen}"; then CFLAGS="${withval}" ;; esac - ], [CFLAGS="`net-snmp-config --base-cflags 2>/dev/null`"]) + ], [AS_IF(["${prefer_NET_SNMP_CONFIG}"], + [CFLAGS="`${NET_SNMP_CONFIG} --base-cflags 2>/dev/null`"], + [AS_IF([test x"$have_PKG_CONFIG" = xyes], + [CFLAGS="`$PKG_CONFIG --silence-errors --cflags netsnmp 2>/dev/null`"] + )] + )] + ) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for Net-SNMP libs) @@ -48,17 +112,211 @@ if test -z "${nut_have_libnetsnmp_seen}"; then LIBS="${withval}" ;; esac - ], [LIBS="`net-snmp-config --libs 2>/dev/null`"]) + ], [AS_IF(["${prefer_NET_SNMP_CONFIG}"], + [LIBS="`${NET_SNMP_CONFIG} --libs 2>/dev/null`"], + [AS_IF([test x"$have_PKG_CONFIG" = xyes], + [LIBS="`$PKG_CONFIG --silence-errors --libs netsnmp 2>/dev/null`"], + [LIBS="-lnetsnmp"])] + )] + ) AC_MSG_RESULT([${LIBS}]) dnl Check if the Net-SNMP library is usable AC_CHECK_HEADERS(net-snmp/net-snmp-config.h, [nut_have_libnetsnmp=yes], [nut_have_libnetsnmp=no], [AC_INCLUDES_DEFAULT]) AC_CHECK_FUNCS(init_snmp, [], [nut_have_libnetsnmp=no]) - if test "${nut_have_libnetsnmp}" = "yes"; then + AS_IF([test "${nut_have_libnetsnmp}" = "yes"], [ LIBNETSNMP_CFLAGS="${CFLAGS}" LIBNETSNMP_LIBS="${LIBS}" - fi + + AC_MSG_CHECKING([for defined usmAESPrivProtocol]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +#include +oid * pProto = usmAESPrivProtocol; +], +[] + )], + [AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol, 1, [Variable or macro by this name is resolvable]) + ], + [AC_MSG_RESULT([no]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol, 0, [Variable or macro by this name is not resolvable]) + ]) + + AC_MSG_CHECKING([for defined usmAES128PrivProtocol]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +#include +oid * pProto = usmAES128PrivProtocol; +], +[] + )], + [AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol, 1, [Variable or macro by this name is resolvable]) + ], + [AC_MSG_RESULT([no]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol, 0, [Variable or macro by this name is not resolvable]) + ]) + + AC_MSG_CHECKING([for defined usmDESPrivProtocol]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +#include +oid * pProto = usmDESPrivProtocol; +#ifdef NETSNMP_DISABLE_DES +#error "NETSNMP_DISABLE_DES is defined" +#endif +], +[] + )], + [AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol, 1, [Variable or macro by this name is resolvable]) + ], + [AC_MSG_RESULT([no]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol, 0, [Variable or macro by this name is not resolvable]) + ]) + + AC_MSG_CHECKING([for defined usmHMAC256SHA384AuthProtocol]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +#include +oid * pProto = usmHMAC256SHA384AuthProtocol; +#ifndef HAVE_EVP_SHA384 +#error "HAVE_EVP_SHA384 is NOT defined" +#endif +], +[] + )], + [AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol, 1, [Variable or macro by this name is resolvable]) + ], + [AC_MSG_RESULT([no]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol, 0, [Variable or macro by this name is not resolvable]) + ]) + + AC_MSG_CHECKING([for defined usmHMAC384SHA512AuthProtocol]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +#include +oid * pProto = usmHMAC384SHA512AuthProtocol; +#ifndef HAVE_EVP_SHA384 +#error "HAVE_EVP_SHA384 is NOT defined" +#endif +], +[] + )], + [AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol, 1, [Variable or macro by this name is resolvable]) + ], + [AC_MSG_RESULT([no]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol, 0, [Variable or macro by this name is not resolvable]) + ]) + + AC_MSG_CHECKING([for defined usmHMAC192SHA256AuthProtocol]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +#include +oid * pProto = usmHMAC192SHA256AuthProtocol; +#ifndef HAVE_EVP_SHA224 +#error "HAVE_EVP_SHA224 is NOT defined" +#endif +], +[] + )], + [AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol, 1, [Variable or macro by this name is resolvable]) + ], + [AC_MSG_RESULT([no]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol, 0, [Variable or macro by this name is not resolvable]) + ]) + + AC_MSG_CHECKING([for defined usmAES192PrivProtocol]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +#include +oid * pProto = usmAES192PrivProtocol; +#ifndef NETSNMP_DRAFT_BLUMENTHAL_AES_04 +#error "NETSNMP_DRAFT_BLUMENTHAL_AES_04 is NOT defined" +#endif +], +[] + )], + [AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol, 1, [Variable or macro by this name is resolvable]) + ], + [AC_MSG_RESULT([no]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol, 0, [Variable or macro by this name is not resolvable]) + ]) + + AC_MSG_CHECKING([for defined usmAES256PrivProtocol]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +#include +oid * pProto = usmAES256PrivProtocol; +#ifndef NETSNMP_DRAFT_BLUMENTHAL_AES_04 +#error "NETSNMP_DRAFT_BLUMENTHAL_AES_04 is NOT defined" +#endif +], +[] + )], + [AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol, 1, [Variable or macro by this name is resolvable]) + ], + [AC_MSG_RESULT([no]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol, 0, [Variable or macro by this name is not resolvable]) + ]) + + AC_MSG_CHECKING([for defined usmHMACMD5AuthProtocol]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +#include +oid * pProto = usmHMACMD5AuthProtocol; +#ifdef NETSNMP_DISABLE_MD5 +#error "NETSNMP_DISABLE_MD5 is defined" +#endif +], +[] + )], + [AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol, 1, [Variable or macro by this name is resolvable]) + ], + [AC_MSG_RESULT([no]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol, 0, [Variable or macro by this name is not resolvable]) + ]) + + AC_MSG_CHECKING([for defined usmHMACSHA1AuthProtocol]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +#include +oid * pProto = usmHMACSHA1AuthProtocol; +], +[] + )], + [AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol, 1, [Variable or macro by this name is resolvable]) + ], + [AC_MSG_RESULT([no]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol, 0, [Variable or macro by this name is not resolvable]) + ]) + + AC_MSG_CHECKING([for defined NETSNMP_DRAFT_BLUMENTHAL_AES_04]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([ +#include +#include +int num = NETSNMP_DRAFT_BLUMENTHAL_AES_04 + 1; /* if defined, NETSNMP_DRAFT_BLUMENTHAL_AES_04 is 1 */ +], +[] + )], + [AC_MSG_RESULT([yes]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04, 1, [Variable or macro by this name is resolvable]) + ], + [AC_MSG_RESULT([no]) + AC_DEFINE_UNQUOTED(NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04, 0, [Variable or macro by this name is not resolvable]) + ]) + + ]) + AC_LANG_POP([C]) dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}" diff --git a/m4/nut_check_libnss.m4 b/m4/nut_check_libnss.m4 new file mode 100644 index 0000000..54833d7 --- /dev/null +++ b/m4/nut_check_libnss.m4 @@ -0,0 +1,108 @@ +dnl Check for Mozilla NSS (LIBNSS) compiler flags. On success, set +dnl nut_have_libnss="yes" and nut_ssl_lib="Mozilla NSS", and define WITH_SSL, +dnl WITH_NSS, LIBSSL_CFLAGS and LIBSSL_LIBS. On failure, set nut_have_libnss="no". +dnl This macro can be run multiple times, but will do the checking only once. + +AC_DEFUN([NUT_CHECK_LIBNSS], +[ +if test -z "${nut_have_libnss_seen}"; then + nut_have_libnss_seen=yes + NUT_CHECK_PKGCONFIG + + dnl save CFLAGS and LIBS + CFLAGS_ORIG="${CFLAGS}" + LIBS_ORIG="${LIBS}" + REQUIRES_ORIG="${REQUIRES}" + + SAVED_GCC="$GCC" + SAVED_CC="$CC" + if ( test "${GCC}" = "yes" ) + then + case "$CFLAGS$LDFLAGS" in + *-m32*) CC="$CC -m32" ;; + *-m64*) CC="$CC -m64" ;; + esac + fi + + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [AC_MSG_CHECKING(for Mozilla NSS version via pkg-config) + NSS_VERSION="`$PKG_CONFIG --silence-errors --modversion nss 2>/dev/null`" + if test "$?" != "0" -o -z "${NSS_VERSION}"; then + NSS_VERSION="none" + fi + AC_MSG_RESULT(${NSS_VERSION} found) + ], + [NSS_VERSION="none" + AC_MSG_NOTICE([can not check libnss settings via pkg-config]) + ] + ) + + AS_IF([test x"$NSS_VERSION" != xnone], + [CFLAGS="`$PKG_CONFIG --silence-errors --cflags nss 2>/dev/null`" + LIBS="`$PKG_CONFIG --silence-errors --libs nss 2>/dev/null`" + REQUIRES="nss" + ], + [CFLAGS="" + LIBS="-lnss3 -lnssutil3 -lsmime3 -lssl3 -lplds4 -lplc4 -lnspr4" + REQUIRES="nss" + ] + ) + + dnl allow overriding NSS settings if the user knows best + AC_MSG_CHECKING(for Mozilla NSS cflags) + AC_ARG_WITH(nss-includes, + AS_HELP_STRING([@<:@--with-nss-includes=CFLAGS@:>@], [include flags for the Mozilla NSS library]), + [ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-nss-includes - see docs/configure.txt) + ;; + *) + CFLAGS="${withval}" + ;; + esac + ], []) + AC_MSG_RESULT([${CFLAGS}]) + + AC_MSG_CHECKING(for Mozilla NSS ldflags) + AC_ARG_WITH(nss-libs, + AS_HELP_STRING([@<:@--with-nss-libs=LIBS@:>@], [linker flags for the Mozilla NSS library]), + [ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-nss-libs - see docs/configure.txt) + ;; + *) + LIBS="${withval}" + ;; + esac + ], []) + AC_MSG_RESULT([${LIBS}]) + + dnl check if NSS is usable: we need both the runtime and headers + dnl NOTE that caller may have to specify PKG_CONFIG_PATH including + dnl their bitness variant if it is not prioritized in their default + dnl setting built in by OS distribution; the .../pkgconfig/nss.pc + dnl tends to specify the libdir which is CPU Arch dependent. + AC_CHECK_FUNCS(NSS_Init, [nut_have_libnss=yes], [nut_have_libnss=no]) + dnl libc6 also provides an nss.h file, so also check for ssl.h + AC_CHECK_HEADERS([nss.h ssl.h], [], [nut_have_libnss=no], [AC_INCLUDES_DEFAULT]) + + if test "${nut_have_libnss}" = "yes"; then + nut_with_ssl="yes" + nut_ssl_lib="(Mozilla NSS)" + AC_DEFINE(WITH_SSL, 1, [Define to enable SSL support]) + AC_DEFINE(WITH_NSS, 1, [Define to enable SSL support using Mozilla NSS]) + LIBSSL_CFLAGS="${CFLAGS}" + LIBSSL_LIBS="${LIBS}" + LIBSSL_REQUIRES="${REQUIRES}" + fi + + dnl restore original CFLAGS and LIBS + CFLAGS="${CFLAGS_ORIG}" + LIBS="${LIBS_ORIG}" + REQUIRES="${REQUIRES_ORIG}" + GCC="$SAVED_GCC" + CC="$SAVED_CC" +fi +]) diff --git a/m4/nut_check_libopenssl.m4 b/m4/nut_check_libopenssl.m4 new file mode 100644 index 0000000..d493615 --- /dev/null +++ b/m4/nut_check_libopenssl.m4 @@ -0,0 +1,92 @@ +dnl Check for OpenSSL (LIBOPENSSL) compiler flags. On success, set +dnl nut_have_openssl="yes" and nut_ssl_lib="OpenSSL", and define WITH_SSL, +dnl WITH_OPENSSL, LIBSSL_CFLAGS and LIBSSL_LIBS. On failure, set +dnl nut_have_libssl="no". +dnl This macro can be run multiple times, but will do the checking only once. + +AC_DEFUN([NUT_CHECK_LIBOPENSSL], +[ +if test -z "${nut_have_libopenssl_seen}"; then + nut_have_libopenssl_seen=yes + NUT_CHECK_PKGCONFIG + + dnl save CFLAGS and LIBS + CFLAGS_ORIG="${CFLAGS}" + LIBS_ORIG="${LIBS}" + REQUIRES_ORIG="${REQUIRES}" + + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [AC_MSG_CHECKING(for OpenSSL version via pkg-config) + OPENSSL_VERSION="`$PKG_CONFIG --silence-errors --modversion openssl 2>/dev/null`" + if test "$?" != "0" -o -z "${OPENSSL_VERSION}"; then + OPENSSL_VERSION="none" + fi + AC_MSG_RESULT(${OPENSSL_VERSION} found) + ], + [OPENSSL_VERSION="none" + AC_MSG_NOTICE([can not check OpenSSL settings via pkg-config]) + ] + ) + + AS_IF([test x"$OPENSSL_VERSION" != xnone], + [CFLAGS="`$PKG_CONFIG --silence-errors --cflags openssl 2>/dev/null`" + LIBS="`$PKG_CONFIG --silence-errors --libs openssl 2>/dev/null`" + REQUIRES="openssl" + ], + [CFLAGS="" + LIBS="-lssl -lcrypto" + REQUIRES="openssl" + ] + ) + + dnl allow overriding OpenSSL settings if the user knows best + AC_MSG_CHECKING(for OpenSSL cflags) + AC_ARG_WITH(openssl-includes, + AS_HELP_STRING([@<:@--with-openssl-includes=CFLAGS@:>@], [include flags for the OpenSSL library]), + [ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-openssl-includes - see docs/configure.txt) + ;; + *) + CFLAGS="${withval}" + ;; + esac + ], []) + AC_MSG_RESULT([${CFLAGS}]) + + AC_MSG_CHECKING(for OpenSSL ldflags) + AC_ARG_WITH(openssl-libs, + AS_HELP_STRING([@<:@--with-openssl-libs=LIBS@:>@], [linker flags for the OpenSSL library]), + [ + case "${withval}" in + yes|no) + AC_MSG_ERROR(invalid option --with(out)-openssl-libs - see docs/configure.txt) + ;; + *) + LIBS="${withval}" + ;; + esac + ], []) + AC_MSG_RESULT([${LIBS}]) + + dnl check if openssl is usable + AC_CHECK_HEADERS(openssl/ssl.h, [nut_have_openssl=yes], [nut_have_openssl=no], [AC_INCLUDES_DEFAULT]) + AC_CHECK_FUNCS(SSL_CTX_new, [], [nut_have_openssl=no]) + + if test "${nut_have_openssl}" = "yes"; then + nut_with_ssl="yes" + nut_ssl_lib="(OpenSSL)" + AC_DEFINE(WITH_SSL, 1, [Define to enable SSL support]) + AC_DEFINE(WITH_OPENSSL, 1, [Define to enable SSL support using OpenSSL]) + LIBSSL_CFLAGS="${CFLAGS}" + LIBSSL_LIBS="${LIBS}" + LIBSSL_REQUIRES="${REQUIRES}" + fi + + dnl restore original CFLAGS and LIBS + CFLAGS="${CFLAGS_ORIG}" + LIBS="${LIBS_ORIG}" + REQUIRES="${REQUIRES_ORIG}" +fi +]) diff --git a/m4/nut_check_libpowerman.m4 b/m4/nut_check_libpowerman.m4 index a29afed..b731cfe 100644 --- a/m4/nut_check_libpowerman.m4 +++ b/m4/nut_check_libpowerman.m4 @@ -7,24 +7,53 @@ AC_DEFUN([NUT_CHECK_LIBPOWERMAN], [ if test -z "${nut_have_libpowerman_seen}"; then nut_have_libpowerman_seen=yes + NUT_CHECK_PKGCONFIG dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" - AC_MSG_CHECKING(for libpowerman cflags) + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [AC_MSG_CHECKING([for LLNC libpowerman version via pkg-config]) + POWERMAN_VERSION="`$PKG_CONFIG --silence-errors --modversion libpowerman 2>/dev/null`" + dnl Unlike other pkg-config enabled projects we use, + dnl libpowerman (at least on Debian) delivers an empty + dnl "Version:" tag in /usr/lib/pkgconfig/libpowerman.pc + dnl (and it is the only file in that dir, others going + dnl to /usr/lib/x86_64-linux-gnu/pkgconfig/ or similar + dnl for other architectures). Empty is not an error here! + if test "$?" != "0" ; then # -o -z "${POWERMAN_VERSION}"; then + POWERMAN_VERSION="none" + fi + AC_MSG_RESULT(['${POWERMAN_VERSION}' found]) + ], + [POWERMAN_VERSION="none" + AC_MSG_NOTICE([can not check LLNC libpowerman settings via pkg-config]) + ] + ) + + AS_IF([test x"$POWERMAN_VERSION" != xnone], + [CFLAGS="`$PKG_CONFIG --silence-errors --cflags libpowerman 2>/dev/null`" + LIBS="`$PKG_CONFIG --silence-errors --libs libpowerman 2>/dev/null`" + ], + [CFLAGS="" + LIBS="" + ] + ) + + AC_MSG_CHECKING([for libpowerman cflags]) AC_ARG_WITH(powerman-includes, AS_HELP_STRING([@<:@--with-powerman-includes=CFLAGS@:>@], [include flags for the libpowerman library]), [ case "${withval}" in yes|no) - AC_MSG_ERROR(invalid option --with(out)-powerman-includes - see docs/configure.txt) + AC_MSG_ERROR([invalid option --with(out)-powerman-includes - see docs/configure.txt]) ;; *) CFLAGS="${withval}" ;; esac - ], [CFLAGS="`pkg-config --silence-errors --cflags libpowerman 2>/dev/null`"]) + ], []) AC_MSG_RESULT([${CFLAGS}]) AC_MSG_CHECKING(for libpowerman libs) @@ -39,12 +68,23 @@ if test -z "${nut_have_libpowerman_seen}"; then LIBS="${withval}" ;; esac - ], [LIBS="`pkg-config --silence-errors --libs libpowerman 2>/dev/null`"]) + ], []) AC_MSG_RESULT([${LIBS}]) dnl check if libpowerman is usable AC_CHECK_HEADERS(libpowerman.h, [nut_have_libpowerman=yes], [nut_have_libpowerman=no], [AC_INCLUDES_DEFAULT]) - AC_CHECK_FUNCS(pm_connect, [], [nut_have_libpowerman=no]) + AC_CHECK_FUNCS(pm_connect, [], [ + dnl Some systems may just have libpowerman in their + dnl standard paths, but not the pkg-config data + AS_IF([test "${nut_have_libpowerman}" = "yes" && test "$POWERMAN_VERSION" = "none" && test -z "$LIBS"], + [AC_MSG_CHECKING([if libpowerman is just present in path]) + LIBS="-L/usr/lib -L/usr/local/lib -lpowerman" + unset ac_cv_func_pm_connect || true + AC_CHECK_FUNCS(pm_connect, [], [nut_have_libpowerman=no]) + AC_MSG_RESULT([${nut_have_libpowerman}]) + ], [nut_have_libpowerman=no] + )] + ) if test "${nut_have_libpowerman}" = "yes"; then LIBPOWERMAN_CFLAGS="${CFLAGS}" diff --git a/m4/nut_check_libssl.m4 b/m4/nut_check_libssl.m4 deleted file mode 100644 index 5984bfd..0000000 --- a/m4/nut_check_libssl.m4 +++ /dev/null @@ -1,72 +0,0 @@ -dnl Check for LIBSSL compiler flags. On success, set nut_have_libssl="yes" -dnl and set LIBSSL_CFLAGS and LIBSSL_LIBS. On failure, set -dnl nut_have_libssl="no". This macro can be run multiple times, but will -dnl do the checking only once. - -AC_DEFUN([NUT_CHECK_LIBSSL], -[ -if test -z "${nut_have_libssl_seen}"; then - nut_have_libssl_seen=yes - - dnl save CFLAGS and LIBS - CFLAGS_ORIG="${CFLAGS}" - LIBS_ORIG="${LIBS}" - - AC_MSG_CHECKING(for openssl version via pkg-config) - OPENSSL_VERSION="`pkg-config --silence-errors --modversion openssl 2>/dev/null`" - if test "$?" = "0" -a -n "${OPENSSL_VERSION}"; then - CFLAGS="`pkg-config --silence-errors --cflags openssl 2>/dev/null`" - LIBS="`pkg-config --silence-errors --libs openssl 2>/dev/null`" - else - OPENSSL_VERSION="none" - CFLAGS="" - LIBS="-lssl -lcrypto" - fi - AC_MSG_RESULT(${OPENSSL_VERSION} found) - - dnl allow overriding openssl settings if the user knows best - AC_MSG_CHECKING(for openssl cflags) - AC_ARG_WITH(ssl-includes, - AS_HELP_STRING([@<:@--with-ssl-includes=CFLAGS@:>@], [include flags for the OpenSSL library]), - [ - case "${withval}" in - yes|no) - AC_MSG_ERROR(invalid option --with(out)-ssl-includes - see docs/configure.txt) - ;; - *) - CFLAGS="${withval}" - ;; - esac - ], []) - AC_MSG_RESULT([${CFLAGS}]) - - AC_MSG_CHECKING(for openssl ldflags) - AC_ARG_WITH(ssl-libs, - AS_HELP_STRING([@<:@--with-ssl-libs=LIBS@:>@], [linker flags for the OpenSSL library]), - [ - case "${withval}" in - yes|no) - AC_MSG_ERROR(invalid option --with(out)-ssl-libs - see docs/configure.txt) - ;; - *) - LIBS="${withval}" - ;; - esac - ], []) - AC_MSG_RESULT([${LIBS}]) - - dnl check if openssl is usable - AC_CHECK_HEADERS(openssl/ssl.h, [nut_have_libssl=yes], [nut_have_libssl=no], [AC_INCLUDES_DEFAULT]) - AC_CHECK_FUNCS(SSL_library_init, [], [nut_have_libssl=no]) - - if test "${nut_have_libssl}" = "yes"; then - AC_DEFINE(HAVE_SSL, 1, [Define to enable SSL development code]) - LIBSSL_CFLAGS="${CFLAGS}" - LIBSSL_LIBS="${LIBS}" - fi - - dnl restore original CFLAGS and LIBS - CFLAGS="${CFLAGS_ORIG}" - LIBS="${LIBS_ORIG}" -fi -]) diff --git a/m4/nut_check_libusb.m4 b/m4/nut_check_libusb.m4 index b2bb0ab..89f7a02 100644 --- a/m4/nut_check_libusb.m4 +++ b/m4/nut_check_libusb.m4 @@ -1,48 +1,173 @@ -dnl Check for LIBUSB compiler flags. On success, set nut_have_libusb="yes" -dnl and set LIBUSB_CFLAGS and LIBUSB_LIBS. On failure, set +dnl Check for LIBUSB 1.0 or 0.1 (and, if found, fill 'nut_usb_lib' with its +dnl approximate version) and its compiler flags. On success, set +dnl nut_have_libusb="yes" and set LIBUSB_CFLAGS and LIBUSB_LIBS. On failure, set dnl nut_have_libusb="no". This macro can be run multiple times, but will dnl do the checking only once. +dnl By default, if both libusb 1.0 and libusb 0.1 are available and appear to be +dnl usable, libusb 1.0 takes precedence. +dnl An optional argument with value 'libusb-1.0' or 'libusb-0.1' can be used to +dnl restrict checks to a specific version. AC_DEFUN([NUT_CHECK_LIBUSB], [ if test -z "${nut_have_libusb_seen}"; then nut_have_libusb_seen=yes + NUT_CHECK_PKGCONFIG dnl save CFLAGS and LIBS CFLAGS_ORIG="${CFLAGS}" LIBS_ORIG="${LIBS}" + CFLAGS="" + LIBS="" - AC_MSG_CHECKING(for libusb version via pkg-config) - LIBUSB_VERSION="`pkg-config --silence-errors --modversion libusb 2>/dev/null`" - if test "$?" = "0" -a -n "${LIBUSB_VERSION}"; then - CFLAGS="`pkg-config --silence-errors --cflags libusb 2>/dev/null`" - LIBS="`pkg-config --silence-errors --libs libusb 2>/dev/null`" - else - AC_MSG_CHECKING(via libusb-config) - LIBUSB_VERSION="`libusb-config --version 2>/dev/null`" - if test "$?" = "0" -a -n "${LIBUSB_VERSION}"; then - CFLAGS="`libusb-config --cflags 2>/dev/null`" - LIBS="`libusb-config --libs 2>/dev/null`" - else + dnl Magic-format string to hold chosen libusb version and its config-source + nut_usb_lib="" + + dnl TOTHINK: What if there are more than 0.1 and 1.0 to juggle? + dnl TODO? Add libusb-compat (1.0 code with 0.1's API) to the mix? + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [AC_MSG_CHECKING([for libusb-1.0 version via pkg-config]) + LIBUSB_1_0_VERSION="`$PKG_CONFIG --silence-errors --modversion libusb-1.0 2>/dev/null`" \ + && test -n "${LIBUSB_1_0_VERSION}" \ + || LIBUSB_1_0_VERSION="none" + AC_MSG_RESULT([${LIBUSB_1_0_VERSION} found]) + + AC_MSG_CHECKING([for libusb(-0.1) version via pkg-config]) + LIBUSB_0_1_VERSION="`$PKG_CONFIG --silence-errors --modversion libusb 2>/dev/null`" \ + && test -n "${LIBUSB_0_1_VERSION}" \ + || LIBUSB_0_1_VERSION="none" + AC_MSG_RESULT([${LIBUSB_0_1_VERSION} found]) + ], + [LIBUSB_0_1_VERSION="none" + LIBUSB_1_0_VERSION="none" + AC_MSG_NOTICE([can not check libusb settings via pkg-config]) + ] + ) + + dnl Note: it seems the script was only shipped for libusb-0.1 + dnl So we don't separate into LIBUSB_0_1_CONFIG and LIBUSB_1_0_CONFIG + AC_PATH_PROGS([LIBUSB_CONFIG], [libusb-config], [none]) + + AC_ARG_WITH(libusb-config, + AS_HELP_STRING([@<:@--with-libusb-config=/path/to/libusb-config@:>@], + [path to program that reports LibUSB configuration]), dnl ...for LibUSB-0.1 + [ + AS_CASE(["${withval}"], + [""], [], dnl empty arg + [yes|no], [ + dnl MAYBE bump preference of script over pkg-config? + AC_MSG_ERROR([invalid option --with(out)-libusb-config - see docs/configure.txt]) + ], + [dnl default + LIBUSB_CONFIG="${withval}" + ] + ) + ] + ) + + AS_IF([test x"${LIBUSB_CONFIG}" != xnone], + [AC_MSG_CHECKING([via ${LIBUSB_CONFIG}]) + LIBUSB_CONFIG_VERSION="`$LIBUSB_CONFIG --version 2>/dev/null`" \ + && test -n "${LIBUSB_CONFIG_VERSION}" \ + || LIBUSB_CONFIG_VERSION="none" + AC_MSG_RESULT([${LIBUSB_CONFIG_VERSION} found]) + ], [LIBUSB_CONFIG_VERSION="none"] + ) + + dnl By default, prefer newest available, and if anything is known + dnl to pkg-config, prefer that. Otherwise, fall back to script data: + AS_IF([test x"${LIBUSB_1_0_VERSION}" != xnone], + [LIBUSB_VERSION="${LIBUSB_1_0_VERSION}" + nut_usb_lib="(libusb-1.0)" + ], + [AS_IF([test x"${LIBUSB_0_1_VERSION}" != xnone], + [LIBUSB_VERSION="${LIBUSB_0_1_VERSION}" + nut_usb_lib="(libusb-0.1)" + ], + [LIBUSB_VERSION="${LIBUSB_CONFIG_VERSION}" + AS_IF([test x"${LIBUSB_CONFIG_VERSION}" != xnone], + [dnl TODO: This assumes 0.1; check for 1.0+ somehow? + nut_usb_lib="(libusb-0.1-config)"], + [nut_usb_lib=""] + )] + )] + ) + + dnl Pick up the default or caller-provided choice here from + dnl NUT_ARG_WITH(usb, ...) in the main configure.ac script + AC_MSG_CHECKING([for libusb preferred version]) + AS_CASE(["${nut_with_usb}"], + [auto], [], dnl Use preference picked above + [yes], [], dnl Use preference from above, fail in the end if none found + [no], [], dnl Try to find, report in the end if that is discarded; TODO: not waste time? + [libusb-1.0|1.0], [ + dnl NOTE: Assuming there is no libusb-config-1.0 or similar script, never saw one + AS_IF([test x"${LIBUSB_1_0_VERSION}" = xnone], + [AC_MSG_ERROR([option --with-usb=${withval} was required, but this library version was not detected]) + ]) + LIBUSB_VERSION="${LIBUSB_1_0_VERSION}" + nut_usb_lib="(libusb-1.0)" + ], + [libusb-0.1|0.1], [ + AS_IF([test x"${LIBUSB_0_1_VERSION}" = xnone \ + && test x"${LIBUSB_CONFIG_VERSION}" = xnone], + [AC_MSG_ERROR([option --with-usb=${withval} was required, but this library version was not detected]) + ]) + AS_IF([test x"${LIBUSB_0_1_VERSION}" != xnone], + [LIBUSB_VERSION="${LIBUSB_0_1_VERSION}" + nut_usb_lib="(libusb-0.1)" + ], + [LIBUSB_VERSION="${LIBUSB_CONFIG_VERSION}" + nut_usb_lib="(libusb-0.1-config)" + ]) + ], + [dnl default + AC_MSG_ERROR([invalid option value --with-usb=${withval} - see docs/configure.txt]) + ] + ) + AC_MSG_RESULT([${LIBUSB_VERSION} ${nut_usb_lib}]) + + AS_IF([test x"${LIBUSB_1_0_VERSION}" != xnone && test x"${nut_usb_lib}" != x"(libusb-1.0)" ], + [AC_MSG_NOTICE([libusb-1.0 support was detected, but another was chosen ${nut_usb_lib}])] + ) + + dnl FIXME? Detect and report all CFLAGS/LIBS that we can, + dnl and *then* pick one set of values to use? + AS_CASE([${nut_usb_lib}], + ["(libusb-1.0)"], [ + CFLAGS="`$PKG_CONFIG --silence-errors --cflags libusb-1.0 2>/dev/null`" + LIBS="`$PKG_CONFIG --silence-errors --libs libusb-1.0 2>/dev/null`" + ], + ["(libusb-0.1)"], [ + CFLAGS="`$PKG_CONFIG --silence-errors --cflags libusb 2>/dev/null`" + LIBS="`$PKG_CONFIG --silence-errors --libs libusb 2>/dev/null`" + ], + ["(libusb-0.1-config)"], [ + CFLAGS="`$LIBUSB_CONFIG --cflags 2>/dev/null`" + LIBS="`$LIBUSB_CONFIG --libs 2>/dev/null`" + ], + [dnl default, for other versions or "none" + AC_MSG_WARN([Defaulting libusb configuration]) LIBUSB_VERSION="none" CFLAGS="" LIBS="-lusb" - fi - fi - AC_MSG_RESULT(${LIBUSB_VERSION} found) + ] + ) + dnl check optional user-provided values for cflags/ldflags + dnl and publish what we end up using AC_MSG_CHECKING(for libusb cflags) AC_ARG_WITH(usb-includes, AS_HELP_STRING([@<:@--with-usb-includes=CFLAGS@:>@], [include flags for the libusb library]), [ - case "${withval}" in - yes|no) - AC_MSG_ERROR(invalid option --with(out)-usb-includes - see docs/configure.txt) - ;; - *) - CFLAGS="${withval}" - ;; - esac + AS_CASE(["${withval}"], + [yes|no], [ + AC_MSG_ERROR(invalid option --with(out)-usb-includes - see docs/configure.txt) + ], + [dnl default + CFLAGS="${withval}" + ] + ) ], []) AC_MSG_RESULT([${CFLAGS}]) @@ -50,28 +175,159 @@ if test -z "${nut_have_libusb_seen}"; then AC_ARG_WITH(usb-libs, AS_HELP_STRING([@<:@--with-usb-libs=LIBS@:>@], [linker flags for the libusb library]), [ - case "${withval}" in - yes|no) - AC_MSG_ERROR(invalid option --with(out)-usb-libs - see docs/configure.txt) - ;; - *) - LIBS="${withval}" - ;; - esac + AS_CASE(["${withval}"], + [yes|no], [ + AC_MSG_ERROR(invalid option --with(out)-usb-libs - see docs/configure.txt) + ], + [dnl default + LIBS="${withval}" + ] + ) ], []) AC_MSG_RESULT([${LIBS}]) - dnl check if libusb is usable - AC_CHECK_HEADERS(usb.h, [nut_have_libusb=yes], [nut_have_libusb=no], [AC_INCLUDES_DEFAULT]) - AC_CHECK_FUNCS(usb_init, [], [nut_have_libusb=no]) + dnl TODO: Consult chosen nut_usb_lib value and/or nut_with_usb argument + dnl (with "auto" we may use a 0.1 if present and working while a 1.0 is + dnl present but useless) + dnl Check if libusb is usable + AC_LANG_PUSH([C]) + if test -n "${LIBUSB_VERSION}"; then + dnl Test specifically for libusb-1.0 via pkg-config, else fall back below + test -n "$PKG_CONFIG" \ + && test x"${nut_usb_lib}" = x"(libusb-1.0)" \ + && $PKG_CONFIG --silence-errors --atleast-version=1.0 libusb-1.0 2>/dev/null + if test "$?" = "0"; then + dnl libusb 1.0: libusb_set_auto_detach_kernel_driver + AC_CHECK_HEADERS(libusb.h, [nut_have_libusb=yes], [nut_have_libusb=no], [AC_INCLUDES_DEFAULT]) + AC_CHECK_FUNCS(libusb_init, [], [nut_have_libusb=no]) + AC_CHECK_FUNCS(libusb_strerror, [], [nut_have_libusb=no; nut_have_libusb_strerror=no]) + if test "${nut_have_libusb_strerror}" = "no"; then + AC_MSG_WARN([libusb_strerror() not found; install libusbx to use libusb 1.0 API. See https://github.com/networkupstools/nut/issues/509]) + fi + if test "${nut_have_libusb}" = "yes"; then + dnl This function is fairly old, but check for it anyway: + AC_CHECK_FUNCS(libusb_kernel_driver_active) + dnl Check for libusb "force driver unbind" availability + AC_CHECK_FUNCS(libusb_set_auto_detach_kernel_driver) + dnl libusb 1.0: libusb_detach_kernel_driver + dnl FreeBSD 10.1-10.3 have this, but not libusb_set_auto_detach_kernel_driver + AC_CHECK_FUNCS(libusb_detach_kernel_driver) + AC_CHECK_FUNCS(libusb_detach_kernel_driver_np) - if test "${nut_have_libusb}" = "yes"; then - dnl Check for libusb "force driver unbind" availability - AC_CHECK_FUNCS(usb_detach_kernel_driver_np) + dnl From libusb-0.1 - check these to have valid config.h definitions + dnl Note: confusingly, FreeBSD does find both as defined + dnl (despite being spread across usb.h and libusb.h), + dnl so our source code has to care :\ + AC_CHECK_FUNCS(usb_detach_kernel_driver_np) + fi + else + dnl libusb 0.1, or missing pkg-config : + AC_CHECK_HEADERS(usb.h, [nut_have_libusb=yes], [nut_have_libusb=no], [AC_INCLUDES_DEFAULT]) + AC_CHECK_FUNCS(usb_init, [], [ + dnl Some systems may just have libusb in their standard + dnl paths, but not the pkg-config or libusb-config data + AS_IF([test "${nut_have_libusb}" = "yes" && test "$LIBUSB_VERSION" = "none" && test -z "$LIBS"], + [AC_MSG_CHECKING([if libusb is just present in path]) + LIBS="-L/usr/lib -L/usr/local/lib -lusb" + unset ac_cv_func_usb_init || true + AC_CHECK_FUNCS(usb_init, [], [nut_have_libusb=no]) + AC_MSG_RESULT([${nut_have_libusb}]) + ], [nut_have_libusb=no] + )] + ) + dnl Check for libusb "force driver unbind" availability + if test "${nut_have_libusb}" = "yes"; then + AC_CHECK_FUNCS(usb_detach_kernel_driver_np) + + dnl From libusb-1.0 - check these to have valid config.h definitions + AC_CHECK_FUNCS(libusb_kernel_driver_active) + AC_CHECK_FUNCS(libusb_set_auto_detach_kernel_driver) + AC_CHECK_FUNCS(libusb_detach_kernel_driver) + AC_CHECK_FUNCS(libusb_detach_kernel_driver_np) + fi + fi + else + nut_have_libusb=no + fi + + AS_IF([test "${nut_have_libusb}" = "yes"], [ + dnl ---------------------------------------------------------------------- + dnl additional USB-related checks + + dnl Solaris 10/11 USB handling (need librt and libusb runtime path) + dnl Should we check for `uname -o == illumos` to avoid legacy here? + dnl Or better yet, perform some active capability tests for need of + dnl workarounds or not? e.g. OpenIndiana should include a capable + dnl version of libusb-1.0.23+ tailored with NUT tests in mind... + dnl HPUX, since v11, needs an explicit activation of pthreads + dnl TODO: There are reports about FreeBSD error-code + dnl handling in libusb-0.1 port returning "-1" always, + dnl instead of differing codes like on other systems. + dnl Should we check for that below?.. + dnl https://github.com/networkupstools/nut/issues/490 + AS_CASE(["${target_os}"], + [solaris2.1*], [ + AC_MSG_CHECKING([for Solaris 10 / 11 specific configuration for usb drivers]) + AC_SEARCH_LIBS(nanosleep, rt) + LIBS="-R/usr/sfw/lib ${LIBS}" + dnl FIXME: Sun's libusb doesn't support timeout (so blocks notification) + dnl and need to call libusb close upon reconnection + dnl TODO: Somehow test for susceptible versions? + AC_DEFINE(SUN_LIBUSB, 1, [Define to 1 for Sun version of the libusb.]) + SUN_LIBUSB=1 + AC_MSG_RESULT([${LIBS}]) + ], + [hpux11*], [ + CFLAGS="${CFLAGS} -lpthread" + ] + ) + ]) + AC_LANG_POP([C]) + + AS_IF([test "${nut_have_libusb}" = "yes"], [ LIBUSB_CFLAGS="${CFLAGS}" LIBUSB_LIBS="${LIBS}" + ], [ + AS_CASE(["${nut_with_usb}"], + [no|auto], [], + [yes|1.0|0.1|libusb-1.0|libusb-0.1], + [dnl Explicitly choosing a library implies 'yes' (i.e. fail if not found), not 'auto'. + AC_MSG_ERROR([USB drivers requested, but libusb not found.]) + ] + ) + ]) + + if test "${nut_with_usb}" = "no"; then + if test -n "${nut_usb_lib}" && test "${nut_usb_lib}" != none ; then + AC_MSG_NOTICE([libusb was detected ${nut_usb_lib}, but a build without USB drivers was requested]) + fi + nut_usb_lib="" + else + nut_with_usb="${nut_have_libusb}" fi + dnl AC_MSG_NOTICE([DEBUG: nut_have_libusb='${nut_have_libusb}']) + dnl AC_MSG_NOTICE([DEBUG: nut_with_usb='${nut_with_usb}']) + dnl AC_MSG_NOTICE([DEBUG: nut_usb_lib='${nut_usb_lib}']) + + dnl Note: AC_DEFINE specifies a verbatim "value" so we pre-calculate it! + dnl Source code should be careful to use "#if" and not "#ifdef" when + dnl checking these values during the build. And both must be defined + dnl with some value. + AS_IF([test "${nut_with_usb}" = "yes" && test "${nut_usb_lib}" = "(libusb-1.0)"], + [AC_DEFINE([WITH_LIBUSB_1_0], [1], + [Define to 1 for version 1.0 of the libusb (via pkg-config).])], + [AC_DEFINE([WITH_LIBUSB_1_0], [0], + [Define to 1 for version 1.0 of the libusb (via pkg-config).])] + ) + + AS_IF([test "${nut_with_usb}" = "yes" && test "${nut_usb_lib}" = "(libusb-0.1)" -o "${nut_usb_lib}" = "(libusb-0.1-config)"], + [AC_DEFINE([WITH_LIBUSB_0_1], [1], + [Define to 1 for version 0.1 of the libusb (via pkg-config or libusb-config).])], + [AC_DEFINE([WITH_LIBUSB_0_1], [0], + [Define to 1 for version 0.1 of the libusb (via pkg-config or libusb-config).])] + ) + dnl restore original CFLAGS and LIBS CFLAGS="${CFLAGS_ORIG}" LIBS="${LIBS_ORIG}" diff --git a/m4/nut_check_libwrap.m4 b/m4/nut_check_libwrap.m4 index 7f1ff9d..c61f01e 100644 --- a/m4/nut_check_libwrap.m4 +++ b/m4/nut_check_libwrap.m4 @@ -18,6 +18,7 @@ if test -z "${nut_have_libwrap_seen}"; then dnl The line below does not work on Solaris 10. dnl AC_SEARCH_LIBS(request_init, wrap, [], [nut_have_libwrap=no]) AC_MSG_CHECKING(for library containing request_init) + AC_LANG_PUSH([C]) AC_LINK_IFELSE([AC_LANG_PROGRAM([[ #include int allow_severity = 0, deny_severity = 0; @@ -35,6 +36,7 @@ int allow_severity = 0, deny_severity = 0; nut_have_libwrap=no ]) ]) + AC_LANG_POP([C]) if test "${nut_have_libwrap}" = "yes"; then AC_DEFINE(HAVE_WRAP, 1, [Define to enable libwrap support]) diff --git a/m4/nut_check_os.m4 b/m4/nut_check_os.m4 index c735712..5f7dbd9 100644 --- a/m4/nut_check_os.m4 +++ b/m4/nut_check_os.m4 @@ -23,8 +23,8 @@ AC_DEFUN_ONCE([NUT_OS_FUNCTIONS], case "${host_cpu}-${host_os}" in *-aix*) echo 'aix' ;; *-freebsd*) echo 'freebsd' ;; - *-darwin*) echo 'darwin' ;; - *solaris*) echo 'solaris' ;; + *-darwin*) echo 'darwin' ;; + *solaris*) echo 'esyscmd(uname -sp)' ;; *-hpux*) echo 'hpux' ;; esac esac diff --git a/m4/nut_check_pkgconfig.m4 b/m4/nut_check_pkgconfig.m4 new file mode 100644 index 0000000..263be4b --- /dev/null +++ b/m4/nut_check_pkgconfig.m4 @@ -0,0 +1,91 @@ +dnl Check for LIBPOWERMAN compiler flags. On success, set nut_have_libpowerman="yes" +dnl and set LIBPOWERMAN_CFLAGS and LIBPOWERMAN_LIBS. On failure, set +dnl nut_have_libpowerman="no". This macro can be run multiple times, but will +dnl do the checking only once. + +AC_DEFUN([NUT_CHECK_PKGCONFIG], +[ +AS_IF([test -z "${nut_have_pkg_config_seen}"], [ + nut_have_pkg_config_seen=yes + + dnl Note that PKG_CONFIG may be a filename, path, + dnl or either with args - so no quoting here + AC_MSG_CHECKING([whether usable PKG_CONFIG was already detected by autoconf]) + AS_IF([test -n "${PKG_CONFIG-}" && test x"${PKG_CONFIG-}" != x"false" && $PKG_CONFIG --help 2>&1 | grep -E '(--cflags|--libs)' >/dev/null], + [AC_MSG_RESULT([yes: ${PKG_CONFIG}]) + have_PKG_CONFIG=yes + ], + [AC_MSG_RESULT([no]) + PKG_CONFIG=false + have_PKG_CONFIG=no + ] + ) + + AS_IF([test x"${PKG_CONFIG-}" = x"false"], + [dnl Some systems have older autotools without direct macro support for PKG_CONF* + have_PKG_CONFIG=yes + AC_PATH_PROG(dummy_PKG_CONFIG, pkg-config) + + AC_ARG_WITH(pkg-config, + AS_HELP_STRING([@<:@--with-pkg-config=/path/to/gdlib-config@:>@], + [path to program that reports development package configuration]), + [ + case "${withval}" in + "") ;; + yes|no) + AC_MSG_ERROR(invalid option --with(out)-pkg-config - see docs/configure.txt) + ;; + *) + dummy_PKG_CONFIG="${withval}" + ;; + esac + ]) + + AC_MSG_CHECKING([whether usable PKG_CONFIG is present in PATH or was set by caller]) + AS_IF([test x"$dummy_PKG_CONFIG" = xno || test -z "$dummy_PKG_CONFIG"], + [AC_MSG_RESULT([no]) + PKG_CONFIG=false + have_PKG_CONFIG=no + ], + [AS_IF([$dummy_PKG_CONFIG --help 2>&1 | grep -E '(--cflags|--libs)' >/dev/null], + [AC_MSG_RESULT([yes: ${dummy_PKG_CONFIG}]) + have_PKG_CONFIG=yes + PKG_CONFIG="$dummy_PKG_CONFIG" + ], + [AC_MSG_RESULT([no]) + PKG_CONFIG=false + have_PKG_CONFIG=no + ] + )] + )] + ) + + have_PKG_CONFIG_MACROS=no + AS_IF([test x"$have_PKG_CONFIG" = xyes], + [AC_MSG_NOTICE([checking for autoconf macro support of pkg-config (${PKG_CONFIG})]) + dummy_RES=0 + ifdef([PKG_PROG_PKG_CONFIG], [], [dummy_RES=1]) + ifdef([PKG_CHECK_MODULES], [], [dummy_RES=2]) + AS_IF([test "${dummy_RES}" = 0], + [AC_MSG_NOTICE([checking for autoconf macro support of pkg-config module checker]) + dnl The m4 macro below may be not defined if pkg-config package is not + dnl installed. Use of ifdef (here and below for e.g. CPPUNIT check) + dnl allows to avoid shell syntax errors in generated configure script + dnl by defining a dummy macro in-place. + ifdef([PKG_CHECK_MODULES], [], [AC_DEFUN([PKG_CHECK_MODULES], [false])]) + PKG_CHECK_MODULES([dummy_PKG_CONFIG], [pkg-config], [have_PKG_CONFIG_MACROS=yes]) + ] + )] + ) + + AS_IF([test x"$have_PKG_CONFIG" = xno], + [AC_MSG_WARN([pkg-config program is needed to look for further dependencies (will be skipped)]) + PKG_CONFIG="false" + ], + [AS_IF([test x"$have_PKG_CONFIG_MACROS" = xno], + [AC_MSG_WARN([pkg-config macros are needed to look for further dependencies, but in some cases pkg-config program can be used directly])] + )] + ) + + ]) dnl if nut_have_pkg_config_seen +]) diff --git a/m4/nut_check_python.m4 b/m4/nut_check_python.m4 new file mode 100644 index 0000000..6357286 --- /dev/null +++ b/m4/nut_check_python.m4 @@ -0,0 +1,128 @@ +dnl Check for python binary program names per language version +dnl to embed into scripts and Make rules + +AC_DEFUN([NUT_CHECK_PYTHON], +[ + AS_IF([test -z "${nut_with_python}"], [ + NUT_ARG_WITH([python], [Use a particular program name of the python interpeter], [auto]) + + PYTHON="" + AS_CASE([${nut_with_python}], + [auto|yes|""], [AC_CHECK_PROGS([PYTHON], [python python3 python2], [_python_runtime])], + [no], [PYTHON="no"], + [PYTHON="${nut_with_python}"] + ) + + dnl Default to calling a basename from PATH, only use a specific full pathname + dnl if provided by the caller: + AS_CASE([${PYTHON}], + [_python_runtime], [ + PYTHON="/usr/bin/env python" + AC_MSG_WARN([A python program name was not detected during configuration, will default to '$PYTHON' (scripts will fail if that is not in PATH at run time)])], + [no], [], + [/*" "*" "*], [ + AC_MSG_WARN([A python program name is not a single token (was specified with more than one argument?), so shebangs can be not reliable]) + ], + [/*], [], + [*" "*" "*], [ + AC_MSG_WARN([A python program name is not a single token (was specified with more than one argument?), so shebangs can be not reliable]) + PYTHON="/usr/bin/env ${PYTHON}" + ], + [*" "*], [ + AC_MSG_WARN([A python program name is not a single token (was specified with an argument?), so /usr/bin/env shebangs can be not reliable]) + PYTHON="/usr/bin/env ${PYTHON}" + ], + [*], [PYTHON="/usr/bin/env ${PYTHON}"] + ) + + AC_MSG_CHECKING([python interpeter to call]) + AC_MSG_RESULT([${PYTHON}]) + AC_SUBST([PYTHON], [${PYTHON}]) + AM_CONDITIONAL([HAVE_PYTHON], [test "${PYTHON}" != "no"]) + AS_IF([test -n "${PYTHON}"], [export PYTHON]) + ]) +]) + +AC_DEFUN([NUT_CHECK_PYTHON2], +[ + AS_IF([test -z "${nut_with_python2}"], [ + NUT_ARG_WITH([python2], [Use a particular program name of the python2 interpeter for code that needs that version and is not compatible with python3], [auto]) + + PYTHON2="" + AS_CASE([${nut_with_python2}], + [auto|yes|""], [AC_CHECK_PROGS([PYTHON2], [python2 python2.7 python-2.7 python], [_python2_runtime])], + [no], [PYTHON2="no"], + [PYTHON2="${nut_with_python2}"] + ) + + dnl Default to calling a basename from PATH, only use a specific full pathname + dnl if provided by the caller: + AS_CASE([${PYTHON2}], + [_python2_runtime], [ + PYTHON2="/usr/bin/env python2" + AC_MSG_WARN([A python2 program name was not detected during configuration, will default to '$PYTHON2' (scripts will fail if that is not in PATH at run time)])], + [no], [], + [/*" "*" "*], [ + AC_MSG_WARN([A python2 program name is not a single token (was specified with more than one argument?), so shebangs can be not reliable]) + ], + [/*], [], + [*" "*" "*], [ + AC_MSG_WARN([A python2 program name is not a single token (was specified with more than one argument?), so shebangs can be not reliable]) + PYTHON2="/usr/bin/env ${PYTHON2}" + ], + [*" "*], [ + AC_MSG_WARN([A python2 program name is not a single token (was specified with an argument?), so /usr/bin/env shebangs can be not reliable]) + PYTHON2="/usr/bin/env ${PYTHON2}" + ], + [*], [PYTHON2="/usr/bin/env ${PYTHON2}"] + ) + + AC_MSG_CHECKING([python2 interpeter to call]) + AC_MSG_RESULT([${PYTHON2}]) + AC_SUBST([PYTHON2], [${PYTHON2}]) + AM_CONDITIONAL([HAVE_PYTHON2], [test "${PYTHON2}" != "no"]) + AS_IF([test -n "${PYTHON2}"], [export PYTHON2]) + ]) +]) + +AC_DEFUN([NUT_CHECK_PYTHON3], +[ + AS_IF([test -z "${nut_with_python3}"], [ + NUT_ARG_WITH([python3], [Use a particular program name of the python3 interpeter for code that needs that version and is not compatible with python2], [auto]) + + PYTHON3="" + AS_CASE([${nut_with_python3}], + [auto|yes|""], [AC_CHECK_PROGS([PYTHON3], [python3 python3.9 python-3.9 python3.7 python-3.7 python3.5 python-3.5 python], [_python3_runtime])], + [no], [PYTHON3="no"], + [PYTHON3="${nut_with_python3}"] + ) + + dnl Default to calling a basename from PATH, only use a specific full pathname + dnl if provided by the caller: + AS_CASE([${PYTHON3}], + [_python3_runtime], [ + PYTHON3="/usr/bin/env python3" + AC_MSG_WARN([A python3 program name was not detected during configuration, will default to '$PYTHON3' (scripts will fail if that is not in PATH at run time)])], + [no], [], + [/*" "*" "*], [ + AC_MSG_WARN([A python3 program name is not a single token (was specified with more than one argument?), so shebangs can be not reliable]) + ], + [/*], [], + [*" "*" "*], [ + AC_MSG_WARN([A python3 program name is not a single token (was specified with more than one argument?), so shebangs can be not reliable]) + PYTHON3="/usr/bin/env ${PYTHON3}" + ], + [*" "*], [ + AC_MSG_WARN([A python3 program name is not a single token (was specified with an argument?), so /usr/bin/env shebangs can be not reliable]) + PYTHON3="/usr/bin/env ${PYTHON3}" + ], + [*], [PYTHON3="/usr/bin/env ${PYTHON3}"] + ) + + AC_MSG_CHECKING([python3 interpeter to call]) + AC_MSG_RESULT([${PYTHON3}]) + AC_SUBST([PYTHON3], [${PYTHON3}]) + AM_CONDITIONAL([HAVE_PYTHON3], [test "${PYTHON3}" != "no"]) + AS_IF([test -n "${PYTHON3}"], [export PYTHON3]) + ]) +]) diff --git a/m4/nut_compiler_family.m4 b/m4/nut_compiler_family.m4 new file mode 100644 index 0000000..8cdc972 --- /dev/null +++ b/m4/nut_compiler_family.m4 @@ -0,0 +1,241 @@ +dnl detect if current compiler is clang or gcc (or...) + +AC_DEFUN([NUT_COMPILER_FAMILY], +[ + AC_CACHE_CHECK([if CC compiler family is GCC], + [nut_cv_GCC], + [AS_IF([test -n "$CC"], + [AS_IF([$CC --version 2>&1 | grep 'Free Software Foundation' > /dev/null], + [nut_cv_GCC=yes],[nut_cv_GCC=no])], + [AC_MSG_ERROR([CC is not set])] + )]) + + AC_CACHE_CHECK([if CXX compiler family is GCC], + [nut_cv_GXX], + [AS_IF([test -n "$CXX"], + [AS_IF([$CXX --version 2>&1 | grep 'Free Software Foundation' > /dev/null], + [nut_cv_GXX=yes],[nut_cv_GXX=no])], + [AC_MSG_ERROR([CXX is not set])] + )]) + + AC_CACHE_CHECK([if CPP preprocessor family is GCC], + [nut_cv_GPP], + [AS_IF([test -n "$CPP"], + [AS_IF([$CPP --version 2>&1 | grep 'Free Software Foundation' > /dev/null], + [nut_cv_GPP=yes],[nut_cv_GPP=no])], + [AC_MSG_ERROR([CPP is not set])] + )]) + + AS_IF([test "x$GCC" = "x" && test "$nut_cv_GCC" = yes], [GCC=yes]) + AS_IF([test "x$GXX" = "x" && test "$nut_cv_GXX" = yes], [GXX=yes]) + AS_IF([test "x$GPP" = "x" && test "$nut_cv_GPP" = yes], [GPP=yes]) + + AC_CACHE_CHECK([if CC compiler family is clang], + [nut_cv_CLANGCC], + [AS_IF([test -n "$CC"], + [AS_IF([$CC --version 2>&1 | grep -E '(clang version|Apple LLVM version .*clang-)' > /dev/null], + [nut_cv_CLANGCC=yes],[nut_cv_CLANGCC=no])], + [AC_MSG_ERROR([CC is not set])] + )]) + + AC_CACHE_CHECK([if CXX compiler family is clang], + [nut_cv_CLANGXX], + [AS_IF([test -n "$CXX"], + [AS_IF([$CXX --version 2>&1 | grep -E '(clang version|Apple LLVM version .*clang-)' > /dev/null], + [nut_cv_CLANGXX=yes],[nut_cv_CLANGXX=no])], + [AC_MSG_ERROR([CXX is not set])] + )]) + + AC_CACHE_CHECK([if CPP preprocessor family is clang], + [nut_cv_CLANGPP], + [AS_IF([test -n "$CPP"], + [AS_IF([$CPP --version 2>&1 | grep -E '(clang version|Apple LLVM version .*clang-)' > /dev/null], + [nut_cv_CLANGPP=yes],[nut_cv_CLANGPP=no])], + [AC_MSG_ERROR([CPP is not set])] + )]) + + AS_IF([test "x$CLANGCC" = "x" && test "$nut_cv_CLANGCC" = yes], [CLANGCC=yes]) + AS_IF([test "x$CLANGXX" = "x" && test "$nut_cv_CLANGXX" = yes], [CLANGXX=yes]) + AS_IF([test "x$CLANGPP" = "x" && test "$nut_cv_CLANGPP" = yes], [CLANGPP=yes]) +]) + +AC_DEFUN([NUT_CHECK_COMPILE_FLAG], +[ +dnl Note: with this line uncommented, builds report +dnl sed: 0: conftest.c: No such file or directory +dnl so seemingly try to parse the method without args: + dnl### AC_REQUIRE([AX_RUN_OR_LINK_IFELSE]) + +dnl Note: per https://stackoverflow.com/questions/52557417/how-to-check-support-compile-flag-in-autoconf-for-clang +dnl the -Werror below is needed to detect "warnings" about unsupported options + COMPILERFLAG="$1" + +dnl We also try to run an actual build since tools called from that might +dnl complain if they are forwarded unknown flags accepted by the front-end. + SAVED_CFLAGS="$CFLAGS" + SAVED_CXXFLAGS="$CXXFLAGS" + + AC_LANG_PUSH([C]) + AX_CHECK_COMPILE_FLAG([${COMPILERFLAG}], + [CFLAGS="$CFLAGS ${COMPILERFLAG}" + AX_RUN_OR_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [], [CFLAGS="$SAVED_CFLAGS"]) + ], [], [-Werror]) + AC_LANG_POP([C]) + + AC_LANG_PUSH([C++]) + AX_CHECK_COMPILE_FLAG([${COMPILERFLAG}], + [CXXFLAGS="$CXXFLAGS ${COMPILERFLAG}" + AX_RUN_OR_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [], [CXXFLAGS="$SAVED_CXXFLAGS"]) + ], [], [-Werror]) + AC_LANG_POP([C++]) +]) + +AC_DEFUN([NUT_COMPILER_FAMILY_FLAGS], +[ + AC_MSG_NOTICE([Detecting support for additional compiler flags]) + +dnl -Qunused-arguments: +dnl Do not die due to `clang: error: argument unused during compilation: '-I .'` +dnl -Wno-unknown-warning-option: Do not die (on older clang releases) due to +dnl error: unknown warning option '-Wno-double-promotion'; did you mean +dnl '-Wno-documentation'? [-Werror,-Wunknown-warning-option] +dnl -fcolor-diagnostics: help find where bugs are in the wall of text (clang) +dnl -fdiagnostics-color=ARG: help find where bugs are in the wall of text (gcc) + + dnl First check for this to avoid failing on unused include paths etc: + NUT_CHECK_COMPILE_FLAG([-Qunused-arguments]) + + m4_foreach_w([TESTCOMPILERFLAG], [ + -Wno-reserved-identifier + ], [ + NUT_CHECK_COMPILE_FLAG([TESTCOMPILERFLAG]) + ]) + + dnl Note: each m4_foreach_w arg must be named uniquely + dnl Note: Seems -fcolor-diagnostics is clang-only and sometimes + dnl gcc blindly accepts it in test and fails to use later. + AS_IF([test x"${nut_enable_Wcolor}" = xyes], [ + m4_foreach_w([TESTCOMPILERFLAG_COLOR], [ + -fdiagnostics-color=always + ], [ + NUT_CHECK_COMPILE_FLAG([TESTCOMPILERFLAG_COLOR]) + ]) + ], [AC_MSG_NOTICE([NOT checking for options to request colorized compiler output (pass --enable-Wcolor for that)])]) + + dnl Last check for this to avoid accepting anything regardless of support. + dnl NOTE that some toolkit versions accept this option blindly and without + dnl really supporting it (but not erroring out on it, either): + dnl cc1: note: unrecognized command-line option '-Wno-unknown-warning-option' + dnl may have been intended to silence earlier diagnostics + NUT_CHECK_COMPILE_FLAG([-Wno-unknown-warning-option]) + +dnl # Older "brute-forced" settings: +dnl AS_IF([test "x$CLANGCC" = xyes], [CFLAGS="$CFLAGS -Wno-unknown-warning-option"]) +dnl AS_IF([test "x$CLANGXX" = xyes], [CXXFLAGS="$CXXFLAGS -Wno-unknown-warning-option"]) + +dnl # Despite the internet lore, practical GCC versions seen so far +dnl # (4.x-10.x) do not know of this CLI option, with varied results +dnl # from "cc1: note: unrecognized command-line option '-Wno-unknown-warning' +dnl # may have been intended to silence earlier diagnostics" +dnl # to "cc1: error: unrecognized command line option '-Wno-unknown-warning' +dnl # [-Werror]"... so we do not pass it by default: +dnl AS_IF([test "x$GCC" = xyes], [CFLAGS="$CFLAGS -Wno-unknown-warning"]) +dnl AS_IF([test "x$GXX" = xyes], [CXXFLAGS="$CXXFLAGS -Wno-unknown-warning"]) + +dnl # There should be no need to include standard system paths (and possibly +dnl # confuse the compiler assumptions - along with its provided headers)... +dnl # ideally; in practice however cppunit, net-snmp and some system include +dnl # files do cause grief to picky compiler settings (more so from third +dnl # party packages shipped via /usr/local/... namespace): + AS_IF([test "x$CLANGCC" = xyes -o "x$GCC" = xyes], [ +dnl # CFLAGS="-isystem /usr/include $CFLAGS" + AS_IF([test -d /usr/local/include], + [CFLAGS="-isystem /usr/local/include $CFLAGS"]) + AS_IF([test -d /usr/pkg/include], + [CFLAGS="-isystem /usr/pkg/include $CFLAGS"]) + ]) + AS_IF([test "x$CLANGXX" = xyes -o "x$GXX" = xyes], [ +dnl # CXXFLAGS="-isystem /usr/include $CXXFLAGS" + AS_IF([test -d /usr/local/include], + [CXXFLAGS="-isystem /usr/local/include $CXXFLAGS"]) + AS_IF([test -d /usr/pkg/include], + [CXXFLAGS="-isystem /usr/pkg/include $CXXFLAGS"]) + ]) + +dnl # Default to avoid noisy warnings on older compilers +dnl # (gcc-4.x, clang-3.x) due to their preference of +dnl # ANSI C (C89/C90) out of the box. While NUT codebase +dnl # currently can build in that mode, reliability of +dnl # results is uncertain - third-party and/or system +dnl # headers and libs seemingly no longer care for C90 +dnl # on modern systems, and we have no recent data from +dnl # truly legacy systems which have no choice. +dnl # Some distributions and platforms also have problems +dnl # building in "strict C" mode, so for the GNU-compatible +dnl # compilers we default to the GNU C/C++ dialects. + AS_IF([test "x$GCC" = xyes -o "x$CLANGCC" = xyes], + [AS_CASE(["${CFLAGS}"], [-std=*], [], + [AC_LANG_PUSH([C]) + AX_CHECK_COMPILE_FLAG([-std=gnu99], + [AC_MSG_NOTICE([Defaulting C standard support to GNU C99 on a GCC or CLANG compatible compiler]) + CFLAGS="$CFLAGS -std=gnu99" + ], [], [-Werror]) + AC_LANG_POP([C]) + ]) + ]) + +dnl # Note: this might upset some very old compilers +dnl # but then by default we wouldn't build C++ parts + AS_IF([test "x$GCC" = xyes -o "x$CLANGCC" = xyes], + [AS_CASE(["${CXXFLAGS}"], [-std=*], [], + [AC_LANG_PUSH([C++]) + AX_CHECK_COMPILE_FLAG([-std=gnu++11], + [AC_MSG_NOTICE([Defaulting C++ standard support to GNU C++11 on a GCC or CLANG compatible compiler]) + CXXFLAGS="$CXXFLAGS -std=gnu++11" + ], [], [-Werror]) + AC_LANG_POP([C++]) + ]) + ]) + +]) + +AC_DEFUN([NUT_COMPILER_FAMILY_FLAGS_DEFAULT_STANDARD], +[ +dnl # Default to avoid noisy warnings on older compilers +dnl # (gcc-4.x, clang-3.x) due to their preference of +dnl # ANSI C (C89/C90) out of the box. While NUT codebase +dnl # currently can build in that mode, reliability of +dnl # results is uncertain - third-party and/or system +dnl # headers and libs seemingly no longer care for C90 +dnl # on modern systems, and we have no recent data from +dnl # truly legacy systems which have no choice. +dnl # Some distributions and platforms also have problems +dnl # building in "strict C" mode, so for the GNU-compatible +dnl # compilers we default to the GNU C/C++ dialects. + AS_IF([test "x$GCC" = xyes -o "x$CLANGCC" = xyes], + [AS_CASE(["${CFLAGS}"], [*-std=*], [], + [AC_LANG_PUSH([C]) + AX_CHECK_COMPILE_FLAG([-std=gnu99], + [AC_MSG_NOTICE([Defaulting C standard support to GNU C99 on a GCC or CLANG compatible compiler]) + CFLAGS="$CFLAGS -std=gnu99" + ], [], [-Werror]) + AC_LANG_POP([C]) + ]) + ]) + +dnl # Note: this might upset some very old compilers +dnl # but then by default we wouldn't build C++ parts + AS_IF([test "x$GCC" = xyes -o "x$CLANGCC" = xyes], + [AS_CASE(["${CXXFLAGS}"], [*-std=*], [], + [AC_LANG_PUSH([C++]) + AX_CHECK_COMPILE_FLAG([-std=gnu++11], + [AC_MSG_NOTICE([Defaulting C++ standard support to GNU C++11 on a GCC or CLANG compatible compiler]) + CXXFLAGS="$CXXFLAGS -std=gnu++11" + ], [], [-Werror]) + AC_LANG_POP([C++]) + ]) + ]) + +]) diff --git a/m4/nut_config_libhal.m4 b/m4/nut_config_libhal.m4 deleted file mode 100644 index 23f6192..0000000 --- a/m4/nut_config_libhal.m4 +++ /dev/null @@ -1,102 +0,0 @@ -dnl Check for LIBHAL configuration if support for HAL was found. -dnl This keeps compile and link time options separate from runtime -dnl configuration items. This macro can be run multiple times, but -dnl will do the checking only once. - -AC_DEFUN([NUT_CONFIG_LIBHAL], -[ -if test -z "${nut_have_config_libhal_seen}" -a "${nut_have_libhal}" = "yes"; then - nut_have_config_libhal_seen=yes - - AC_REQUIRE([NUT_CHECK_LIBHAL]) - - AC_MSG_CHECKING(for libhal user) - AC_ARG_WITH(hal-user, - AS_HELP_STRING([@<:@--with-hal-user=USER@:>@], [addons run as user]), - [ - case "${withval}" in - yes|no) - AC_MSG_ERROR(invalid option --with(out)-hal-user - see docs/configure.txt) - ;; - *) - HAL_USER="${withval}" - ;; - esac - ], [ - dnl this will only work as of HAL 0.5.9 - HAL_USER="`pkg-config --silence-errors --variable=haluser hal 2>/dev/null`" - if test "$?" != "0" -o -z "${HAL_USER}"; then - HAL_USER="haldaemon" - fi - ]) - AC_MSG_RESULT(${HAL_USER}) - AC_DEFINE_UNQUOTED(HAL_USER, "${HAL_USER}", [addons run as user]) - - AC_MSG_CHECKING(for libhal device match key) - AC_ARG_WITH(hal-device-match-key, - AS_HELP_STRING([@<:@--with-hal-device-match-key=KEY@:>@], [device match key]), - [ - case "${withval}" in - yes|no) - AC_MSG_ERROR(invalid option --with(out)-hal-device-match-key - see docs/configure.txt) - ;; - *) - HAL_DEVICE_MATCH_KEY="${withval}" - ;; - esac - ], [ - dnl the device match key changed with HAL 0.5.11 - if pkg-config --silence-errors --atleast-version=0.5.11 hal 2>/dev/null; then - HAL_DEVICE_MATCH_KEY="info.bus" - else - HAL_DEVICE_MATCH_KEY="info.subsystem" - fi - ]) - AC_MSG_RESULT(${HAL_DEVICE_MATCH_KEY}) - - AC_MSG_CHECKING(for libhal Callouts path) - AC_ARG_WITH(hal-callouts-path, - AS_HELP_STRING([@<:@--with-hal-callouts-path=PATH@:>@], [installation path for callouts]), - [ - case "${withval}" in - yes|no) - AC_MSG_ERROR(invalid option --with(out)-hal-callouts-path - see docs/configure.txt) - ;; - *) - HAL_CALLOUTS_PATH="${withval}" - ;; - esac - ], [ - dnl Determine installation path for callouts - dnl As per HAL spec, §5 Callouts addon install path: $libdir/hal - HAL_CALLOUTS_PATH="`pkg-config --silence-errors --variable=libexecdir hal 2>/dev/null`" - if test "$?" != "0" -o -z "${HAL_CALLOUTS_PATH}"; then - HAL_CALLOUTS_PATH="${libdir}/hal" - fi - ]) - AC_MSG_RESULT(${HAL_CALLOUTS_PATH}) - - AC_MSG_CHECKING(for libhal Device Information path) - AC_ARG_WITH(hal-fdi-path, - AS_HELP_STRING([@<:@--with-hal-fdi-path=PATH@:>@], [installation path for device information files]), - [ - case "${withval}" in - yes|no) - AC_MSG_ERROR(invalid option --with(out)-hal-fdi-path - see docs/configure.txt) - ;; - *) - HAL_FDI_PATH="${withval}" - ;; - esac - ], [ - dnl Determine installation path for .fdi - dnl As per HAL spec, §2 Device Information Files - dnl fdi install path: $datarootdir/hal/fdi/information/20thirdparty - HAL_FDI_PATH="`pkg-config --silence-errors --variable=hal_fdidir hal 2>/dev/null`" - if test "$?" != "0" -o -z "${HAL_FDI_PATH}"; then - HAL_FDI_PATH="${datarootdir}/hal/fdi/information/20thirdparty" - fi - ]) - AC_MSG_RESULT(${HAL_FDI_PATH}) -fi -]) diff --git a/m4/nut_func_getnameinfo_argtypes.m4 b/m4/nut_func_getnameinfo_argtypes.m4 new file mode 100644 index 0000000..8f32c42 --- /dev/null +++ b/m4/nut_func_getnameinfo_argtypes.m4 @@ -0,0 +1,96 @@ +dnl This code was lifted and adapted for NUT from cURL project: +dnl https://github.com/curl/curl/blob/e3657644d695373e9cf9ab9b4f1571afda7fd041/acinclude.m4#L228 + +dnl NUT_FUNC_GETNAMEINFO_ARGTYPES +dnl ------------------------------------------------- +dnl Check the type to be passed to five of the arguments +dnl of getnameinfo function, and define those types in +dnl macros GETNAMEINFO_TYPE_ARG1, GETNAMEINFO_TYPE_ARG2, +dnl GETNAMEINFO_TYPE_ARG46 (4 and 6) and GETNAMEINFO_TYPE_ARG7. +dnl As seen from the loop, these vary a lot between OSes... +dnl Order of attempts in the loop below was updated to first +dnl try and quickly match current X/Open definition at +dnl https://pubs.opengroup.org/onlinepubs/9699919799/functions/getnameinfo.html +dnl for modern conforming OSes. + +AC_DEFUN([NUT_FUNC_GETNAMEINFO_ARGTYPES], [ + AC_REQUIRE([NUT_CHECK_HEADER_WS2TCPIP])dnl + AC_REQUIRE([NUT_TYPE_SOCKLEN_T])dnl + AC_CHECK_HEADERS(sys/types.h sys/socket.h netdb.h) + AC_LANG_PUSH([C]) + AC_CACHE_CHECK([types of arguments for getnameinfo], + [nut_cv_func_getnameinfo_args], [ + nut_cv_func_getnameinfo_args="unknown" + for gni_arg1 in 'const struct sockaddr *' 'struct sockaddr *' 'void *'; do + for gni_arg2 in 'socklen_t' 'size_t' 'int'; do + for gni_arg46 in 'socklen_t' 'size_t' 'int' 'unsigned int'; do + for gni_arg7 in 'int' 'unsigned int'; do + AC_COMPILE_IFELSE([ + AC_LANG_PROGRAM([ +#undef inline +#ifdef HAVE_WINDOWS_H +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# if (!defined(_WIN32_WINNT)) || (_WIN32_WINNT < 0x0501) +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x0501 +# endif +# include +# ifdef HAVE_WINSOCK2_H +# include +# ifdef HAVE_WS2TCPIP_H +# include +# endif +# endif +# define GNICALLCONV WSAAPI +#else +# ifdef HAVE_SYS_TYPES_H +# include +# endif +# ifdef HAVE_SYS_SOCKET_H +# include +# endif +# ifdef HAVE_NETDB_H +# include +# endif +# define GNICALLCONV +#endif + extern int GNICALLCONV getnameinfo($gni_arg1, $gni_arg2, + char *, $gni_arg46, + char *, $gni_arg46, + $gni_arg7); + ],[ + $gni_arg2 salen=0; + $gni_arg46 hostlen=0; + $gni_arg46 servlen=0; + $gni_arg7 flags=0; + int res = getnameinfo(0, salen, 0, hostlen, 0, servlen, flags); + ]) + ],[ + nut_cv_func_getnameinfo_args="$gni_arg1,$gni_arg2,$gni_arg46,$gni_arg7" + break 4 + ]) + done + done + done + done + ]) + AC_LANG_POP([C]) + if test "$nut_cv_func_getnameinfo_args" = "unknown"; then + AC_MSG_WARN([Cannot find proper types to use for getnameinfo args]) + else + gni_prev_IFS=$IFS; IFS=',' + set dummy `echo "$nut_cv_func_getnameinfo_args" | sed 's/\*/\*/g'` + IFS=$gni_prev_IFS + shift + AC_DEFINE_UNQUOTED(GETNAMEINFO_TYPE_ARG1, $[1], + [Define to the type of arg 1 for getnameinfo.]) + AC_DEFINE_UNQUOTED(GETNAMEINFO_TYPE_ARG2, $[2], + [Define to the type of arg 2 for getnameinfo.]) + AC_DEFINE_UNQUOTED(GETNAMEINFO_TYPE_ARG46, $[3], + [Define to the type of args 4 and 6 for getnameinfo.]) + AC_DEFINE_UNQUOTED(GETNAMEINFO_TYPE_ARG7, $[4], + [Define to the type of arg 7 for getnameinfo.]) + fi +]) diff --git a/m4/nut_report_feature.m4 b/m4/nut_report_feature.m4 index f6f35b7..72667ba 100644 --- a/m4/nut_report_feature.m4 +++ b/m4/nut_report_feature.m4 @@ -30,4 +30,13 @@ AC_DEFUN([NUT_REPORT_FEATURE], AC_DEFUN([NUT_PRINT_FEATURE_REPORT], [ cat conf_nut_report_feature + + echo "------------------" + echo "Compiler settings:" + printf 'CC \t:%s\n' "$CC" + printf 'CFLAGS \t:%s\n' "$CFLAGS" + printf 'CXX \t:%s\n' "$CXX" + printf 'CXXFLAGS\t:%s\n' "$CXXFLAGS" + printf 'CPP \t:%s\n' "$CPP" + printf 'CPPFLAGS\t:%s\n' "$CPPFLAGS" ]) diff --git a/m4/nut_stash_warnings.m4 b/m4/nut_stash_warnings.m4 new file mode 100644 index 0000000..069d6b0 --- /dev/null +++ b/m4/nut_stash_warnings.m4 @@ -0,0 +1,77 @@ +dnl Callers like CI or developers can enable various warning flags +dnl including those that would be fatal to the configure script +dnl itself passing (autotools probing code is rather sloppy by +dnl strict standards). These routines try to stash away the warning +dnl flags from CFLAGS and CXXFLAGS passed by user, to re-apply in +dnl the end of configure script run. + +AC_DEFUN([NUT_STASH_WARNINGS], +[ + dnl WARNING: This code assumes that there are no whitespaces + dnl inside C*FLAGS values (e.g. no spacey include paths) + CFLAGS_STASHED_WARNINGS="" + CPPFLAGS_STASHED_WARNINGS="" + CXXFLAGS_STASHED_WARNINGS="" + + AS_IF([test -z "$CFLAGS"],[],[ + TMP="" + for V in ${CFLAGS} ; do + case "$V" in + -W*|-*pedantic*) CFLAGS_STASHED_WARNINGS="${CFLAGS_STASHED_WARNINGS} ${V}" ;; + *) TMP="${TMP} ${V}" ;; + esac + done + CFLAGS="$TMP" + ]) + AS_IF([test -n "${CFLAGS_STASHED_WARNINGS}"], + [AC_MSG_NOTICE([Stashed CFLAGS warnings to not confuse autotools probes: ${CFLAGS_STASHED_WARNINGS}])]) + + AS_IF([test -z "$CPPFLAGS"],[],[ + TMP="" + for V in ${CPPFLAGS} ; do + case "$V" in + -W*|-*pedantic*) CPPFLAGS_STASHED_WARNINGS="${CPPFLAGS_STASHED_WARNINGS} ${V}" ;; + *) TMP="${TMP} ${V}" ;; + esac + done + CPPFLAGS="$TMP" + ]) + AS_IF([test -n "${CPPFLAGS_STASHED_WARNINGS}"], + [AC_MSG_NOTICE([Stashed CPPFLAGS warnings to not confuse autotools probes: ${CPPFLAGS_STASHED_WARNINGS}])]) + + + AS_IF([test -z "$CXXFLAGS"],[],[ + TMP="" + for V in ${CXXFLAGS} ; do + case "$V" in + -W*|-*pedantic*) CXXFLAGS_STASHED_WARNINGS="${CXXFLAGS_STASHED_WARNINGS} ${V}" ;; + *) TMP="${TMP} ${V}" ;; + esac + done + CXXFLAGS="$TMP" + ]) + AS_IF([test -n "${CXXFLAGS_STASHED_WARNINGS}"], + [AC_MSG_NOTICE([Stashed CXXFLAGS warnings to not confuse autotools probes: ${CXXFLAGS_STASHED_WARNINGS}])]) + +]) + +AC_DEFUN([NUT_POP_WARNINGS], +[ + AS_IF([test -n "${CFLAGS_STASHED_WARNINGS}"],[ + AC_MSG_NOTICE([Applying back the stashed CFLAGS warnings]) + CFLAGS="${CFLAGS} ${CFLAGS_STASHED_WARNINGS}" + AC_MSG_NOTICE([Ended up with: '${CFLAGS}']) + ]) + + AS_IF([test -n "${CPPFLAGS_STASHED_WARNINGS}"],[ + AC_MSG_NOTICE([Applying back the stashed CPPFLAGS warnings]) + CPPFLAGS="${CPPFLAGS} ${CPPFLAGS_STASHED_WARNINGS}" + AC_MSG_NOTICE([Ended up with: '${CPPFLAGS}']) + ]) + + AS_IF([test -n "${CXXFLAGS_STASHED_WARNINGS}"],[ + AC_MSG_NOTICE([Applying back the stashed CXXFLAGS warnings]) + CXXFLAGS="${CXXFLAGS} ${CXXFLAGS_STASHED_WARNINGS}" + AC_MSG_NOTICE([Ended up with: '${CXXFLAGS}']) + ]) +]) diff --git a/m4/nut_type_socklen_t.m4 b/m4/nut_type_socklen_t.m4 index 0cbf4b3..205a5f9 100644 --- a/m4/nut_type_socklen_t.m4 +++ b/m4/nut_type_socklen_t.m4 @@ -7,29 +7,65 @@ dnl This code gets around. This instance came from rsync 2.5.6. AC_DEFUN([NUT_TYPE_SOCKLEN_T], [ + AC_REQUIRE([NUT_CHECK_HEADER_WS2TCPIP])dnl + HEADERS_SOCKLEN_T=' +#undef inline +#ifdef HAVE_WINDOWS_H +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# if (!defined(_WIN32_WINNT)) || (_WIN32_WINNT < 0x0501) +# undef _WIN32_WINNT +# define _WIN32_WINNT 0x0501 +# endif +# include +# ifdef HAVE_WINSOCK2_H +# include +# ifdef HAVE_WS2TCPIP_H +# include +# endif +# endif +# define GNICALLCONV WSAAPI +# define GNICALLLINK WINSOCK_API_LINKAGE +#else +# ifdef HAVE_SYS_TYPES_H +# include +# endif +# ifdef HAVE_SYS_SOCKET_H +# include +# endif +# ifdef HAVE_NETDB_H +# include +# endif +# define GNICALLCONV +# define GNICALLLINK +#endif +' AC_CHECK_TYPE([socklen_t], ,[ AC_MSG_CHECKING([for socklen_t equivalent]) AC_CACHE_VAL([nut_cv_socklen_t_equiv], [ # Systems have either "struct sockaddr *" or # "void *" as the second argument to getpeername + AC_LANG_PUSH([C]) nut_cv_socklen_t_equiv= - for arg2 in "struct sockaddr" void; do - for t in int size_t unsigned long "unsigned long"; do - AC_TRY_COMPILE([ -#include -#include - - int getpeername (int, $arg2 *, $t *); - ],[ - $t len; + for arg1 in "int" "SOCKET"; do + for arg2 in "struct sockaddr" void; do + for arg3 in int size_t unsigned long "unsigned long"; do + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([${HEADERS_SOCKLEN_T} + GNICALLLINK int GNICALLCONV getpeername ($arg1, $arg2 *, $arg3 *); + ],[ + $arg3 len; getpeername(0,0,&len); - ],[ - nut_cv_socklen_t_equiv="$t" + ])], + [ + nut_cv_socklen_t_equiv="$arg3" break ]) done + done done + AC_LANG_POP([C]) if test "x$nut_cv_socklen_t_equiv" = x; then AC_MSG_ERROR([Cannot find a type to use in place of socklen_t]) @@ -38,6 +74,5 @@ AC_DEFUN([NUT_TYPE_SOCKLEN_T], AC_MSG_RESULT($nut_cv_socklen_t_equiv) AC_DEFINE_UNQUOTED(socklen_t, $nut_cv_socklen_t_equiv, [type to use in place of socklen_t if not defined])], - [#include -#include ]) + [${HEADERS_SOCKLEN_T}]) ]) diff --git a/missing b/missing index 28055d2..8d0eaad 100755 --- a/missing +++ b/missing @@ -1,11 +1,10 @@ #! /bin/sh -# Common stub for a few missing GNU programs while installing. +# Common wrapper for a few potentially missing GNU programs. -scriptversion=2009-04-28.21; # UTC +scriptversion=2018-03-07.03; # UTC -# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006, -# 2008, 2009 Free Software Foundation, Inc. -# Originally by Fran,cois Pinard , 1996. +# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard , 1996. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,7 +17,7 @@ scriptversion=2009-04-28.21; # UTC # GNU General Public License for more details. # You should have received a copy of the GNU General Public License -# along with this program. If not, see . +# along with this program. If not, see . # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a @@ -26,69 +25,40 @@ scriptversion=2009-04-28.21; # UTC # the same distribution terms that you use for the rest of that program. if test $# -eq 0; then - echo 1>&2 "Try \`$0 --help' for more information" + echo 1>&2 "Try '$0 --help' for more information" exit 1 fi -run=: -sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' -sed_minuso='s/.* -o \([^ ]*\).*/\1/p' - -# In the cases where this matters, `missing' is being run in the -# srcdir already. -if test -f configure.ac; then - configure_ac=configure.ac -else - configure_ac=configure.in -fi - -msg="missing on your system" - case $1 in ---run) - # Try to run requested program, and just exit if it succeeds. - run= - shift - "$@" && exit 0 - # Exit code 63 means version mismatch. This often happens - # when the user try to use an ancient version of a tool on - # a file that requires a minimum version. In this case we - # we should proceed has if the program had been absent, or - # if --run hadn't been passed. - if test $? = 63; then - run=: - msg="probably too old" - fi - ;; + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; -h|--h|--he|--hel|--help) echo "\ $0 [OPTION]... PROGRAM [ARGUMENT]... -Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an -error status if there is no known handling for PROGRAM. +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. Options: -h, --help display this help and exit -v, --version output version information and exit - --run try to run the given command, and emulate it if it fails Supported PROGRAM values: - aclocal touch file \`aclocal.m4' - autoconf touch file \`configure' - autoheader touch file \`config.h.in' - autom4te touch the output file, or create a stub one - automake touch all \`Makefile.in' files - bison create \`y.tab.[ch]', if possible, from existing .[ch] - flex create \`lex.yy.c', if possible, from existing .c - help2man touch the output file - lex create \`lex.yy.c', if possible, from existing .c - makeinfo touch the output file - tar try tar, gnutar, gtar, then tar without non-portable flags - yacc create \`y.tab.[ch]', if possible, from existing .[ch] + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man -Version suffixes to PROGRAM as well as the prefixes \`gnu-', \`gnu', and -\`g' are ignored when checking the name. +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. Send bug reports to ." exit $? @@ -100,277 +70,146 @@ Send bug reports to ." ;; -*) - echo 1>&2 "$0: Unknown \`$1' option" - echo 1>&2 "Try \`$0 --help' for more information" + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" exit 1 ;; esac -# normalize program name to check for. -program=`echo "$1" | sed ' - s/^gnu-//; t - s/^gnu//; t - s/^g//; t'` +# Run the given program, remember its exit status. +"$@"; st=$? -# Now exit if we have it, but it failed. Also exit now if we -# don't have it and --version was passed (most likely to detect -# the program). This is about non-GNU programs, so use $1 not -# $program. -case $1 in - lex*|yacc*) - # Not GNU programs, they don't have --version. +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=https://www.perl.org/ +flex_URL=https://github.com/westes/flex +gnu_software_URL=https://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'autom4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} - tar*) - if test -n "$run"; then - echo 1>&2 "ERROR: \`tar' requires --run" - exit 1 - elif test "x$2" = "x--version" || test "x$2" = "x--help"; then - exit 1 - fi - ;; +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 - *) - if test -z "$run" && ($1 --version) > /dev/null 2>&1; then - # We have it, but it failed. - exit 1 - elif test "x$2" = "x--version" || test "x$2" = "x--help"; then - # Could not run --version or --help. This is probably someone - # running `$TOOL --version' or `$TOOL --help' to check whether - # $TOOL exists and not knowing $TOOL uses missing. - exit 1 - fi - ;; -esac - -# If it does not exist, or fails to run (possibly an outdated version), -# try to emulate it. -case $program in - aclocal*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`acinclude.m4' or \`${configure_ac}'. You might want - to install the \`Automake' and \`Perl' packages. Grab them from - any GNU archive site." - touch aclocal.m4 - ;; - - autoconf*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`${configure_ac}'. You might want to install the - \`Autoconf' and \`GNU m4' packages. Grab them from any GNU - archive site." - touch configure - ;; - - autoheader*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`acconfig.h' or \`${configure_ac}'. You might want - to install the \`Autoconf' and \`GNU m4' packages. Grab them - from any GNU archive site." - files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` - test -z "$files" && files="config.h" - touch_files= - for f in $files; do - case $f in - *:*) touch_files="$touch_files "`echo "$f" | - sed -e 's/^[^:]*://' -e 's/:.*//'`;; - *) touch_files="$touch_files $f.in";; - esac - done - touch $touch_files - ;; - - automake*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. - You might want to install the \`Automake' and \`Perl' packages. - Grab them from any GNU archive site." - find . -type f -name Makefile.am -print | - sed 's/\.am$/.in/' | - while read f; do touch "$f"; done - ;; - - autom4te*) - echo 1>&2 "\ -WARNING: \`$1' is needed, but is $msg. - You might have modified some files without having the - proper tools for further handling them. - You can get \`$1' as part of \`Autoconf' from any GNU - archive site." - - file=`echo "$*" | sed -n "$sed_output"` - test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` - if test -f "$file"; then - touch $file - else - test -z "$file" || exec >$file - echo "#! /bin/sh" - echo "# Created by GNU Automake missing as a replacement of" - echo "# $ $@" - echo "exit 0" - chmod +x $file - exit 1 - fi - ;; - - bison*|yacc*) - echo 1>&2 "\ -WARNING: \`$1' $msg. You should only need it if - you modified a \`.y' file. You may need the \`Bison' package - in order for those modifications to take effect. You can get - \`Bison' from any GNU archive site." - rm -f y.tab.c y.tab.h - if test $# -ne 1; then - eval LASTARG="\${$#}" - case $LASTARG in - *.y) - SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` - if test -f "$SRCFILE"; then - cp "$SRCFILE" y.tab.c - fi - SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` - if test -f "$SRCFILE"; then - cp "$SRCFILE" y.tab.h - fi - ;; - esac - fi - if test ! -f y.tab.h; then - echo >y.tab.h - fi - if test ! -f y.tab.c; then - echo 'main() { return 0; }' >y.tab.c - fi - ;; - - lex*|flex*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified a \`.l' file. You may need the \`Flex' package - in order for those modifications to take effect. You can get - \`Flex' from any GNU archive site." - rm -f lex.yy.c - if test $# -ne 1; then - eval LASTARG="\${$#}" - case $LASTARG in - *.l) - SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` - if test -f "$SRCFILE"; then - cp "$SRCFILE" lex.yy.c - fi - ;; - esac - fi - if test ! -f lex.yy.c; then - echo 'main() { return 0; }' >lex.yy.c - fi - ;; - - help2man*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified a dependency of a manual page. You may need the - \`Help2man' package in order for those modifications to take - effect. You can get \`Help2man' from any GNU archive site." - - file=`echo "$*" | sed -n "$sed_output"` - test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` - if test -f "$file"; then - touch $file - else - test -z "$file" || exec >$file - echo ".ab help2man is required to generate this page" - exit $? - fi - ;; - - makeinfo*) - echo 1>&2 "\ -WARNING: \`$1' is $msg. You should only need it if - you modified a \`.texi' or \`.texinfo' file, or any other file - indirectly affecting the aspect of the manual. The spurious - call might also be the consequence of using a buggy \`make' (AIX, - DU, IRIX). You might want to install the \`Texinfo' package or - the \`GNU make' package. Grab either from any GNU archive site." - # The file to touch is that specified with -o ... - file=`echo "$*" | sed -n "$sed_output"` - test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` - if test -z "$file"; then - # ... or it is the one specified with @setfilename ... - infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` - file=`sed -n ' - /^@setfilename/{ - s/.* \([^ ]*\) *$/\1/ - p - q - }' $infile` - # ... or it is derived from the source name (dir/f.texi becomes f.info) - test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info - fi - # If the file does not exist, the user really needs makeinfo; - # let's fail without touching anything. - test -f $file || exit 1 - touch $file - ;; - - tar*) - shift - - # We have already tried tar in the generic part. - # Look for gnutar/gtar before invocation to avoid ugly error - # messages. - if (gnutar --version > /dev/null 2>&1); then - gnutar "$@" && exit 0 - fi - if (gtar --version > /dev/null 2>&1); then - gtar "$@" && exit 0 - fi - firstarg="$1" - if shift; then - case $firstarg in - *o*) - firstarg=`echo "$firstarg" | sed s/o//` - tar "$firstarg" "$@" && exit 0 - ;; - esac - case $firstarg in - *h*) - firstarg=`echo "$firstarg" | sed s/h//` - tar "$firstarg" "$@" && exit 0 - ;; - esac - fi - - echo 1>&2 "\ -WARNING: I can't seem to be able to run \`tar' with the given arguments. - You may want to install GNU tar or Free paxutils, or check the - command line arguments." - exit 1 - ;; - - *) - echo 1>&2 "\ -WARNING: \`$1' is needed, and is $msg. - You might have modified some files without having the - proper tools for further handling them. Check the \`README' file, - it often tells you about the needed prerequisites for installing - this package. You may also peek at any GNU archive site, in case - some other package would contain this missing \`$1' program." - exit 1 - ;; -esac - -exit 0 +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st # Local variables: -# eval: (add-hook 'write-file-hooks 'time-stamp) +# eval: (add-hook 'before-save-hook 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" -# time-stamp-time-zone: "UTC" +# time-stamp-time-zone: "UTC0" # time-stamp-end: "; # UTC" # End: diff --git a/scripts/Aix/nut-aix.spec.in b/scripts/Aix/nut-aix.spec.in new file mode 100644 index 0000000..37e4934 --- /dev/null +++ b/scripts/Aix/nut-aix.spec.in @@ -0,0 +1,330 @@ +%define nut_id @RUN_AS_USER@ +%define nut_group @RUN_AS_GROUP@ + +%define _prefix /usr/local/ups +%define _docdir %{_datadir}/doc + +%define confdir %{_prefix}/etc +%define rcdir /etc/rc.d +%define initdir %{rcdir}/init.d +%define cgidir /var/www/nut-cgi-bin +%define piddir /var/run/nut + +Summary: Network UPS Tools +Name: nut +Version: @PACKAGE_VERSION@ +Release: 1 +Group: Applications/System +License: GPLv2+ +Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) +Url: http://www.networkupstools.org/ +Source: http://www.networkupstools.org/source/@TREE_VERSION@/%{name}-%{version}.tar.gz +Source1: nut.init +#Source2: ups.sysconfig +#Source3: nut-client.tmpfiles + + +# FIXME: adjust according to what is available through RPM on Aix +BuildRequires: libtool +BuildRequires: net-snmp-devel +BuildRequires: openssl-devel +BuildRequires: pkgconfig + +# AIX BUILDERS, PLEASE NOTE: +# If building with xlc version 3.6.X rather than gcc, you must ensure +# you have the following PTF's installed on your system, or +# you will see a runtime error that says: +# "Expected but saw " +# PTFS needed: U462006 U462007 U462023 U462024 U462025 U462026 U462027 +# Refer to http://service.software.ibm.com/support/rs6000, or +# set CC=gcc to force use of the GCC compiler. +# +# %define stdlib lib +# %define liblink ../.. +# %define DEFCC xlc + +%description +Network UPS Tools (NUT) is a client/server monitoring system that allows +computers to share uninterruptible power supply (UPS) and power distribution +unit (PDU) hardware. Clients access the hardware through the server, and are +notified whenever the power status changes. + +%package client +Group: Applications/System +Summary: Network UPS Tools client monitoring utilities +#Requires(post): chkconfig +#Requires(preun): chkconfig +#Requires(pre): shadow-utils + +%description client +This package includes the client utilities that are required to monitor a +ups that the client host has access to, but where the UPS is physically +attached to a different computer on the network. + +%package devel +Group: Development/Libraries +Summary: Development files for NUT Client +Requires: %{name}-client = %{version}-%{release} webserver openssl-devel + +%description devel +This package contains the development header files and libraries +necessary to develop NUT client applications. + +%prep +%setup -q + +%build +/usr/bin/rm configure.in + +%configure \ + --with-all \ + --without-powerman \ + --without-avahi \ + --without-usb \ + --without-ipmi \ + --without-cgi \ + --datadir=%{_datadir}/%{name} \ + --with-user=%{nut_id} \ + --with-group=%{nut_group} \ + --with-statepath=%{piddir} \ + --with-pidpath=%{piddir} \ + --with-altpidpath=%{piddir} \ + --sysconfdir=%{confdir} \ + --with-cgipath=%{cgidir} \ + --with-drvpath=%{_sbindir} \ + --with-pkgconfig-dir=%{_libdir}/pkgconfig \ + --disable-static \ + --libdir=%{_libdir} \ + --program-transform-name=s,^%{_target_platform}-,, \ + LDFLAGS="$LDFLAGS -Wl,-brtl" \ +# --with-libltdl-includes=/opt/freeware/share/libtool/libltdl/libltdl/ \ +# --with-libltdl-libs=/opt/freeware/lib \ +# --with-doc \ asciidoc >= 8.6.3 is required + +# FIXME: remove rpath? +#sed -i 's|^hardcode_libdir_flag_spec=.*|hardcode_libdir_flag_spec=""|g' libtool +#sed -i 's|^runpath_var=LD_RUN_PATH|runpath_var=DIE_RPATH_DIE|g' libtool + +make %{?_smp_mflags} + +%install +/usr/bin/rm -rf %{buildroot} + +/usr/bin/mkdir -p %{buildroot}%{_sbindir} \ + %{buildroot}%{piddir} \ + %{buildroot}%{_libdir}/ups \ + %{buildroot}%{initdir} \ + %{buildroot}%{_libexecdir} + +make install DESTDIR=%{buildroot} + +install -m 755 %{SOURCE1} %{buildroot}%{initdir}/ups + +/usr/bin/rm -f %{buildroot}%{_libdir}/*.la + +# Remove ".sample" suffix from the config filenames +#pushd conf; +#make install DESTDIR=%{buildroot} +#for file in %{buildroot}%{confdir}/*.sample +#do +# mv $file %{buildroot}%{confdir}/`basename $file .sample` +#done +#popd + +%pre +/usr/bin/test -L %{_libdir}/ups || \ +/usr/bin/mkdir -p %{_libdir}/ups +/usr/bin/grep -qc %{nut_group} /etc/group || \ +/usr/bin/mkgroup %{nut_group} +/usr/bin/grep -qc %{nut_id} /etc/passwd || \ +/usr/sbin/useradd -c "Network UPS Tools" \ + -g %{nut_group} -d %{_libdir}/ups %{nut_id} +/usr/bin/test -L %{piddir} || \ +/usr/bin/mkdir -p %{piddir} +/usr/bin/chmod 750 %{piddir} +/usr/bin/chown %{nut_id}:%{nut_group} %{piddir} + +%post +/usr/bin/test -L %{rcdir}/rc2.d/Sups || \ +/usr/bin/ln -s %{initdir}/ups %{rcdir}/rc2.d/Sups +/usr/bin/test -L %{rcdir}/rc2.d/Kups || \ +/usr/bin/ln -s %{initdir}/ups %{rcdir}/rc2.d/Kups +exit 0 + +%preun +%{initdir}/ups stop +if [ "$1" = "0" ]; then + /usr/bin/rm -f %{rcdir}/rc2.d/[SK]ups +fi +exit 0 + +%postun +if [ "$1" = "0" ]; then + /usr/bin/grep -qc %{nut_id} /etc/passwd && \ + /usr/sbin/userdel %{nut_id} + /usr/bin/grep -qc %{nut_group}: /etc/group && \ + /usr/sbin/rmgroup %{nut_group} + /usr/bin/test -L %{piddir} && \ + /usr/bin/rm -rf %{piddir} + /usr/bin/test -L %{_libdir}/ups && \ + /usr/bin/rm -rf %{_libdir}/ups +fi +exit 0 + +%pre client +/usr/bin/grep -qc %{nut_group}: /etc/group || \ +/usr/bin/mkgroup %{nut_group} +/usr/bin/grep -qc %{nut_id} /etc/passwd || \ +/usr/sbin/useradd -c "Network UPS Tools" \ + -g %{nut_group} -d %{_libdir}/ups %{nut_id} +/usr/bin/test -L %{piddir} || \ +/usr/bin/mkdir -p %{piddir} +/usr/bin/chmod 750 %{piddir} +/usr/bin/chown %{nut_id}:%{nut_group} %{piddir} + +%post client +/usr/bin/test -L %{rcdir}/rc2.d/Sups || \ +/usr/bin/ln -s %{initdir}/ups %{rcdir}/rc2.d/Sups +/usr/bin/test -L %{rcdir}/rc2.d/Kups || \ +/usr/bin/ln -s %{initdir}/ups %{rcdir}/rc2.d/Kups +#%{initdir}/ups start +exit 0 + +%preun client +%{initdir}/ups stop +remove="no" +if /usr/bin/rpm -q nut >/dev/null 2>&1; then + remove="no" +elif [ "$1" = "0" ]; then + remove="yes" +fi +if [ "$remove" = "yes" ]; then + /usr/bin/rm -f %{rcdir}/rc2.d/[SK]ups + /usr/bin/test -L %{piddir} && \ + /usr/bin/rm -rf %{piddir} +fi +exit 0 + +%postun client +remove="no" +if /usr/bin/rpm -q nut >/dev/null 2>&1; then + remove="no" +elif [ "$1" = "0" ]; then + remove="yes" +fi +if [ "$remove" = "yes" ]; then + /usr/bin/grep -qc %{nut_id} /etc/passwd && \ + /usr/sbin/userdel %{nut_id} + /usr/bin/grep -qc %{nut_group}: /etc/group && \ + /usr/sbin/rmgroup %{nut_group} +#else +# %{initdir}/ups start +fi +exit 0 + +%clean +/usr/bin/rm -rf %{buildroot} + +%files +%defattr(-,root,root,-) +%attr(755,root,root) %{initdir}/ups +%doc COPYING ChangeLog AUTHORS MAINTAINERS README docs UPGRADING INSTALL NEWS +%config(noreplace) %attr(640,root,%nut_group) %{confdir}/nut.conf.sample +%config(noreplace) %attr(640,root,%nut_group) %{confdir}/ups.conf.sample +%config(noreplace) %attr(640,root,%nut_group) %{confdir}/upsd.conf.sample +%config(noreplace) %attr(640,root,%nut_group) %{confdir}/upsd.users.sample +%dir %attr(750,%nut_id,%nut_group) %{_libdir}/ups +#%ghost %{piddir} +%{_sbindir}/* +%{_bindir}/upslog +%{_bindir}/nutconf +%{_libdir}/libnutscan.so* +%{_libdir}/libupsclient.so* +%{_datadir}/%{name}/cmdvartab +%{_datadir}/%{name}/driver.list +%{_mandir}/man5/nut.conf.5 +%{_mandir}/man5/ups.conf.5 +%{_mandir}/man5/upsd.conf.5 +%{_mandir}/man5/upsd.users.5 +%{_mandir}/man8/apcsmart.8 +%{_mandir}/man8/bcmxcp.8 +#%{_mandir}/man8/bcmxcp_usb.8 +%{_mandir}/man8/belkin.8 +%{_mandir}/man8/bestfcom.8 +%{_mandir}/man8/belkinunv.8 +%{_mandir}/man8/bestfortress.8 +%{_mandir}/man8/bestups.8 +%{_mandir}/man8/bestuferrups.8 +%{_mandir}/man8/blazer.8 +%{_mandir}/man8/clone.8 +%{_mandir}/man8/dummy-ups.8 +%{_mandir}/man8/everups.8 +%{_mandir}/man8/etapro.8 +%{_mandir}/man8/gamatronic.8 +%{_mandir}/man8/genericups.8 +%{_mandir}/man8/isbmex.8 +%{_mandir}/man8/ivtscd.8 +%{_mandir}/man8/liebert.8 +%{_mandir}/man8/liebert-esp2.8 +%{_mandir}/man8/masterguard.8 +%{_mandir}/man8/metasys.8 +%{_mandir}/man8/microdowell.8 +%{_mandir}/man8/mge-utalk.8 +%{_mandir}/man8/mge-shut.8 +%{_mandir}/man8/nutupsdrv.8 +%{_mandir}/man8/oneac.8 +%{_mandir}/man8/optiups.8 +%{_mandir}/man8/powercom.8 +#%{_mandir}/man8/powerman-pdu.8 +%{_mandir}/man8/powerpanel.8 +%{_mandir}/man8/rhino.8 +#%{_mandir}/man8/richcomm_usb.8 +%{_mandir}/man8/safenet.8 +%{_mandir}/man8/snmp-ups.8 +%{_mandir}/man8/solis.8 +%{_mandir}/man8/tripplite.8 +#%{_mandir}/man8/tripplite_usb.8 +%{_mandir}/man8/tripplitesu.8 +%{_mandir}/man8/victronups.8 +%{_mandir}/man8/upscode2.8 +%{_mandir}/man8/upsd.8 +%{_mandir}/man8/upsdrvctl.8 + +%files client +%doc COPYING +%defattr(-,root,root) +%attr(755,root,root) %{initdir}/ups +%dir %{confdir} +%config(noreplace) %attr(640,root,%nut_group) %{confdir}/upsmon.conf.sample +%config(noreplace) %attr(640,root,%nut_group) %{confdir}/upssched.conf.sample +%dir %attr(750,%nut_id,%nut_group) %{_libdir}/ups +#%ghost %{piddir} +%{_bindir}/upsc +%{_bindir}/upscmd +%{_bindir}/upsrw +%{_sbindir}/upsmon +%{_sbindir}/upssched +%{_bindir}/upssched-cmd +%{_libdir}/libupsclient.so* +%{_mandir}/man5/upsmon.conf.5 +%{_mandir}/man5/upssched.conf.5 +%{_mandir}/man8/upsc.8 +%{_mandir}/man8/upscmd.8 +%{_mandir}/man8/upsrw.8 +%{_mandir}/man8/upslog.8 +%{_mandir}/man8/upsmon.8 +%{_mandir}/man8/upssched.8 + +%files devel +%defattr(-,root,root,-) +%{_includedir}/* +%{_mandir}/man3/upscli* +%{_libdir}/libupsclient.so* +%{_libdir}/pkgconfig/libupsclient.pc + +%changelog +* Tue Jul 12 2014 Arnaud Quette - 2.7.2-1.master +- Minor adjustments + +* Tue Jul 12 2011 Arnaud Quette - 2.6.5-1.trunk +- derive from RHEL 2.6.1-2, and adapt for Aix 6.1 diff --git a/scripts/Aix/nut.init.in b/scripts/Aix/nut.init.in new file mode 100755 index 0000000..018777e --- /dev/null +++ b/scripts/Aix/nut.init.in @@ -0,0 +1,171 @@ +#! /bin/sh +# +# ups: Starts the Network UPS Tools +# +# chkconfig: - 26 74 +# description: Network UPS Tools is a collection of programs which provide a common \ +# interface for monitoring and administering UPS hardware. +# processname: upsd +# config: /usr/local/ups/etc +# config: /etc/rc.ups +# +### BEGIN INIT INFO +# Provides: ups +# Required-Start: $syslog $network $named +# Required-Stop: $local_fs +# Default-Stop: 0 1 6 +# Short-Description: Starts the Network UPS tools +# Description: Network UPS Tools is a collection of programs which provide a common \ +# interface for monitoring and administering UPS hardware. +### END INIT INFO + +success() { + echo OK +} + +failure() { + echo FAILED +} + +# Resolve what processes should run +SERVER="no" +CLIENT="no" + +NUT_DIR="@prefix@" +NUT_SBIN_DIR="${NUT_DIR}/sbin" +NUT_LIB_DIR="${NUT_DIR}/lib" +NUT_RUN_DIR="@PIDPATH@/nut" +CONFIG="@CONFPATH@/nut.conf" +NUTUSER="@RUN_AS_USER@" +NUTGROUP="@RUN_AS_GROUP@" +NUT_VAR_LOCK="/var/locks/ups" + +if [ -f "$CONFIG" ] ; then + . "$CONFIG" + + case "$MODE" in + standalone|netserver) + SERVER="yes" + ;; + esac + + rpm -q nut-client >/dev/null 2>&1 && CLIENT="yes" +fi + +do_start() { + RETVAL=0 + + if [ ! -d "$NUT_RUN_DIR" ]; then + mkdir -p "$NUT_RUN_DIR" && \ + chown "root:$NUTGROUP" "$NUT_RUN_DIR" && \ + chmod 770 "$NUT_RUN_DIR" + RETVAL=$? + fi + + if [ "$SERVER" = "yes" ]; then + echo "Starting UPS driver controller: \c" + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}"/upsdrvctl start >/dev/null 2>&1 && success || { RETVAL=$?; failure; } + + echo "Starting upsd: \c" + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}"/upsd $UPSD_OPTIONS >/dev/null 2>&1 && success || { RETVAL=$?; failure; } + fi + + if [ "$CLIENT" = "yes" ]; then + echo "Starting UPS monitor: \c" + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}"/upsmon >/dev/null 2>&1 && success || { RETVAL=$?; failure; } + fi + + [ "$RETVAL" = 0 ] && touch "${NUT_VAR_LOCK}" + return $RETVAL +} + +do_stop() { + RETVAL=0 + if test -e "${NUT_RUN_DIR}"/upsmon.pid; then + echo "Stopping UPS monitor: \c" + PID="`cat "${NUT_RUN_DIR}"/upsmon.pid`" + kill -15 $PID && success || { RETVAL=$?; failure; } + rm "${NUT_RUN_DIR}"/upsmon.pid + fi + + if [ "$SERVER" = "yes" ]; then + if test -e "${NUT_RUN_DIR}"/upsd.pid; then + echo "Stopping upsd: \c" + PID="`cat "${NUT_RUN_DIR}"/upsd.pid`" + kill -15 $PID && success || { RETVAL=$?; failure; } + rm "${NUT_RUN_DIR}"/upsd.pid + fi + + echo "Shutting down UPS driver controller: \c" + "${NUT_SBIN_DIR}"/upsdrvctl stop > /dev/null 2>&1 && success || { RETVAL=$?; failure; } + fi + [ "$RETVAL" = 0 ] && rm -f "${NUT_VAR_LOCK}" + return $RETVAL +} + +do_restart() { + do_stop + waitmore=5 + while [ -n "$(ls "${NUT_RUN_DIR}"/)" -a $waitmore -ge 1 ] + do + sleep 1 + waitmore="$(expr $waitmore - 1)" + done + do_start +} + +do_reload() { + # FIXME: upsd and upsmon always return 0 + # => can't tell if reload was successful + RETVAL=0 + if [ "$SERVER" = "yes" ]; then + echo "Reloading upsd" + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}"/upsd -c reload && success || { RETVAL=$?; failure; } + fi + + echo "Reloading upsmon" + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}"/upsmon -c reload && success || { RETVAL=$?; failure; } + return $RETVAL +} + +# See how we are called. +case "$1" in + start) + do_start ;; + + stop) + do_stop ;; + + restart) + do_restart ;; + + try-restart) + [ -f "${NUT_VAR_LOCK}" ] && do_restart || true + ;; + + reload) + do_reload ;; + + force-reload) + do_restart ;; + + status) + if [ "$SERVER" = "yes" ]; then + if test -f "${NUT_VAR_LOCK}"; then + echo "upsd is running with PID" `cat "${NUT_RUN_DIR}"/upsd.pid` + fi + fi + + if test -e "${NUT_RUN_DIR}"/upsmon.pid; then + echo "upsmon is running with PID" `cat "${NUT_RUN_DIR}"/upsmon.pid` + elif rpm -q nut-client >/dev/null 2>&1; then + echo "upsmon isn't running" + fi + ;; + + *) + echo "Usage: $0 {start|stop|restart|try-restart|reload|force-reload|status}" + RETVAL=3 +esac + +exit $RETVAL diff --git a/scripts/HP-UX/Makefile b/scripts/HP-UX/Makefile deleted file mode 100644 index b829ca9..0000000 --- a/scripts/HP-UX/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -# directory definitions -INSTALLSH = ../../install-sh -CONFIGPATH = /etc/rc.config.d -SCRIPTPATH = /sbin/init.d -LINKPATH = /sbin/rc3.d -LINKPREFIX = 991 -OWNER = root -GROUP = root -SCRIPTS = nut-upsd.sh nut-drvctl.sh nut-upsmon.sh -CONFIGS = nut-drvctl nut-upsd nut-upsmon - -INSTALLPERMS = 0755 -INSTALLCMD = $(INSTALLSH) -c - -all: install - - -install: - @if (test ! -d $(CONFIGPATH)) then \ - echo "NO $(CONFIGPATH)"; exit 1; \ - fi - @if (test ! -d $(SCRIPTPATH)) then \ - echo "NO $(SCRIPTPATH)"; exit 1; \ - fi - @if (test ! -d $(LINKPATH)) then \ - echo "NO $(LINKPATH)"; exit 1; \ - fi - @for script in $(SCRIPTS); do\ - name=`basename $$script .sh` ; \ - $(INSTALLSH) -c -o $(OWNER) -g $(GROUP) $$script $(SCRIPTPATH)/$$name || exit 1; \ - ln -s $(SCRIPTPATH)/$$name $(LINKPATH)/K$(LINKPREFIX)$$name \ - || echo "Unable to link $(SCRIPTPATH)/$$name to $(LINKPATH)/K$(LINKPREFIX)$$name"; \ - ln -s $(SCRIPTPATH)/$$name $(LINKPATH)/S$(LINKPREFIX)$$name \ - || echo "Unable to link $(SCRIPTPATH)/$$name to $(LINKPATH)/S$(LINKPREFIX)$$name"; \ - done - @for config in $(CONFIGS); do\ - $(INSTALLSH) -c -o $(OWNER) -g $(GROUP) $$config $(CONFIGPATH) || exit 1 ; \ - done - -remove: - @for config in $(CONFIGS); do\ - rm -f $(CONFIGPATH)/$$config;\ - done - @for script in $(SCRIPTS); do\ - name=`basename $$script .sh` ; \ - rm -f $(SCRIPTPATH)/$$name || exit 1; \ - rm -f $(LINKPATH)/K$(LINKPREFIX)$$name \ - || echo "Unable to remove $(LINKPATH)/K$(LINKPREFIX)$$name"; \ - rm -f $(LINKPATH)/S$(LINKPREFIX)$$name \ - || echo "Unable to remove $(LINKPATH)/S$(LINKPREFIX)$$name"; \ - done diff --git a/scripts/HP-UX/nut.psf.in b/scripts/HP-UX/nut.psf.in index 1e73a92..feb98a5 100644 --- a/scripts/HP-UX/nut.psf.in +++ b/scripts/HP-UX/nut.psf.in @@ -1,4 +1,5 @@ -# PSF file for Network UPS Tools /usr/local/ups 11/2/2011 +B/ +# PSF file for Network UPS Tools 11/2/2011 # # Useful commands: # @@ -50,219 +51,38 @@ product tag Server title "nut-server" revision @PACKAGE_VERSION@ + postinstall ./postinstall -#Including "conf" files under "/usr/local/ups/etc/nut". - file -u 644 -g bin -o bin ../../conf/ups.conf.sample /usr/local/ups/etc/ups.conf - file -u 644 -g bin -o bin ../../conf/upsd.conf.sample /usr/local/ups/etc/upsd.conf - file -u 644 -g bin -o bin ../../conf/upsd.users.sample /usr/local/ups/etc/upsd.users +#Including "conf" files under "/usr/local/ups/etc/". + file -u 644 -g bin -o bin ./nut_install@prefix@/etc/ups.conf.sample @prefix@/etc/ups.conf.sample + file -u 644 -g bin -o bin ./nut_install@prefix@/etc/upsd.conf.sample @prefix@/etc/upsd.conf.sample + file -u 644 -g bin -o bin ./nut_install@prefix@/etc/upsd.users.sample @prefix@/etc/upsd.users.sample #Including "server" files under "/usr/local/ups/sbin". - file -u 755 -g bin -o bin ../../server/upsd /usr/local/ups/sbin/upsd - file -u 755 -g bin -o bin ../../drivers/upsdrvctl /usr/local/ups/sbin/upsdrvctl + file -u 755 -g bin -o bin ./nut_install@prefix@/sbin/upsd @prefix@/sbin/upsd #Including "share" files under "/usr/local/ups/share". - file -u 644 -g bin -o bin ../../data/cmdvartab /usr/local/ups/share/cmdvartab - file -u 644 -g bin -o bin ../../data/driver.list /usr/local/ups/share/driver.list + file -u 644 -g bin -o bin ./nut_install@prefix@/share/cmdvartab @prefix@/share/cmdvartab + file -u 644 -g bin -o bin ./nut_install@prefix@/share/driver.list @prefix@/share/driver.list #Including required "libupsclient1" under "/usr/local/ups/lib" - file -u 755 -g bin -o bin ../../clients/.libs/libupsclient.so.1 /usr/local/ups/lib/libupsclient.so.1 - file -u 555 -g bin -o bin ../../clients/.libs/libupsclient.so.1.0 /usr/local/ups/lib/libupsclient.so.1.0 - + file -u 755 -g bin -o bin ./nut_install@prefix@/lib/libupsclient.sl.3 @prefix@/lib/libupsclient.sl.3 + file -u 555 -g bin -o bin ./nut_install@prefix@/lib/libupsclient.sl.3.1 @prefix@/lib/libupsclient.sl.3.1 + +#Including nut service script to "usr/local/ups/script" + file -u 744 -g bin -o bin @top_srcdir@/scripts/HP-UX/nut-upsd.sh @prefix@/script/nut-upsd.sh + file -u 744 -g bin -o bin @top_srcdir@/scripts/HP-UX/nut-drvctl.sh @prefix@/script/nut-drvctl.sh + file -u 744 -g bin -o bin @top_srcdir@/scripts/HP-UX/nut-upsmon.sh @prefix@/script/nut-upsmon.sh + file -u 444 -g bin -o bin @top_srcdir@/scripts/HP-UX/nut-upsd @prefix@/script/nut-upsd + file -u 444 -g bin -o bin @top_srcdir@/scripts/HP-UX/nut-drvctl @prefix@/script/nut-drvctl + file -u 444 -g bin -o bin @top_srcdir@/scripts/HP-UX/nut-upsmon @prefix@/script/nut-upsmon + #Including required UPS drivers files under "/usr/local/ups/bin/". #such as "nut-snmp", "nut-xml or netxml-ups" - directory ../../drivers=/usr/local/ups/bin/ + directory ./nut_install@prefix@/bin=@prefix@/bin/ #file_permissions -u 755 -g bin -o bin file * -# Exclude the files from "drivers" folder that are not required in the package. - exclude Makefile - exclude Makefile.am - exclude Makefile.in - exclude apc-hid.c - exclude apc-hid.h - exclude apc-mib.c - exclude apc-mib.h - exclude apcsmart.c - exclude apcsmart.h - exclude apcsmart.o - exclude baytech-mib.c - exclude baytech-mib.h - exclude bcmxcp.c - exclude bcmxcp.h - exclude bcmxcp.o - exclude bcmxcp_io.h - exclude bcmxcp_ser.c - exclude bcmxcp_ser.o - exclude bcmxcp_usb.c - exclude belkin-hid.c - exclude belkin-hid.h - exclude belkin.c - exclude belkin.h - exclude belkin.o - exclude belkinunv.c - exclude belkinunv.o - exclude bestfcom.c - exclude bestfcom.o - exclude bestfortress.c - exclude bestfortress.o - exclude bestpower-mib.c - exclude bestpower-mib.h - exclude bestuferrups.c - exclude bestuferrups.o - exclude bestups.c - exclude bestups.o - exclude blazer.c - exclude blazer.h - exclude blazer.o - exclude blazer_ser.c - exclude blazer_ser.o - exclude blazer_usb.c - exclude clone-outlet.c - exclude clone-outlet.o - exclude clone.c - exclude clone.o - exclude compaq-mib.c - exclude compaq-mib.h - exclude cps-hid.c - exclude cps-hid.h - exclude dstate-hal.c - exclude dstate-hal.h - exclude dstate.c - exclude dstate.h - exclude dstate.o - exclude dummy-ups.c - exclude dummy-ups.h - exclude dummy_ups-dummy-ups.o - exclude eaton-mib.c - exclude eaton-mib.h - exclude etapro.c - exclude etapro.o - exclude everups.c - exclude everups.o - exclude explore-hid.c - exclude explore-hid.h - exclude gamatronic.c - exclude gamatronic.h - exclude gamatronic.o - exclude genericups.c - exclude genericups.h - exclude genericups.o - exclude hidparser.c - exclude hidparser.h - exclude hidparser.o - exclude hidtypes.h - exclude ietf-mib.c - exclude ietf-mib.h - exclude isbmex.c - exclude isbmex.o - exclude ivtscd.c - exclude ivtscd.o - exclude libhid.c - exclude libhid.h - exclude libshut.c - exclude libshut.h - exclude libusb.c - exclude libusb.h - exclude liebert-esp2.c - exclude liebert-esp2.o - exclude liebert-hid.c - exclude liebert-hid.h - exclude liebert.c - exclude liebert.o - exclude main-hal.c - exclude main-hal.h - exclude main.c - exclude main.h - exclude main.o - exclude masterguard.c - exclude masterguard.o - exclude metasys.c - exclude metasys.o - exclude mge-hid.c - exclude mge-hid.h - exclude mge-mib.c - exclude mge-mib.h - exclude mge-shut.c - exclude mge-shut.h - exclude mge-shut.o - exclude mge-utalk.c - exclude mge-utalk.h - exclude mge-utalk.o - exclude mge-xml.c - exclude mge-xml.h - exclude microdowell.c - exclude microdowell.h - exclude microdowell.o - exclude netvision-mib.c - exclude netvision-mib.h - exclude netxml-ups.c - exclude netxml-ups.h - exclude newmge_shut-hidparser.o - exclude newmge_shut-libhid.o - exclude newmge_shut-libshut.o - exclude newmge_shut-mge-hid.o - exclude newmge_shut-usbhid-ups.o - exclude oneac.c - exclude oneac.h - exclude oneac.o - exclude optiups - exclude optiups.c - exclude optiups.o - exclude powercom-hid.c - exclude powercom-hid.h - exclude powercom.c - exclude powercom.h - exclude powercom.o - exclude powerman-pdu.c - exclude powerp-bin.c - exclude powerp-bin.h - exclude powerp-bin.o - exclude powerp-txt.c - exclude powerp-txt.h - exclude powerp-txt.o - exclude powerpanel.c - exclude powerpanel.h - exclude powerpanel.o - exclude powerware-mib.c - exclude powerware-mib.h - exclude raritan-pdu-mib.c - exclude raritan-pdu-mib.h - exclude rhino.c - exclude rhino.o - exclude richcomm_usb.c - exclude safenet.c - exclude safenet.h - exclude safenet.o - exclude serial.c - exclude serial.h - exclude serial.o - exclude skel.c - exclude skel.o - exclude snmp-ups.c - exclude snmp-ups.h - exclude solis.c - exclude solis.h - exclude solis.o - exclude tripplite-hid.c - exclude tripplite-hid.h - exclude tripplite.c - exclude tripplite.h - exclude tripplite.o - exclude tripplite_usb.c - exclude tripplitesu.c - exclude tripplitesu.o - exclude upscode2.c - exclude upscode2.o - exclude upsdrvctl.c - exclude upsdrvctl.o - exclude upshandler.h - exclude usb-common.c - exclude usb-common.h - exclude usbhid-ups.c - exclude usbhid-ups.h - exclude victronups.c - exclude victronups.o - #TBD files to be added under "/usr/share/doc/nut-server/*.gz" #TBD files to be added under "/usr/share/man/man5/*.gz" #TBD files to be added under "/usr/share/man/man8/*.gz" @@ -275,21 +95,21 @@ product title "nut-client" revision @PACKAGE_VERSION@ - file -u 755 -g bin -o bin ../../clients/upsc /usr/local/ups/bin/upsc - file -u 755 -g bin -o bin ../../clients/upscmd /usr/local/ups/bin/upscmd - file -u 755 -g bin -o bin ../../clients/upslog /usr/local/ups/bin/upslog - file -u 755 -g bin -o bin ../../clients/upsrw /usr/local/ups/bin/upsrw - file -u 755 -g bin -o bin ../../clients/upsmon /usr/local/ups/sbin/upsmon - file -u 755 -g bin -o bin ../../clients/upssched /usr/local/ups/sbin/upssched + file -u 755 -g bin -o bin ./nut_install@prefix@/bin/upsc @prefix@/bin/upsc + file -u 755 -g bin -o bin ./nut_install@prefix@/bin/upscmd @prefix@/bin/upscmd + file -u 755 -g bin -o bin ./nut_install@prefix@/bin/upslog @prefix@/bin/upslog + file -u 755 -g bin -o bin ./nut_install@prefix@/bin/upsrw @prefix@/bin/upsrw + file -u 755 -g bin -o bin ./nut_install@prefix@/sbin/upsmon @prefix@/sbin/upsmon + file -u 755 -g bin -o bin ./nut_install@prefix@/sbin/upssched @prefix@/sbin/upssched #Including "conf" files under "/usr/local/ups/etc". - file -u 644 -g bin -o bin ../../conf/nut.conf.sample /usr/local/ups/etc/nut.conf - file -u 644 -g bin -o bin ../../conf/upsmon.conf.sample /usr/local/ups/etc/upsmon.conf - file -u 644 -g bin -o bin ../../conf/upssched.conf.sample /usr/local/ups/etc/upssched.conf + file -u 644 -g bin -o bin ./nut_install@prefix@/etc/nut.conf.sample @prefix@/etc/nut.conf.sample + file -u 644 -g bin -o bin ./nut_install@prefix@/etc/upsmon.conf.sample @prefix@/etc/upsmon.conf.sample + file -u 644 -g bin -o bin ./nut_install@prefix@/etc/upssched.conf.sample @prefix@/etc/upssched.conf.sample #Need to check if "libupsclient1" required for Client again. - #file -u 755 -g bin -o bin ../../clients/.libs/libupsclient.so.1 /usr/local/ups/lib/libupsclient.so.1 - #file -u 555 -g bin -o bin ../../clients/.libs/libupsclient.so.1.0 /usr/local/ups/lib/libupsclient.so.1.0 + #file -u 755 -g bin -o bin ./nut_install@prefix@/lib/libupsclient.sl.3 @prefix@/lib/libupsclient.sl.3 + #file -u 555 -g bin -o bin ./nut_install@prefix@/lib/libupsclient.sl.3.1 @prefix@/lib/libupsclient.sl.3.1 end # ---------------------------------------- #Including "libupsclient1-dev" files. @@ -298,74 +118,24 @@ product title "libupsclient1-dev" revision @PACKAGE_VERSION@ - file -u 755 -g bin -o bin ../../clients/.libs/libupsclient.a /usr/local/ups/lib/libupsclient.a - file -u 755 -g bin -o bin ../../clients/.libs/libupsclient.la /usr/local/ups/lib/libupsclient.la - file -u 755 -g bin -o bin ../../clients/.libs/libupsclient.so /usr/local/ups/lib/libupsclient.so - file -u 644 -g bin -o bin ../../include/parseconf.h /usr/include/parseconf.h - file -u 644 -g bin -o bin ../../clients/upsclient.h /usr/include/upsclient.h - #file -u 755 -g bin -o bin ../../clients/.libs/libupsclient.so /usr/lib/libupsclient.so - file -u 755 -g bin -o bin ../../lib/libupsclient.pc /usr/lib/pkgconfig/libupsclient.pc + file -u 755 -g bin -o bin ./nut_install@prefix@/lib/libupsclient.a @prefix@/lib/libupsclient.a + file -u 755 -g bin -o bin ./nut_install@prefix@/lib/libupsclient.la @prefix@/lib/libupsclient.la + file -u 755 -g bin -o bin ./nut_install@prefix@/lib/libupsclient.sl @prefix@/lib/libupsclient.sl + file -u 644 -g bin -o bin @top_srcdir@/include/parseconf.h @prefix@/include/parseconf.h + file -u 644 -g bin -o bin @top_srcdir@/clients/upsclient.h @prefix@/include/upsclient.h + file -u 755 -g bin -o bin ./nut_install@prefix@/lib/pkgconfig/libupsclient.pc @prefix@/lib/pkgconfig/libupsclient.pc end # ---------------------------------------- -#Including "nut-cgi " files. - fileset - tag cgi - title "nut-cgi" - revision @PACKAGE_VERSION@ - - file -u 644 -g bin -o bin ../../conf/hosts.conf.sample /etc/nut/hosts.conf - file -u 644 -g bin -o bin ../../conf/upsset.conf.sample /etc/nut/upsset.conf - #file -u 644 -g bin -o bin ../../conf/upsstats-single.html.sample /etc/nut/upsstats-single.html - #file -u 644 -g bin -o bin ../../conf/upsstats.html.sample /etc/nut/upsstats.html - - #file -u 644 -g bin -o bin ../../data/html/bottom.html /usr/share/nut/www/bottom.html - #file -u 644 -g bin -o bin ../../data/html/header.html /usr/share/nut/www/header.html - #file -u 644 -g bin -o bin ../../data/html/index.html /usr/share/nut/www/index.html - #file -u 644 -g bin -o bin ../../data/html/nut-banner.png /usr/share/nut/www/nut-banner.png - end -# ---------------------------------------- - -#Including "nut-doc" files. - #TBD - #fileset - # tag doc - # title "nut-doc" - # revision @PACKAGE_VERSION@ - - #Need to check from the following location for the required files - # /usr/local/share/doc/libxml2-2.7.7/html/tutorial/ - #end -# ---------------------------------------- - #Including "libups-nut-perl" files. fileset - tag libups-nut-perl + tag libups-nut-perl title "libups-nut-perl" revision @PACKAGE_VERSION@ - file -u 644 -g bin -o bin ../../scripts/perl/Nut.pm /usr/share/perl5/UPS/Nut.pm + file -u 644 -g bin -o bin @top_srcdir@/scripts/perl/Nut.pm @prefix@/share/perl5/UPS/Nut.pm end # ---------------------------------------- -#Including "nut-monitor" files - fileset - tag nut-monitor - title "nut-monitor" - revision @PACKAGE_VERSION@ - - file -u 644 -g bin -o bin ../../scripts/python/app/NUT-Monitor /usr/bin/NUT-Monitor - file -u 644 -g bin -o bin ../../scripts/python/app/nut-monitor.desktop /usr/share/applications/nut-monitor.desktop - file -u 644 -g bin -o bin ../../scripts/python/app/locale/fr/LC_MESSAGES/NUT-Monitor.mo /usr/share/locale/fr/LC_MESSAGES/NUT-Monitor.mo - file -u 644 -g bin -o bin ../../scripts/python/app/gui-1.3.glade /usr/share/nut-monitor/gui-1.3.glade - file -u 644 -g bin -o bin ../../scripts/python/app/pixmaps/on_battery.png /usr/share/nut-monitor/pixmaps/on_battery.png - file -u 644 -g bin -o bin ../../scripts/python/app/pixmaps/on_line.png /usr/share/nut-monitor/pixmaps/on_line.png - file -u 644 -g bin -o bin ../../scripts/python/app/pixmaps/var-ro.png /usr/share/nut-monitor/pixmaps/var-ro.png - file -u 644 -g bin -o bin ../../scripts/python/app/pixmaps/var-rw.png /usr/share/nut-monitor/pixmaps/var-rw.png - file -u 644 -g bin -o bin ../../scripts/python/app/pixmaps/warning.png /usr/share/nut-monitor/pixmaps/warning.png - file -u 644 -g bin -o bin ../../scripts/python/app/nut-monitor.png /usr/share/pixmaps/nut-monitor.png - end - -# ---------------------------------------- end #End product diff --git a/scripts/HP-UX/postinstall.in b/scripts/HP-UX/postinstall.in new file mode 100644 index 0000000..8111b6a --- /dev/null +++ b/scripts/HP-UX/postinstall.in @@ -0,0 +1,81 @@ +#!/bin/sh + +# directory definitions +NUT_DIR="@prefix@" +INSTALLPATH=$NUT_DIR/script +CONFIGPATH=/etc/rc.config.d +SCRIPTPATH=/sbin/init.d +LINKPATH=/sbin/rc3.d +LINKPREFIX=991 +OWNER=root +GROUP=root +SCRIPTS="nut-upsd.sh nut-drvctl.sh nut-upsmon.sh" +CONFIGS="nut-drvctl nut-upsd nut-upsmon" + +SCRIPTPERMS=0744 +CONFIGPERMS=0444 + +# make sure the nut user exists and has correct memberships +res=`grget -n nut` +if [ -z "$res" ]; then + groupadd nut +fi +res=`pwget -n nut` +if [ -z "$res" ]; then + useradd -g nut -G root -d ${NUT_DIR}/bin nut +fi + +# make sure that conffiles are secured and have the correct ownerships +if [ -d @CONFPATH@ ] ; then + chown root:nut @CONFPATH@ +fi +for file in nut.conf ups.conf upsd.conf upsmon.conf upsd.users upssched.conf; do + if [ -f @CONFPATH@/$file ] ; then + chown root:nut @CONFPATH@/$file + chmod 640 @CONFPATH@/$file + fi +done + +# make sure that /var/run/nut exists and has the correct ownerships +if [ ! -d @PIDPATH@/nut ] ; then + mkdir -p @PIDPATH@/nut +fi +if [ -d @PIDPATH@/nut ] ; then + chown root:nut @PIDPATH@/nut + chmod 770 @PIDPATH@/nut +fi + +# make sure that /var/state/ups exists and has the correct ownerships +if [ ! -d @STATEPATH@ ] ; then + mkdir -p @STATEPATH@ +fi +if [ -d /var/state/ups ] ; then + chown root:nut @STATEPATH@ + chmod 770 @STATEPATH@ +fi + +#Set-up automatic start-up + +if [ ! -d $CONFIGPATH ]; then + echo "NO $CONFIGPATH"; exit 1; +fi +if [ ! -d $SCRIPTPATH ]; then + echo "NO $SCRIPTPATH"; exit 1; +fi +if [ ! -d $LINKPATH ]; then + echo "NO $LINKPATH"; exit 1; +fi +for script in $SCRIPTS; do + name=`basename ${script} .sh` ; + cp $INSTALLPATH/$script $SCRIPTPATH/$name + chown $OWNER:$GROUP $SCRIPTPATH/$name + chmod $SCRIPTPERMS $SCRIPTPATH/$name + + ln -f -s $SCRIPTPATH/$name $LINKPATH/K$LINKPREFIX$name + ln -f -s $SCRIPTPATH/$name $LINKPATH/S$LINKPREFIX$name +done +for config in $CONFIGS; do + cp $INSTALLPATH/$config $CONFIGPATH + chown $OWNER:$GROUP $CONFIGPATH + chmod $CONFIGPERMS $CONFIGPATH +done diff --git a/scripts/Makefile.am b/scripts/Makefile.am index 2e0ed23..ef6a1dd 100644 --- a/scripts/Makefile.am +++ b/scripts/Makefile.am @@ -1,26 +1,30 @@ EXTRA_DIST = README \ -avahi/nut.service.in \ -HP-UX/Makefile \ -HP-UX/nut-drvctl \ -HP-UX/nut-drvctl.sh \ -HP-UX/nut-upsd \ -HP-UX/nut-upsd.sh \ -HP-UX/nut-upsmon \ -HP-UX/nut-upsmon.sh \ -misc/nut.bash_completion \ -misc/osd-notify \ -perl/Nut.pm \ -RedHat/halt.patch \ -RedHat/README \ -RedHat/ups \ -RedHat/upsd \ -RedHat/upsd.in \ -RedHat/upsmon \ -RedHat/upsmon.in \ -Solaris8/S99upsmon \ -subdriver/path-to-subdriver.sh \ -upower/95-upower-hid.rules \ -Windows/halt.c \ -Windows/Makefile + avahi/nut.service.in \ + HP-UX/nut-drvctl \ + HP-UX/nut-drvctl.sh \ + HP-UX/nut-upsd \ + HP-UX/nut-upsd.sh \ + HP-UX/nut-upsmon \ + HP-UX/nut-upsmon.sh \ + logrotate/nutlogd \ + misc/nut.bash_completion \ + misc/osd-notify \ + perl/Nut.pm \ + RedHat/halt.patch \ + RedHat/README \ + RedHat/ups \ + RedHat/upsd \ + RedHat/upsd.in \ + RedHat/upsmon \ + RedHat/upsmon.in \ + Solaris8/S99upsmon \ + subdriver/gen-usbhid-subdriver.sh \ + subdriver/gen-snmp-subdriver.sh \ + ufw/README \ + upower/95-upower-hid.rules \ + Windows/halt.c \ + Windows/Makefile -SUBDIRS = augeas hal hotplug java python systemd udev +SUBDIRS = augeas devd hotplug python systemd udev ufw Solaris upsdrvsvcctl + +MAINTAINERCLEANFILES = Makefile.in .dirstamp diff --git a/scripts/Makefile.in b/scripts/Makefile.in index 35ef1f9..def2e6a 100644 --- a/scripts/Makefile.in +++ b/scripts/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -15,6 +14,61 @@ @SET_MAKE@ VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -35,53 +89,103 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = scripts -DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = SOURCES = DIST_SOURCES = -RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ - html-recursive info-recursive install-data-recursive \ - install-dvi-recursive install-exec-recursive \ - install-html-recursive install-info-recursive \ - install-pdf-recursive install-ps-recursive install-recursive \ - installcheck-recursive installdirs-recursive pdf-recursive \ - ps-recursive uninstall-recursive +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive -AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ - $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ - distdir +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ @@ -111,8 +215,11 @@ am__relativize = \ A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -123,16 +230,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -141,11 +257,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -155,14 +268,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -173,21 +287,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -201,35 +324,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -240,8 +374,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -260,18 +398,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -282,31 +423,34 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ EXTRA_DIST = README \ -avahi/nut.service.in \ -HP-UX/Makefile \ -HP-UX/nut-drvctl \ -HP-UX/nut-drvctl.sh \ -HP-UX/nut-upsd \ -HP-UX/nut-upsd.sh \ -HP-UX/nut-upsmon \ -HP-UX/nut-upsmon.sh \ -misc/nut.bash_completion \ -misc/osd-notify \ -perl/Nut.pm \ -RedHat/halt.patch \ -RedHat/README \ -RedHat/ups \ -RedHat/upsd \ -RedHat/upsd.in \ -RedHat/upsmon \ -RedHat/upsmon.in \ -Solaris8/S99upsmon \ -subdriver/path-to-subdriver.sh \ -upower/95-upower-hid.rules \ -Windows/halt.c \ -Windows/Makefile + avahi/nut.service.in \ + HP-UX/nut-drvctl \ + HP-UX/nut-drvctl.sh \ + HP-UX/nut-upsd \ + HP-UX/nut-upsd.sh \ + HP-UX/nut-upsmon \ + HP-UX/nut-upsmon.sh \ + logrotate/nutlogd \ + misc/nut.bash_completion \ + misc/osd-notify \ + perl/Nut.pm \ + RedHat/halt.patch \ + RedHat/README \ + RedHat/ups \ + RedHat/upsd \ + RedHat/upsd.in \ + RedHat/upsmon \ + RedHat/upsmon.in \ + Solaris8/S99upsmon \ + subdriver/gen-usbhid-subdriver.sh \ + subdriver/gen-snmp-subdriver.sh \ + ufw/README \ + upower/95-upower-hid.rules \ + Windows/halt.c \ + Windows/Makefile -SUBDIRS = augeas hal hotplug java python systemd udev +SUBDIRS = augeas devd hotplug python systemd udev ufw Solaris upsdrvsvcctl +MAINTAINERCLEANFILES = Makefile.in .dirstamp all: all-recursive .SUFFIXES: @@ -322,14 +466,13 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu scripts/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -348,22 +491,25 @@ clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd -# into them and run `make' without going through this Makefile. -# To change the values of `make' variables: instead of editing Makefiles, -# (1) if the variable is set in `config.status', edit `config.status' -# (which will cause the Makefiles to be regenerated when you run `make'); -# (2) otherwise, pass the desired values on the `make' command line. -$(RECURSIVE_TARGETS): - @fail= failcom='exit 1'; \ - for f in x $$MAKEFLAGS; do \ - case $$f in \ - *=* | --[!k]*);; \ - *k*) failcom='fail=yes';; \ - esac; \ - done; \ +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ - list='$(SUBDIRS)'; for subdir in $$list; do \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ @@ -378,57 +524,12 @@ $(RECURSIVE_TARGETS): $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" -$(RECURSIVE_CLEAN_TARGETS): - @fail= failcom='exit 1'; \ - for f in x $$MAKEFLAGS; do \ - case $$f in \ - *=* | --[!k]*);; \ - *k*) failcom='fail=yes';; \ - esac; \ - done; \ - dot_seen=no; \ - case "$@" in \ - distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ - *) list='$(SUBDIRS)' ;; \ - esac; \ - rev=''; for subdir in $$list; do \ - if test "$$subdir" = "."; then :; else \ - rev="$$subdir $$rev"; \ - fi; \ - done; \ - rev="$$rev ."; \ - target=`echo $@ | sed s/-recursive//`; \ - for subdir in $$rev; do \ - echo "Making $$target in $$subdir"; \ - if test "$$subdir" = "."; then \ - local_target="$$target-am"; \ - else \ - local_target="$$target"; \ - fi; \ - ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ - || eval $$failcom; \ - done && test -z "$$fail" -tags-recursive: - list='$(SUBDIRS)'; for subdir in $$list; do \ - test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ - done -ctags-recursive: - list='$(SUBDIRS)'; for subdir in $$list; do \ - test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ - done +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags -ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ - mkid -fID $$unique -tags: TAGS - -TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ @@ -444,12 +545,7 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ + $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ @@ -461,15 +557,11 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $$unique; \ fi; \ fi -ctags: CTAGS -CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique @@ -478,11 +570,29 @@ GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -514,13 +624,10 @@ distdir: $(DISTFILES) done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ - test -d "$(distdir)/$$subdir" \ - || $(MKDIR_P) "$(distdir)/$$subdir" \ - || exit 1; \ - fi; \ - done - @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ @@ -555,10 +662,15 @@ install-am: all-am installcheck: installcheck-recursive install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -570,6 +682,7 @@ distclean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am @@ -636,22 +749,22 @@ ps-am: uninstall-am: -.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ - install-am install-strip tags-recursive +.MAKE: $(am__recursive_targets) install-am install-strip -.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ - all all-am check check-am clean clean-generic clean-libtool \ - ctags ctags-recursive distclean distclean-generic \ - distclean-libtool distclean-tags distdir dvi dvi-am html \ - html-am info info-am install install-am install-data \ - install-data-am install-dvi install-dvi-am install-exec \ - install-exec-am install-html install-html-am install-info \ - install-info-am install-man install-pdf install-pdf-am \ - install-ps install-ps-am install-strip installcheck \ - installcheck-am installdirs installdirs-am maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ - uninstall uninstall-am +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/scripts/README b/scripts/README index 0b1b981..f31e103 100644 --- a/scripts/README +++ b/scripts/README @@ -2,13 +2,10 @@ These directories hold various scripts: - example startup and shutdown scripts for various operating systems and distributions, - hotplug and udev integration for on the fly privileges settings (Linux only), -- Hardware Abstraction Layer script, for declaring and binding NUT addons to -specific USB devices, - UPower (previously DeviceKit-power) rules file, - Python Client module and application, - Perl client module, - Augeas support lenses and modules for NUT, -- Java client library and test application, - systemd support files. They have either been contributed by users of the software, or by the NUT Team diff --git a/scripts/Solaris/Makefile.am b/scripts/Solaris/Makefile.am new file mode 100644 index 0000000..1544c79 --- /dev/null +++ b/scripts/Solaris/Makefile.am @@ -0,0 +1,88 @@ +EXTRA_DIST = makelocal.sh precheck.py.in preproto.pl.in README +PROTOTYPE_DIR = $(DESTDIR)@prefix@ +SOLARIS_CHECK_TARGETS = +PYTHON = @PYTHON@ + +SOLARIS_SMF_MANIFESTS = \ + nut.xml \ + nut-server.xml \ + nut-monitor.xml \ + nut-driver.xml \ + nut-driver-enumerator.xml + +SOLARIS_SMF_METHODSCRIPTS = \ + svc-nut-server \ + svc-nut-monitor + +if WITH_SOLARIS_SMF +# OS equivalent of /lib/svc/method and /var/svc/manifest/application +# but we can just use then from this location +solarissmfmethoddir = @datadir@/solaris-smf/method +solarissmfmanifestdir = @datadir@/solaris-smf/manifest +solarissmfmethod_SCRIPTS = $(SOLARIS_SMF_METHODSCRIPTS) +solarissmfmanifest_DATA = $(SOLARIS_SMF_MANIFESTS) + +libexec_SCRIPTS = ../upsdrvsvcctl/nut-driver-enumerator.sh + +sbin_SCRIPTS = ../upsdrvsvcctl/upsdrvsvcctl + +SOLARIS_CHECK_TARGETS += check-local-solaris-smf +endif + +solarisinitscriptdir = @datadir@/solaris-init +solarisinitscript_SCRIPTS = nut + +SOLARIS_PACKAGE_TARGETS = + +if WITH_SOLARIS_PKG_IPS +SOLARIS_PACKAGE_TARGETS += package-solaris-ips +endif + +if WITH_SOLARIS_PKG_SVR4 +SOLARIS_PACKAGE_TARGETS += package-solaris-svr4 +endif + +package: $(SOLARIS_PACKAGE_TARGETS) + +# TODO: Reduce build dependencies (implicit!) on python and perl +# by shelling the scripts used below +# NOTE: This assumes the rest of the product has already been built +# and installed under PROTOTYPE_DIR, but declares no explicit +# dependency on that +SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS = makelocal.sh precheck.py preproto.pl +SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS = preinstall postinstall preremove postremove +SOLARIS_PACKAGE_SVR4_INSTALLDATA = pkginfo +package-solaris-svr4: $(SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLDATA) + if test -n "@auglensdir@" && test -d "$(DESTDIR)@auglensdir@" ; then \ + mkdir -p "$(DESTDIR)@datadir@/augeas-lenses" && \ + cd "$(DESTDIR)@auglensdir@" && \ + ( cp -prf ./ "$(DESTDIR)@datadir@/augeas-lenses/" || cp -rf ./ "$(DESTDIR)@datadir@/augeas-lenses/" ) ; fi + cd $(PROTOTYPE_DIR) && find . -print | pkgproto > prototype1 + cp $(SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLDATA) $(PROTOTYPE_DIR) + cd $(PROTOTYPE_DIR) && chmod +x $(SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS) + cd $(PROTOTYPE_DIR) && perl preproto.pl +if HAVE_PYTHON + cd $(PROTOTYPE_DIR) && $(PYTHON) precheck.py +endif + cd $(PROTOTYPE_DIR) && rm -f prototype1 + cd $(PROTOTYPE_DIR) && ./makelocal.sh + cp $(PROTOTYPE_DIR)/*.gz $(builddir) + UNAME_P="`uname -p`" && case "$${UNAME_P}" in \ + i386|sparc) \ + mv -f NUT_solaris_package.local.gz "$(abs_top_builddir)/NUT_solaris_$${UNAME_P}_package@PACKAGE_VERSION@.local.gz" ;; \ + esac + +# TODO: Define support for IPS packaging (provide p5m files and make rules) +package-solaris-ips: + @echo "SKIPPED : Target $@ is not implemented yet" + +check-local: $(SOLARIS_CHECK_TARGETS) + +check-local-solaris-smf: $(SOLARIS_SMF_MANIFESTS) + @[ -x /usr/sbin/svccfg ] || { echo "WARNING : Target $@ skipped due to absent /usr/sbin/svccfg" >&2; return 0; } ; \ + RES=0 ; for F in $^ ; do \ + echo " SVCCFG-VALIDATE $$F"; \ + /usr/sbin/svccfg validate "$$F" || RES=$$? ; \ + done; exit $$RES + +MAINTAINERCLEANFILES = Makefile.in .dirstamp diff --git a/scripts/Solaris/Makefile.in b/scripts/Solaris/Makefile.in new file mode 100644 index 0000000..b618339 --- /dev/null +++ b/scripts/Solaris/Makefile.in @@ -0,0 +1,887 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@WITH_SOLARIS_SMF_TRUE@am__append_1 = check-local-solaris-smf +@WITH_SOLARIS_PKG_IPS_TRUE@am__append_2 = package-solaris-ips +@WITH_SOLARIS_PKG_SVR4_TRUE@am__append_3 = package-solaris-svr4 +subdir = scripts/Solaris +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/nut_arg_with.m4 \ + $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ + $(top_srcdir)/m4/nut_check_libavahi.m4 \ + $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ + $(top_srcdir)/m4/nut_check_libgd.m4 \ + $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ + $(top_srcdir)/m4/nut_check_libneon.m4 \ + $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ + $(top_srcdir)/m4/nut_check_libpowerman.m4 \ + $(top_srcdir)/m4/nut_check_libusb.m4 \ + $(top_srcdir)/m4/nut_check_libwrap.m4 \ + $(top_srcdir)/m4/nut_check_os.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ + $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ + $(top_srcdir)/m4/nut_type_socklen_t.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/include/config.h +CONFIG_CLEAN_FILES = nut-driver-enumerator.xml nut-driver.xml \ + nut-monitor.xml nut-server.xml nut.xml pkginfo svc-nut-server \ + svc-nut-monitor precheck.py preinstall postinstall preremove \ + postremove preproto.pl nut +CONFIG_CLEAN_VPATH_FILES = +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" \ + "$(DESTDIR)$(solarisinitscriptdir)" \ + "$(DESTDIR)$(solarissmfmethoddir)" \ + "$(DESTDIR)$(solarissmfmanifestdir)" +SCRIPTS = $(libexec_SCRIPTS) $(sbin_SCRIPTS) \ + $(solarisinitscript_SCRIPTS) $(solarissmfmethod_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(solarissmfmanifest_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(srcdir)/nut-driver-enumerator.xml.in \ + $(srcdir)/nut-driver.xml.in $(srcdir)/nut-monitor.xml.in \ + $(srcdir)/nut-server.xml.in $(srcdir)/nut.in \ + $(srcdir)/nut.xml.in $(srcdir)/pkginfo.in \ + $(srcdir)/postinstall.in $(srcdir)/postremove.in \ + $(srcdir)/precheck.py.in $(srcdir)/preinstall.in \ + $(srcdir)/preproto.pl.in $(srcdir)/preremove.in \ + $(srcdir)/svc-nut-monitor.in $(srcdir)/svc-nut-server.in \ + README +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +A2X = @A2X@ +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BINDIR = @BINDIR@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONFPATH = @CONFPATH@ +CPP = @CPP@ +CPPCHECK = @CPPCHECK@ +CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DBLATEX = @DBLATEX@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ +DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ +DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ +DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ +LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ +LIBGD_CFLAGS = @LIBGD_CFLAGS@ +LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ +LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ +LIBIPMI_LIBS = @LIBIPMI_LIBS@ +LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ +LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ +LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ +LIBNEON_LIBS = @LIBNEON_LIBS@ +LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ +LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ +LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ +LIBS = @LIBS@ +LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ +LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ +LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ +LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ +LIBUSB_LIBS = @LIBUSB_LIBS@ +LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ +LIBWRAP_LIBS = @LIBWRAP_LIBS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LN_S_R = @LN_S_R@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OS_NAME = @OS_NAME@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ +RANLIB = @RANLIB@ +RUN_AS_GROUP = @RUN_AS_GROUP@ +RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ +SED = @SED@ +SERLIBS = @SERLIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ +STATEPATH = @STATEPATH@ +STRIP = @STRIP@ +SUN_LIBUSB = @SUN_LIBUSB@ +TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +auglensdir = @auglensdir@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +cgiexecdir = @cgiexecdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +devddir = @devddir@ +docdir = @docdir@ +driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +hotplugdir = @hotplugdir@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +now = @now@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +systemdshutdowndir = @systemdshutdowndir@ +systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +udevdir = @udevdir@ +EXTRA_DIST = makelocal.sh precheck.py.in preproto.pl.in README +PROTOTYPE_DIR = $(DESTDIR)@prefix@ +SOLARIS_CHECK_TARGETS = $(am__append_1) +SOLARIS_SMF_MANIFESTS = \ + nut.xml \ + nut-server.xml \ + nut-monitor.xml \ + nut-driver.xml \ + nut-driver-enumerator.xml + +SOLARIS_SMF_METHODSCRIPTS = \ + svc-nut-server \ + svc-nut-monitor + + +# OS equivalent of /lib/svc/method and /var/svc/manifest/application +# but we can just use then from this location +@WITH_SOLARIS_SMF_TRUE@solarissmfmethoddir = @datadir@/solaris-smf/method +@WITH_SOLARIS_SMF_TRUE@solarissmfmanifestdir = @datadir@/solaris-smf/manifest +@WITH_SOLARIS_SMF_TRUE@solarissmfmethod_SCRIPTS = $(SOLARIS_SMF_METHODSCRIPTS) +@WITH_SOLARIS_SMF_TRUE@solarissmfmanifest_DATA = $(SOLARIS_SMF_MANIFESTS) +@WITH_SOLARIS_SMF_TRUE@libexec_SCRIPTS = ../upsdrvsvcctl/nut-driver-enumerator.sh +@WITH_SOLARIS_SMF_TRUE@sbin_SCRIPTS = ../upsdrvsvcctl/upsdrvsvcctl +solarisinitscriptdir = @datadir@/solaris-init +solarisinitscript_SCRIPTS = nut +SOLARIS_PACKAGE_TARGETS = $(am__append_2) $(am__append_3) + +# TODO: Reduce build dependencies (implicit!) on python and perl +# by shelling the scripts used below +# NOTE: This assumes the rest of the product has already been built +# and installed under PROTOTYPE_DIR, but declares no explicit +# dependency on that +SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS = makelocal.sh precheck.py preproto.pl +SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS = preinstall postinstall preremove postremove +SOLARIS_PACKAGE_SVR4_INSTALLDATA = pkginfo +MAINTAINERCLEANFILES = Makefile.in .dirstamp +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/Solaris/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu scripts/Solaris/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +nut-driver-enumerator.xml: $(top_builddir)/config.status $(srcdir)/nut-driver-enumerator.xml.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nut-driver.xml: $(top_builddir)/config.status $(srcdir)/nut-driver.xml.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nut-monitor.xml: $(top_builddir)/config.status $(srcdir)/nut-monitor.xml.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nut-server.xml: $(top_builddir)/config.status $(srcdir)/nut-server.xml.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nut.xml: $(top_builddir)/config.status $(srcdir)/nut.xml.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +pkginfo: $(top_builddir)/config.status $(srcdir)/pkginfo.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +svc-nut-server: $(top_builddir)/config.status $(srcdir)/svc-nut-server.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +svc-nut-monitor: $(top_builddir)/config.status $(srcdir)/svc-nut-monitor.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +precheck.py: $(top_builddir)/config.status $(srcdir)/precheck.py.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +preinstall: $(top_builddir)/config.status $(srcdir)/preinstall.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +postinstall: $(top_builddir)/config.status $(srcdir)/postinstall.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +preremove: $(top_builddir)/config.status $(srcdir)/preremove.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +postremove: $(top_builddir)/config.status $(srcdir)/postremove.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +preproto.pl: $(top_builddir)/config.status $(srcdir)/preproto.pl.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nut: $(top_builddir)/config.status $(srcdir)/nut.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-libexecSCRIPTS: $(libexec_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(libexec_SCRIPTS)'; test -n "$(libexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-libexecSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(libexec_SCRIPTS)'; test -n "$(libexecdir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(libexecdir)'; $(am__uninstall_files_from_dir) +install-sbinSCRIPTS: $(sbin_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(sbin_SCRIPTS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_SCRIPTS)'; test -n "$(sbindir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(sbindir)'; $(am__uninstall_files_from_dir) +install-solarisinitscriptSCRIPTS: $(solarisinitscript_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(solarisinitscript_SCRIPTS)'; test -n "$(solarisinitscriptdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(solarisinitscriptdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(solarisinitscriptdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(solarisinitscriptdir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(solarisinitscriptdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-solarisinitscriptSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(solarisinitscript_SCRIPTS)'; test -n "$(solarisinitscriptdir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(solarisinitscriptdir)'; $(am__uninstall_files_from_dir) +install-solarissmfmethodSCRIPTS: $(solarissmfmethod_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(solarissmfmethod_SCRIPTS)'; test -n "$(solarissmfmethoddir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(solarissmfmethoddir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(solarissmfmethoddir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(solarissmfmethoddir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(solarissmfmethoddir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-solarissmfmethodSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(solarissmfmethod_SCRIPTS)'; test -n "$(solarissmfmethoddir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(solarissmfmethoddir)'; $(am__uninstall_files_from_dir) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-solarissmfmanifestDATA: $(solarissmfmanifest_DATA) + @$(NORMAL_INSTALL) + @list='$(solarissmfmanifest_DATA)'; test -n "$(solarissmfmanifestdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(solarissmfmanifestdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(solarissmfmanifestdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(solarissmfmanifestdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(solarissmfmanifestdir)" || exit $$?; \ + done + +uninstall-solarissmfmanifestDATA: + @$(NORMAL_UNINSTALL) + @list='$(solarissmfmanifest_DATA)'; test -n "$(solarissmfmanifestdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(solarissmfmanifestdir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-local +check: check-am +all-am: Makefile $(SCRIPTS) $(DATA) +installdirs: + for dir in "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(solarisinitscriptdir)" "$(DESTDIR)$(solarissmfmethoddir)" "$(DESTDIR)$(solarissmfmanifestdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-solarisinitscriptSCRIPTS \ + install-solarissmfmanifestDATA install-solarissmfmethodSCRIPTS + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-libexecSCRIPTS install-sbinSCRIPTS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-libexecSCRIPTS uninstall-sbinSCRIPTS \ + uninstall-solarisinitscriptSCRIPTS \ + uninstall-solarissmfmanifestDATA \ + uninstall-solarissmfmethodSCRIPTS + +.MAKE: check-am install-am install-strip + +.PHONY: all all-am check check-am check-local clean clean-generic \ + clean-libtool cscopelist-am ctags-am distclean \ + distclean-generic distclean-libtool distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-libexecSCRIPTS install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-sbinSCRIPTS \ + install-solarisinitscriptSCRIPTS \ + install-solarissmfmanifestDATA install-solarissmfmethodSCRIPTS \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am uninstall-libexecSCRIPTS \ + uninstall-sbinSCRIPTS uninstall-solarisinitscriptSCRIPTS \ + uninstall-solarissmfmanifestDATA \ + uninstall-solarissmfmethodSCRIPTS + +.PRECIOUS: Makefile + + +package: $(SOLARIS_PACKAGE_TARGETS) +package-solaris-svr4: $(SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLDATA) + if test -n "@auglensdir@" && test -d "$(DESTDIR)@auglensdir@" ; then \ + mkdir -p "$(DESTDIR)@datadir@/augeas-lenses" && \ + cd "$(DESTDIR)@auglensdir@" && \ + ( cp -prf ./ "$(DESTDIR)@datadir@/augeas-lenses/" || cp -rf ./ "$(DESTDIR)@datadir@/augeas-lenses/" ) ; fi + cd $(PROTOTYPE_DIR) && find . -print | pkgproto > prototype1 + cp $(SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLDATA) $(PROTOTYPE_DIR) + cd $(PROTOTYPE_DIR) && chmod +x $(SOLARIS_PACKAGE_SVR4_HELPERSCRIPTS) $(SOLARIS_PACKAGE_SVR4_INSTALLSCRIPTS) + cd $(PROTOTYPE_DIR) && perl preproto.pl +@HAVE_PYTHON_TRUE@ cd $(PROTOTYPE_DIR) && $(PYTHON) precheck.py + cd $(PROTOTYPE_DIR) && rm -f prototype1 + cd $(PROTOTYPE_DIR) && ./makelocal.sh + cp $(PROTOTYPE_DIR)/*.gz $(builddir) + UNAME_P="`uname -p`" && case "$${UNAME_P}" in \ + i386|sparc) \ + mv -f NUT_solaris_package.local.gz "$(abs_top_builddir)/NUT_solaris_$${UNAME_P}_package@PACKAGE_VERSION@.local.gz" ;; \ + esac + +# TODO: Define support for IPS packaging (provide p5m files and make rules) +package-solaris-ips: + @echo "SKIPPED : Target $@ is not implemented yet" + +check-local: $(SOLARIS_CHECK_TARGETS) + +check-local-solaris-smf: $(SOLARIS_SMF_MANIFESTS) + @[ -x /usr/sbin/svccfg ] || { echo "WARNING : Target $@ skipped due to absent /usr/sbin/svccfg" >&2; return 0; } ; \ + RES=0 ; for F in $^ ; do \ + echo " SVCCFG-VALIDATE $$F"; \ + /usr/sbin/svccfg validate "$$F" || RES=$$? ; \ + done; exit $$RES + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/scripts/Solaris/README b/scripts/Solaris/README new file mode 100644 index 0000000..adb305e --- /dev/null +++ b/scripts/Solaris/README @@ -0,0 +1,38 @@ +This directory contains init-scripts and SMF manifests and methods +for integration of NUT services with Solaris and descendant OSes. + +This also includes the nut-driver-enumerator.sh (service and implementation +method) and upsdrvsvcctl (tool) to manage NUT drivers as service instances, +which are stored in ../upsdrvsvcctl/ subdirectory (portable codebase shared +with Linux systemd). + +The default implementation (runs once) can be enabled with: + + svcadm enable nut-driver-enumerator:default + +Note that at the moment there is no out-of-the-box integration for triggering +a restart/refresh of the nut-driver-enumerator SMF service at the very instant +when the `ups.conf` file is modified, like there is with systemd path unit type. +Due to this, the systems administrator is expected to either invoke +`svcadm refresh nut-driver-enumerator` after changing the NUT configuration +or wait until the daemonized mode, if enabled, picks up the change (should do +so within a minute by default). However, a DTrace script or a tool like +https://github.com/emcrisostomo/fswatch wrapped into a service might be used +for equivalent effect. + +Alternatively, but somewhat more expensively, the same `nut-driver-enumerator.sh` +script can be executed in a loop as the payload of the SMF service to keep +inspecting the configuration and apply changes to the running system. It is +not a common use-case to keep changing device setups, so this solution is not +enforced by default ;) although a service variant is provided... + +Note that only one of these can be enabled at the same time: + + svcadm disable nut-driver-enumerator:default + svcadm enable nut-driver-enumerator:daemon + +Init-script solution contributed by numerous authors +SMF solution contributed by Jim Klimov + +For special notes about USB-connected device monitoring with NUT under Solaris +and related operating systems, see docs/solaris-usb.txt diff --git a/scripts/Solaris/makelocal.sh b/scripts/Solaris/makelocal.sh new file mode 100755 index 0000000..f017c43 --- /dev/null +++ b/scripts/Solaris/makelocal.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +# Creates the package file from current-directory contents +# Called by Makefile starting from installed prototype directory + +echo "Making Solaris SVR4 package metadata..." && \ +pkgmk -o -d "`pwd`" && \ +echo "Making Solaris SVR4 package archive file..." && \ +( yes "" | pkgtrans "`pwd`" "`pwd`/NUT_solaris_package.local" ) && \ +echo "Compressing Solaris SVR4 package archive file..." && \ +gzip "`pwd`/NUT_solaris_package.local" diff --git a/scripts/Solaris/nut-driver-enumerator.xml.in b/scripts/Solaris/nut-driver-enumerator.xml.in new file mode 100644 index 0000000..55723c0 --- /dev/null +++ b/scripts/Solaris/nut-driver-enumerator.xml.in @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/Solaris/nut-driver.xml.in b/scripts/Solaris/nut-driver.xml.in new file mode 100644 index 0000000..c52afac --- /dev/null +++ b/scripts/Solaris/nut-driver.xml.in @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/Solaris/nut-monitor.xml.in b/scripts/Solaris/nut-monitor.xml.in new file mode 100644 index 0000000..c3118d3 --- /dev/null +++ b/scripts/Solaris/nut-monitor.xml.in @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/Solaris/nut-server.xml.in b/scripts/Solaris/nut-server.xml.in new file mode 100644 index 0000000..6b3b4d0 --- /dev/null +++ b/scripts/Solaris/nut-server.xml.in @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/Solaris/nut.in b/scripts/Solaris/nut.in new file mode 100755 index 0000000..1c39603 --- /dev/null +++ b/scripts/Solaris/nut.in @@ -0,0 +1,66 @@ +#!/sbin/sh + +#init.d script to start nut services + +NUT_DIR="@prefix@" +NUT_SBIN_DIR="${NUT_DIR}/sbin" +NUT_LIB_DIR="${NUT_DIR}/lib" +CONFIG="@CONFPATH@/nut.conf" + +if [ -f "$CONFIG" ] ; then + . "$CONFIG" +fi + +ups_stop () { + pkill -n upsmon + pkill -n upsd + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsdrvctl" stop > /dev/null 2>&1 +} + +ups_start () { + if [ "$MODE" = "none" ];then + echo "No NUT mode set, not starting anything" >&2 + exit 1 + fi + + if [ "$MODE" != "netclient" ] ; then + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsdrvctl" start #> /dev/null 2>&1 + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsd" #> /dev/null 2>&1 + fi + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsmon" #> /dev/null 2>&1 +} + +case $1 in +'start') + ups_start + ;; + +'stop') + ups_stop + ;; + +'restart') + ups_stop + while pgrep upsd > /dev/null + do + sleep 1 + done + ups_start + ;; +'poweroff') + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsmon" -K >/dev/null 2>&1 + if [ $? = 0 ]; then + echo "Shutting down the UPS(es) ..." + echo "WARNING: UPS shutdown is currently disabled, please uncomment it in the init-script if desired" >&2 + #${NUT_SBIN_DIR}/upsdrvctl shutdown + fi + ;; +*) + echo "" + echo "Usage: '$0' {start | stop | restart }" + echo "" + exit 64 + ;; + +esac +exit $? diff --git a/scripts/Solaris/nut.xml.in b/scripts/Solaris/nut.xml.in new file mode 100644 index 0000000..b06b2b1 --- /dev/null +++ b/scripts/Solaris/nut.xml.in @@ -0,0 +1,102 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/Solaris/pkginfo.in b/scripts/Solaris/pkginfo.in new file mode 100644 index 0000000..39667f0 --- /dev/null +++ b/scripts/Solaris/pkginfo.in @@ -0,0 +1,11 @@ +PKG="NUT" +NAME="Network UPS Tools" +ARCH="@target_cpu@" +VERSION="@PACKAGE_VERSION@" +CATEGORY="application" +VENDOR="http://www.networkupstools.org" +EMAIL=" " +PSTAMP=" " +DESCRIPTION="Network UPS Tools (NUT) is a client/server monitoring system that allows computers to share uninterruptible power supply (UPS) and power distribution unit (PDU) hardware. Clients access the hardware through the server, and are notified whenever the power status changes." +BASEDIR="@prefix@" +CLASSES="none" diff --git a/scripts/Solaris/postinstall.in b/scripts/Solaris/postinstall.in new file mode 100755 index 0000000..a6068b4 --- /dev/null +++ b/scripts/Solaris/postinstall.in @@ -0,0 +1,126 @@ +#!/bin/sh + +# Postinstall script for Network UPS Tools package + +NUT_DIR="@prefix@" +prefix="@prefix@" # expanded as part of some autoconf macros below + +# TODO/FIXME : Should "/var/run" be a configure variable? +# Note that "/var/run" is transient tmpfs, so upgrade has to be done during same uptime. +ACTIVE_ENUMERATOR_FMRI_FILE="/var/run/nut-driver-enumerator-fmri.prev" + +# make sure the nut user exists and has correct memberships +res="`getent group @RUN_AS_GROUP@`" || res="" +if [ -z "$res" ]; then + /usr/sbin/groupadd "@RUN_AS_GROUP@" +fi +res="`getent passwd @RUN_AS_USER@`" || res="" +if [ -z "$res" ]; then + /usr/sbin/useradd -c "Network UPS Tools" -g "@RUN_AS_GROUP@" -G root -d "@STATEPATH@" -s /bin/false @RUN_AS_USER@ +fi + +res="`groups "@RUN_AS_GROUP@" | grep -w "@RUN_AS_USER@"`" || res="" +if [ -z "$res" ]; then + /usr/sbin/usermod -g "@RUN_AS_GROUP@" -G root "@RUN_AS_USER@" +fi + +# make sure that conffiles are secured and have the correct ownerships +if [ -d "@CONFPATH@" ] ; then + chown "root:@RUN_AS_GROUP@" "@CONFPATH@" +fi +for file in nut.conf ups.conf upsd.conf upsmon.conf upsd.users upssched.conf nut-driver-enumerator.conf; do + if [ -f "@CONFPATH@/$file" ] ; then + chown "root:@RUN_AS_GROUP@" "@CONFPATH@/$file" + chmod 640 "@CONFPATH@/$file" + fi +done + +# make sure that /var/run/nut exists and has the correct ownerships +if [ ! -d "@PIDPATH@/nut" ] ; then + mkdir -p "@PIDPATH@/nut" +fi +if [ -d "@PIDPATH@/nut" ] ; then + chown "root:@RUN_AS_GROUP@" "@PIDPATH@/nut" + chmod 770 "@PIDPATH@/nut" +fi + +# make sure that /var/state/ups exists and has the correct ownerships +if [ ! -d "@STATEPATH@" ] ; then + mkdir -p "@STATEPATH@" +fi +if [ -d "@STATEPATH@" ] ; then + chown "root:@RUN_AS_GROUP@" "@STATEPATH@" + chmod 770 "@STATEPATH@" +fi + +if [ -n "@auglensdir@" ] && [ -d "@auglensdir@" ] && [ -d "@datarootdir@/augeas-lenses" ] ; then + ( cd "@datarootdir@/augeas-lenses" && cp -prf ./ "@auglensdir@"/ ) +fi + +if test -x /usr/sbin/svcadm && test -x /usr/sbin/svccfg && test -x /usr/bin/svcs ; then + echo "Register SMF services..." + for S in nut-driver-enumerator nut-driver nut-server nut-monitor nut ; do + echo "Importing NUT service manifest: $S..." + /usr/sbin/svccfg import "@datarootdir@/solaris-smf/manifest/$S.xml" + done + # Enable services if the system already has a configuration (e.g. upgrade) + if test -s "@CONFPATH@/ups.conf" ; then + echo "Stopping NUT drivers, if any (in case of upgrade)..." + @SBINDIR@/upsdrvsvcctl stop + @SBINDIR@/upsdrvctl -DDDDD stop + sleep 5 + echo "(Re-)register NUT drivers (if any)..." + REPORT_RESTART_42=no AUTO_START=no "@NUT_LIBEXECDIR@/nut-driver-enumerator.sh" --reconfigure + sleep 2 + echo "Enable NUT drivers (if any)..." + # Note: we now provide two services, a daemon that keeps checking + # the config for changes and a default one that should be refreshed + # manually to reconfigure nut-driver instances - and is "cheaper". + # This may still fail if the daemon instance is somehow enabled (admin) + PREV_ACTIVE_ENUMERATOR="" + if test -s "${ACTIVE_ENUMERATOR_FMRI_FILE}" ; then + PREV_ACTIVE_ENUMERATOR="`head -1 "${ACTIVE_ENUMERATOR_FMRI_FILE}"`" + fi + [ x"nut-driver-enumerator:default" = x"${PREV_ACTIVE_ENUMERATOR}" ] && PREV_ACTIVE_ENUMERATOR="" + for ACTIVE_ENUMERATOR in ${PREV_ACTIVE_ENUMERATOR} nut-driver-enumerator:default ; do + /usr/sbin/svcadm enable -s ${ACTIVE_ENUMERATOR} || \ + { /usr/sbin/svcadm clear ${ACTIVE_ENUMERATOR} 2>/dev/null ; \ + /usr/sbin/svcadm enable -s ${ACTIVE_ENUMERATOR} ; } && break || true + done + @SBINDIR@/upsdrvsvcctl start + else + echo "NOT ENABLING nut-driver-enumerator at this time : missing or empty @CONFPATH@/ups.conf" >&2 + fi + if test -s "@CONFPATH@/ups.conf" && test -e "@CONFPATH@/upsd.conf" && test -e "@CONFPATH@/upsd.users" ; then + # Note on the mix of "-s" and "-e" in tests above: + # it is a valid use-case for an admin to have just touched an + # empty upsd.conf and so use default settings for the daemon + echo "Enable NUT upsd data server..." + /usr/sbin/svcadm enable -s nut-server + else + echo "NOT ENABLING nut-server at this time : missing at least one of : @CONFPATH@/ups.conf @CONFPATH@/upsd.conf @CONFPATH@/upsd.users" >&2 + fi + if test -s "@CONFPATH@/upsmon.conf" ; then + echo "Enable NUT upsmon client..." + /usr/sbin/svcadm enable -s nut-monitor + else + echo "NOT ENABLING nut-monitor at this time : missing or empty @CONFPATH@/upsmon.conf" >&2 + fi + echo "Enable NUT umbrella service..." + /usr/sbin/svcadm enable -s nut +else + echo "Put init script in /etc/init.d..." + cp -pf "@NUT_DATADIR@/solaris-init/nut" /etc/init.d + chown root:bin /etc/init.d/nut + chmod 744 /etc/init.d/nut + + ln -s ../init.d/nut /etc/rc3.d/S90nut > /dev/null 2>&1 + ln -s ../init.d/nut /etc/rc3.d/K10nut > /dev/null 2>&1 + + # Start nut services + + #echo "Starting nut services" + #$NUT_DIR/sbin/upsdrvctl start #> /dev/null 2>&1 + #$NUT_DIR/sbin/upsd #> /dev/null 2>&1 + #$NUT_DIR/sbin/upsmon #> /dev/null 2>&1 +fi diff --git a/scripts/Solaris/postremove.in b/scripts/Solaris/postremove.in new file mode 100755 index 0000000..dce3f44 --- /dev/null +++ b/scripts/Solaris/postremove.in @@ -0,0 +1,19 @@ +#!/bin/sh + +# Postremove script for Network UPS Tools package + +# Remove init script from /etc/init.d - created by scripts not packaging + +rm -f /etc/init.d/nut +rm -f /etc/rc3.d/S90nut +rm -f /etc/rc3.d/K10nut + +# Remove nut group and user + +/usr/sbin/userdel "@RUN_AS_USER@" + +/usr/sbin/groupdel "@RUN_AS_GROUP@" + +# Remove /var/run/nut + +rm -rf "@PIDPATH@/nut" diff --git a/scripts/Solaris/precheck.py.in b/scripts/Solaris/precheck.py.in new file mode 100755 index 0000000..5a6457c --- /dev/null +++ b/scripts/Solaris/precheck.py.in @@ -0,0 +1,48 @@ +#!@PYTHON@ + +import sys + +if sys.version_info >= ( 2, 6 ): + import subprocess + p = subprocess.Popen(["uname", "-s"], stdout=subprocess.PIPE) + platform = p.communicate()[0] + if p.returncode != 0: + raise Exception("FAILED to get platform from 'uname -s'!") + + p = subprocess.Popen(["uname", "-p"], stdout=subprocess.PIPE) + architecture = p.communicate()[0] + if p.returncode != 0: + raise Exception("FAILED to get architecture from 'uname -p'!") +else: + import commands + platform = commands.getoutput('uname -s') + architecture = commands.getoutput('uname -p') + +# checkinstall script creation +fp=open("checkinstall","w") +# Note: same arch is relevant for different bitnesses that +# can be discerned via `isainfo` further (if ever needed) +fp.write("#!/bin/sh\n") +fp.write("\nexpected_platform=SunOS\n") +if platform == "SunOS" and architecture == "i386": + fp.write("expected_architecture=i386\n") +elif platform == "SunOS" and architecture == "sparc": + fp.write("expected_architecture=sparc\n") + +fp.write("platform=\"`uname -s`\"\n") +fp.write("architecture=\"`uname -p`\"\n\n") + +fp.write("if [ \"${platform}\" -eq \"${expected_platform}\" ]; then\n") +fp.write("\tif [ \"${architecture}\" -eq \"${expected_architecture}\" ]; then\n") +fp.write("\t\techo \"Checkinstall complete\"\n") +fp.write("\telse\n") +fp.write("\t\techo \"This is not Solaris $architecture machine: platform='${platform}' expected_platform='${expected_platform}'\"\n") +fp.write("\t\texit 1\n") +fp.write("\tfi\n") +fp.write("else\n") +fp.write("\techo \"This is not Solaris machine: architecture='${architecture}' expected_architecture='${expected_architecture}'\"\n") +fp.write("\texit 1\n") +fp.write("fi\n") +fp.write("exit 0\n") + +fp.close() diff --git a/scripts/Solaris/preinstall.in b/scripts/Solaris/preinstall.in new file mode 100755 index 0000000..42da1ce --- /dev/null +++ b/scripts/Solaris/preinstall.in @@ -0,0 +1,23 @@ +#!/bin/sh + +# Preinstall script for Network UPS Tools package +NUT_DIR="@prefix@" + +# Create group nut + +grep -w "@RUN_AS_GROUP@" /etc/group +if [ "$?" != 0 ]; then + /usr/sbin/groupadd "@RUN_AS_GROUP@" +fi + +# Create user for installing "Network UPS Tools" + +grep -w "@RUN_AS_USER@" /etc/passwd +if [ "$?" != 0 ]; then + /usr/sbin/useradd -c "Network UPS Tools" -g "@RUN_AS_GROUP@" -G root -d "@STATEPATH@" -s /bin/false "@RUN_AS_USER@" +fi + +res="`groups "@RUN_AS_GROUP@" | grep -w "@RUN_AS_USER@"`" || res="" +if [ -z "$res" ]; then + /usr/sbin/usermod -g "@RUN_AS_GROUP@" -G root "@RUN_AS_USER@" +fi diff --git a/scripts/Solaris/preproto.pl.in b/scripts/Solaris/preproto.pl.in new file mode 100755 index 0000000..ac52c16 --- /dev/null +++ b/scripts/Solaris/preproto.pl.in @@ -0,0 +1,39 @@ +#!/usr/bin/env perl + +$temp = "prototype1"; +$prototype="prototype"; +$pkginfo = "pkginfo"; +$checkinstall = "checkinstall"; +$preinstall = "preinstall"; +$postinstall = "postinstall"; +$preremove = "preremove"; +$postremove = "postremove"; +$nutuser = "@RUN_AS_USER@"; +$nutgroup = "@RUN_AS_GROUP@"; + +open (PREPROTO,"< $temp") || die "Unable to read prototype information ($!)\n"; +open (PROTO,"> $prototype") || die "Unable to write file prototype ($!)\n"; +print PROTO "i pkginfo=./$pkginfo\n"; +print PROTO "i checkinstall=./$checkinstall\n"; +print PROTO "i preinstall=./$preinstall\n"; +print PROTO "i postinstall=./$postinstall\n"; +print PROTO "i preremove=./$preremove\n"; +print PROTO "i postremove=./$postremove\n"; +while () { + # Read the prototype information from /tmp/prototype$$ + chomp; + $thisline = $_; + if ($thisline =~ " prototype1 ") { + # We don't need that line + } elsif ($thisline =~ "^[fd] ") { + # Change the ownership for files and directories + ($dir, $none, $file, $mode, $user, $group) = split / /,$thisline; + print PROTO "$dir $none $file=$file $mode $nutuser $nutgroup\n"; + } else { + # Symlinks and other stuff should be printed as well ofcourse + print PROTO "$thisline\n"; + } +} +#print PROTO "f $none nut $mode root $nutgroup\n"; +close PROTO; +close PREPROTO; diff --git a/scripts/Solaris/preremove.in b/scripts/Solaris/preremove.in new file mode 100755 index 0000000..abce051 --- /dev/null +++ b/scripts/Solaris/preremove.in @@ -0,0 +1,71 @@ +#!/bin/sh + +# Preremove script for Network UPS Tools package + +# Stop all nut services + +NUT_DIR="@prefix@" +prefix="@prefix@" # expanded as part of some autoconf macros below + +# TODO/FIXME : Should "/var/run" be a configure variable? +# Note that "/var/run" is transient tmpfs, so upgrade has to be done during same uptime. +ACTIVE_ENUMERATOR_FMRI_FILE="/var/run/nut-driver-enumerator-fmri.prev" + +if test -x /usr/sbin/svcadm && test -x /usr/sbin/svccfg && test -x /usr/bin/svcs ; then + # Unconfigure SMF services + # First detect the first active (online, maintenance, etc.) + # instance of nut-driver-enumerator so we can pass it to the + # next lifetime in case of re-install of NUT and keep the + # user's previously declared preference. + ACTIVE_ENUMERATOR="`/usr/bin/svcs -H -o state,fmri '*/nut-driver-enumerator:*' | while read S F ; do [ "$S" != "disabled" ] && [ "$S" != "offline" ] && echo "$F" && break ; done`" + if [ -n "$ACTIVE_ENUMERATOR" ]; then + rm -f "${ACTIVE_ENUMERATOR_FMRI_FILE}" + touch "${ACTIVE_ENUMERATOR_FMRI_FILE}" + chmod 600 "${ACTIVE_ENUMERATOR_FMRI_FILE}" + chown 0:0 "${ACTIVE_ENUMERATOR_FMRI_FILE}" + echo "${ACTIVE_ENUMERATOR}" > "${ACTIVE_ENUMERATOR_FMRI_FILE}" + fi + # First tell the automagic to stop, so it does not interfere; diligently clean it out below + /usr/sbin/svcadm disable nut-driver-enumerator:default || true + /usr/sbin/svcadm disable nut-driver-enumerator:daemon || true + for S in nut nut-monitor nut-server ; do + echo "Stopping NUT service: $S..." + /usr/sbin/svcadm clear "$S" 2>/dev/null + /usr/sbin/svcadm disable -s "$S" + echo "Removing NUT service: $S..." + /usr/sbin/svccfg delete "$S" || \ + /usr/sbin/svccfg -s "$S" delete || \ + /usr/sbin/svccfg -s "$S" delete default + done + echo "Stopping NUT drivers, if any..." + @SBINDIR@/upsdrvsvcctl stop + @SBINDIR@/upsdrvctl -DDDDD stop + sleep 5 + for S in `/usr/bin/svcs -H -o fmri '*/nut-driver:*'` `/usr/bin/svcs -H -o fmri '*/nut-driver-enumerator:*'` ; do + echo "Stopping NUT service: $S..." + /usr/sbin/svcadm clear "$S" 2>/dev/null + /usr/sbin/svcadm disable -s "$S" + done + sleep 5 + for S in `/usr/bin/svcs -H -o fmri '*/nut-driver:*' | grep -wv default` `/usr/bin/svcs -H -o fmri '*/nut-driver-enumerator:*' | grep -wv default` ; do + echo "Removing NUT service: $S..." + # Note: S here is a full FMRI URL + SB="`echo "$S" | sed 's,:[^:]*$,,'`" + SI="`echo "$S" | sed 's,^.*:\([^:]*\)$,\1,'`" + /usr/sbin/svcadm disable -s "$S" + /usr/sbin/svccfg -s "$SB" delete -f "$SI" || \ + /usr/sbin/svccfg delete "$S" + done + for S in nut-driver-enumerator nut-driver ; do + echo "Removing NUT service: $S..." && \ + /usr/sbin/svccfg delete "$S" || \ + /usr/sbin/svccfg -s "$S" delete || \ + /usr/sbin/svccfg -s "$S" delete default + done +else + [ -x /etc/init.d/nut ] && /etc/init.d/nut stop +fi + +if [ -n "@auglensdir@" ] && [ -d "@auglensdir@" ] && [ -d "@datarootdir@/augeas-lenses" ] ; then + ( cd "@datarootdir@/augeas-lenses" && find . -type f -exec rm -f "@auglensdir@"/'{}' \; ) +fi diff --git a/scripts/Solaris/svc-nut-monitor.in b/scripts/Solaris/svc-nut-monitor.in new file mode 100755 index 0000000..a7dd0dc --- /dev/null +++ b/scripts/Solaris/svc-nut-monitor.in @@ -0,0 +1,56 @@ +#!/sbin/sh + +# Trivial (better is yet to come) SMF method script to start nut services +# Adapted for OpenIndiana userland from init.d script template in NUT sources +# Adaptation copyright (C) 2016-2017 Jim Klimov + +if [ -z "$SMF_FMRI" ]; then + echo "$0 must be called in SMF context!" >&2 + exit 1 +fi + +# smf(5) +. /lib/svc/share/smf_include.sh || exit + +prefix="@prefix@" +NUT_DIR="@prefix@" +NUT_SBIN_DIR="${NUT_DIR}/sbin" +NUT_LIB_DIR="${NUT_DIR}/lib" +NUT_RUN_DIR="@PIDPATH@/nut" +CONFIG="@CONFPATH@/nut.conf" +NUTUSER="@RUN_AS_USER@" +NUTGROUP="@RUN_AS_GROUP@" + +if [ -f "$CONFIG" ] ; then + . "$CONFIG" +fi + +ups_start () { + if [ "$MODE" = "none" ];then + echo "No NUT mode set, not starting anything" >&2 + exit $SMF_EXIT_ERR_CONFIG + fi + + # Default rights inspired by NUT scripts/Solaris/postinstall.in + mkdir -p "$NUT_RUN_DIR" && \ + chown "root:$NUTGROUP" "$NUT_RUN_DIR" && \ + chmod 770 "$NUT_RUN_DIR" \ + || exit $SMF_EXIT_ERR_FATAL + + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}"/upsmon #> /dev/null 2>&1 +} + +case "$1" in +'start') + ups_start + ;; + +*) + echo "" + echo "Usage: '$0' {start}" + echo "" + exit $SMF_EXIT_ERR_CONFIG + ;; +esac + +exit $? diff --git a/scripts/Solaris/svc-nut-server.in b/scripts/Solaris/svc-nut-server.in new file mode 100755 index 0000000..5fee9c1 --- /dev/null +++ b/scripts/Solaris/svc-nut-server.in @@ -0,0 +1,64 @@ +#!/sbin/sh + +# Trivial (better is yet to come) SMF method script to start nut services +# Adapted for OpenIndiana userland from init.d script template in NUT sources +# Adaptation copyright (C) 2016 Jim Klimov + +if [ -z "$SMF_FMRI" ]; then + echo "$0 must be called in SMF context!" >&2 + exit 1 +fi + +# smf(5) +. /lib/svc/share/smf_include.sh || exit + +prefix="@prefix@" +NUT_DIR="@prefix@" +NUT_SBIN_DIR="$NUT_DIR/sbin" +NUT_LIB_DIR="${NUT_DIR}/lib" +NUT_RUN_DIR="@PIDPATH@/nut" +CONFIG="@CONFPATH@/nut.conf" +NUTUSER="@RUN_AS_USER@" +NUTGROUP="@RUN_AS_GROUP@" + +if [ -f "$CONFIG" ] ; then + . "$CONFIG" +fi + +ups_start () { + # Default rights inspired by NUT scripts/Solaris/postinstall.in + mkdir -p "$NUT_RUN_DIR" && \ + chown "root:$NUTGROUP" "$NUT_RUN_DIR" && \ + chmod 770 "$NUT_RUN_DIR" \ + || exit $SMF_EXIT_ERR_FATAL + + if [ "$MODE" = "none" ];then + echo "No NUT mode set, not starting anything" >&2 + exit 1 + fi + + if [ "$MODE" != "netclient" ] ; then + # In this distribution, UPS drivers are wrapped by service instances + #LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsdrvctl" start #> /dev/null 2>&1 + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsd" #> /dev/null 2>&1 + fi +} + +case "$1" in +'start') + ups_start + ;; + +'refresh'|'reload') + LD_LIBRARY_PATH="${NUT_LIB_DIR}:$LD_LIBRARY_PATH" "${NUT_SBIN_DIR}/upsd" -c reload + ;; + +*) + echo "" + echo "Usage: '$0' {start}" + echo "" + exit $SMF_EXIT_ERR_CONFIG + ;; +esac + +exit $? diff --git a/scripts/augeas/Makefile.am b/scripts/augeas/Makefile.am index c387541..b9b8c8e 100644 --- a/scripts/augeas/Makefile.am +++ b/scripts/augeas/Makefile.am @@ -1,16 +1,49 @@ -EXTRA_DIST = gen-nutupsconf-aug.py nutupsconf.aug.tpl \ +EXTRA_DIST = gen-nutupsconf-aug.py.in nutupsconf.aug.tpl \ README tests/test_nut.aug +PYTHON = @PYTHON@ + # only call the script to generate Augeas ups.conf lens upon "make dist", -# and if Python is present +# and if Python is present; the distributed gen-nutupsconf-aug.py.in template +# is assumed to only differ from a generated gen-nutupsconf-aug.py by the +# @PYTHON@ shebang. dist-hook: - @if python -c pass; then \ - echo "Regenerating Augeas ups.conf lens."; \ - $(distdir)/gen-nutupsconf-aug.py $(distdir)/; \ + @if [ -n "$(PYTHON)" ] && $(PYTHON) -c "import re,glob,codecs"; then \ + echo "Regenerating Augeas ups.conf lens with '$(PYTHON)'."; \ + $(PYTHON) $(distdir)/gen-nutupsconf-aug.py.in $(distdir)/; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Python is not available."; \ - echo "Skipping Augeas ups.conf lens regeneration."; \ + echo "Skipping regeneration of Augeas lens for ups.conf parsing." ; \ echo "----------------------------------------------------------------------"; \ fi + +# This needs augparse from augeas-tools +if HAVE_AUGPARSE +check-local: + @echo "augparse proceeding to lenses verification job..."; \ + echo "DISABLED for now due to https://github.com/networkupstools/nut/issues/657" +endif +# FIXME +# augparse -I $(srcdir)/ $(srcdir)/tests/test_nut.aug + +if WITH_AUGLENS +# Now "make install" should cover delivery of Augeas lenses... +# The "auglensdir" value should be set up by configure +# The *.aug files are generated by rule above or by autogen.sh and/or configure + auglens_DATA = \ + nuthostsconf.aug nutupsconf.aug nutupsdusers.aug nutupsschedconf.aug \ + nutnutconf.aug nutupsdconf.aug nutupsmonconf.aug nutupssetconf.aug +endif + +MAINTAINERCLEANFILES = Makefile.in .dirstamp +CLEANFILES = *-spellchecked + +# Can be re-generated by configure script and/or make: +DISTCLEANFILES = gen-nutupsconf-aug.py + +# Generated by autogen.sh and needed to run the configure script: +MAINTAINERCLEANFILES += nutupsconf.aug.in + +DISTCLEANFILES += *.aug diff --git a/scripts/augeas/Makefile.in b/scripts/augeas/Makefile.in index 6cb69c7..d427b5c 100644 --- a/scripts/augeas/Makefile.in +++ b/scripts/augeas/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -14,7 +13,63 @@ # PARTICULAR PURPOSE. @SET_MAKE@ + VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -35,51 +90,114 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = scripts/augeas -DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ - $(srcdir)/nuthostsconf.aug.in $(srcdir)/nutnutconf.aug.in \ - $(srcdir)/nutupsconf.aug.in $(srcdir)/nutupsdconf.aug.in \ - $(srcdir)/nutupsdusers.aug.in $(srcdir)/nutupsmonconf.aug.in \ - $(srcdir)/nutupsschedconf.aug.in \ - $(srcdir)/nutupssetconf.aug.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h -CONFIG_CLEAN_FILES = nutnutconf.aug nutupsconf.aug nutupsdconf.aug \ - nutupsdusers.aug nutupsmonconf.aug nutupsschedconf.aug \ - nuthostsconf.aug nutupssetconf.aug +CONFIG_CLEAN_FILES = nutnutconf.aug nutupsdconf.aug nutupsdusers.aug \ + nutupsmonconf.aug nutupsschedconf.aug nuthostsconf.aug \ + nutupssetconf.aug gen-nutupsconf-aug.py nutupsconf.aug CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(auglensdir)" +DATA = $(auglens_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(srcdir)/gen-nutupsconf-aug.py.in \ + $(srcdir)/nuthostsconf.aug.in $(srcdir)/nutnutconf.aug.in \ + $(srcdir)/nutupsconf.aug.in $(srcdir)/nutupsdconf.aug.in \ + $(srcdir)/nutupsdusers.aug.in $(srcdir)/nutupsmonconf.aug.in \ + $(srcdir)/nutupsschedconf.aug.in \ + $(srcdir)/nutupssetconf.aug.in README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -90,16 +208,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -108,11 +235,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -122,14 +246,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -140,21 +265,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -168,35 +302,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -207,8 +352,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -227,18 +376,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -248,9 +400,26 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ -EXTRA_DIST = gen-nutupsconf-aug.py nutupsconf.aug.tpl \ +EXTRA_DIST = gen-nutupsconf-aug.py.in nutupsconf.aug.tpl \ README tests/test_nut.aug +# FIXME +# augparse -I $(srcdir)/ $(srcdir)/tests/test_nut.aug + +# Now "make install" should cover delivery of Augeas lenses... +# The "auglensdir" value should be set up by configure +# The *.aug files are generated by rule above or by autogen.sh and/or configure +@WITH_AUGLENS_TRUE@auglens_DATA = \ +@WITH_AUGLENS_TRUE@ nuthostsconf.aug nutupsconf.aug nutupsdusers.aug nutupsschedconf.aug \ +@WITH_AUGLENS_TRUE@ nutnutconf.aug nutupsdconf.aug nutupsmonconf.aug nutupssetconf.aug + + +# Generated by autogen.sh and needed to run the configure script: +MAINTAINERCLEANFILES = Makefile.in .dirstamp nutupsconf.aug.in +CLEANFILES = *-spellchecked + +# Can be re-generated by configure script and/or make: +DISTCLEANFILES = gen-nutupsconf-aug.py *.aug all: all-am .SUFFIXES: @@ -266,14 +435,13 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/augeas/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu scripts/augeas/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -286,8 +454,6 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): nutnutconf.aug: $(top_builddir)/config.status $(srcdir)/nutnutconf.aug.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ -nutupsconf.aug: $(top_builddir)/config.status $(srcdir)/nutupsconf.aug.in - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ nutupsdconf.aug: $(top_builddir)/config.status $(srcdir)/nutupsdconf.aug.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ nutupsdusers.aug: $(top_builddir)/config.status $(srcdir)/nutupsdusers.aug.in @@ -300,20 +466,48 @@ nuthostsconf.aug: $(top_builddir)/config.status $(srcdir)/nuthostsconf.aug.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ nutupssetconf.aug: $(top_builddir)/config.status $(srcdir)/nutupssetconf.aug.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +gen-nutupsconf-aug.py: $(top_builddir)/config.status $(srcdir)/gen-nutupsconf-aug.py.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nutupsconf.aug: $(top_builddir)/config.status $(srcdir)/nutupsconf.aug.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -tags: TAGS -TAGS: +install-auglensDATA: $(auglens_DATA) + @$(NORMAL_INSTALL) + @list='$(auglens_DATA)'; test -n "$(auglensdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(auglensdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(auglensdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(auglensdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(auglensdir)" || exit $$?; \ + done -ctags: CTAGS -CTAGS: +uninstall-auglensDATA: + @$(NORMAL_UNINSTALL) + @list='$(auglens_DATA)'; test -n "$(auglensdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(auglensdir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -346,10 +540,15 @@ distdir: $(DISTFILES) $(MAKE) $(AM_MAKEFLAGS) \ top_distdir="$(top_distdir)" distdir="$(distdir)" \ dist-hook +@HAVE_AUGPARSE_FALSE@check-local: check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) check-local check: check-am -all-am: Makefile +all-am: Makefile $(DATA) installdirs: + for dir in "$(DESTDIR)$(auglensdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done install: install-am install-exec: install-exec-am install-data: install-data-am @@ -360,21 +559,29 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am @@ -395,7 +602,7 @@ info: info-am info-am: -install-data-am: +install-data-am: install-auglensDATA install-dvi: install-dvi-am @@ -439,36 +646,46 @@ ps: ps-am ps-am: -uninstall-am: +uninstall-am: uninstall-auglensDATA -.MAKE: install-am install-strip +.MAKE: check-am install-am install-strip -.PHONY: all all-am check check-am clean clean-generic clean-libtool \ - dist-hook distclean distclean-generic distclean-libtool \ - distdir dvi dvi-am html html-am info info-am install \ - install-am install-data install-data-am install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-ps install-ps-am \ - install-strip installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - uninstall uninstall-am +.PHONY: all all-am check check-am check-local clean clean-generic \ + clean-libtool cscopelist-am ctags-am dist-hook distclean \ + distclean-generic distclean-libtool distdir dvi dvi-am html \ + html-am info info-am install install-am install-auglensDATA \ + install-data install-data-am install-dvi install-dvi-am \ + install-exec install-exec-am install-html install-html-am \ + install-info install-info-am install-man install-pdf \ + install-pdf-am install-ps install-ps-am install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ + uninstall-am uninstall-auglensDATA + +.PRECIOUS: Makefile # only call the script to generate Augeas ups.conf lens upon "make dist", -# and if Python is present +# and if Python is present; the distributed gen-nutupsconf-aug.py.in template +# is assumed to only differ from a generated gen-nutupsconf-aug.py by the +# @PYTHON@ shebang. dist-hook: - @if python -c pass; then \ - echo "Regenerating Augeas ups.conf lens."; \ - $(distdir)/gen-nutupsconf-aug.py $(distdir)/; \ + @if [ -n "$(PYTHON)" ] && $(PYTHON) -c "import re,glob,codecs"; then \ + echo "Regenerating Augeas ups.conf lens with '$(PYTHON)'."; \ + $(PYTHON) $(distdir)/gen-nutupsconf-aug.py.in $(distdir)/; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Python is not available."; \ - echo "Skipping Augeas ups.conf lens regeneration."; \ + echo "Skipping regeneration of Augeas lens for ups.conf parsing." ; \ echo "----------------------------------------------------------------------"; \ fi +# This needs augparse from augeas-tools +@HAVE_AUGPARSE_TRUE@check-local: +@HAVE_AUGPARSE_TRUE@ @echo "augparse proceeding to lenses verification job..."; \ +@HAVE_AUGPARSE_TRUE@ echo "DISABLED for now due to https://github.com/networkupstools/nut/issues/657" + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. .NOEXPORT: diff --git a/scripts/augeas/README b/scripts/augeas/README index 3be147e..bfa03d3 100644 --- a/scripts/augeas/README +++ b/scripts/augeas/README @@ -41,7 +41,7 @@ As an example, on Debian and derivatives, do the following: $ apt-get install augeas-lenses augeas-tools -And optionaly: +And optionally: $ apt-get install libaugeas0 libaugeas-dev python-augeas @@ -57,7 +57,7 @@ These are the *.aug files in the present directory. You can either install the files to the right location on your system, generally in '/usr/share/augeas/lenses/', or use these from NUT -source directory ('nut/scripts/augeas'). The latter is to be prefered for +source directory ('nut/scripts/augeas'). The latter is to be preferred for the time being. @@ -240,7 +240,7 @@ a.set(("/files/etc/nut/upsd.users/%s/instcmds" % user), "ALL") monuser = "monuser" monpasswd = "******" a.set(("/files/etc/nut/upsd.users/%s/password" % monuser), monpasswd) -a.set(("/files/etc/nut/upsd.users/%s/upsmon" % monuser), "master") +a.set(("/files/etc/nut/upsd.users/%s/upsmon" % monuser), "primary") # Generate upsmon.conf a.set("/files/etc/nut/upsmon.conf/MONITOR/system/upsname", device_name) @@ -250,7 +250,7 @@ a.set("/files/etc/nut/upsmon.conf/MONITOR/system/upsname", device_name) a.set("/files/etc/nut/upsmon.conf/MONITOR/powervalue", "1") a.set("/files/etc/nut/upsmon.conf/MONITOR/username", monuser) a.set("/files/etc/nut/upsmon.conf/MONITOR/password", monpasswd) -a.set("/files/etc/nut/upsmon.conf/MONITOR/type", "master") +a.set("/files/etc/nut/upsmon.conf/MONITOR/type", "primary") # FIXME: glitch on the generated content a.set("/files/etc/nut/upsmon.conf/SHUTDOWNCMD", "/sbin/shutdown -h +0") diff --git a/scripts/augeas/gen-nutupsconf-aug.py b/scripts/augeas/gen-nutupsconf-aug.py.in similarity index 96% rename from scripts/augeas/gen-nutupsconf-aug.py rename to scripts/augeas/gen-nutupsconf-aug.py.in index 55db17a..04b6fa1 100755 --- a/scripts/augeas/gen-nutupsconf-aug.py +++ b/scripts/augeas/gen-nutupsconf-aug.py.in @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!@PYTHON@ # Copyright (C) 2010 - Arnaud Quette # # This program is free software; you can redistribute it and/or modify @@ -18,6 +18,8 @@ # This program extracts all drivers specific variables, declared # using 'addvar()' and output a complete ups.conf lens for Augeas +from __future__ import print_function + import sys import re import glob @@ -57,11 +59,11 @@ def grep(string,list): if __name__ == '__main__': rawCount = 0 - global finalCount + #global finalCount variableNames = [] specificVars = "" - global inLensContent - global finalLensContent + #global inLensContent + #global finalLensContent Exceptionlist = ['../../drivers/main.c', '../../drivers/skel.c'] outputFilename = 'nutupsconf.aug.in' templateFilename = 'nutupsconf.aug.tpl' @@ -69,7 +71,7 @@ if __name__ == '__main__': if (len(sys.argv) == 2): dirPrefix = sys.argv[1] - print dirPrefix + print(dirPrefix) # 1/ Extract all specific drivers parameters, in a sorted list with unique entries # 1.1/ List all drivers implementation files diff --git a/scripts/augeas/nutupsconf.aug.in b/scripts/augeas/nutupsconf.aug.in index d3199bc..3c921f2 100644 --- a/scripts/augeas/nutupsconf.aug.in +++ b/scripts/augeas/nutupsconf.aug.in @@ -33,7 +33,10 @@ let ups_sep = IniFile.sep IniFile.sep_re IniFile.sep_default let ups_global = "chroot" | "driverpath" | "maxstartdelay" + | "maxretry" + | "retrydelay" | "pollinterval" + | "synchronous" | "user" let ups_fields = "driver" @@ -41,44 +44,107 @@ let ups_fields = "driver" | "sdorder" | "desc" | "nolock" + | "ignorelb" | "maxstartdelay" + | "synchronous" + | "BYPASS" + | "CHRG_addr" + | "CHRG_noro" + | "CHRG_regtype" | "CP" | "CS" + | "Could not addvar(%s)" + | "DISCHRG_addr" + | "DISCHRG_noro" + | "DISCHRG_regtype" + | "FSD_addr" + | "FSD_noro" + | "FSD_pulse_duration" + | "FSD_regtype" + | "HB_addr" + | "HB_noro" + | "HB_regtype" | "ID" | "LB" + | "LB_addr" + | "LB_noro" + | "LB_regtype" | "LowBatt" + | "OB_addr" + | "OB_noro" + | "OB_regtype" | "OL" + | "OL_addr" + | "OL_noro" + | "OL_regtype" | "OffDelay" | "OnDelay" + | "RB" + | "RB_addr" + | "RB_noro" + | "RB_regtype" | "SD" + | "advanced_eco_mode" | "advorder" + | "alarm_control" | "authPassword" | "authProtocol" | "authtype" | "awd" | "batteryPercentage" + | "battery_alarm" + | "battery_max" + | "battery_min" + | "battery_number" + | "battery_open_status_check" | "battext" + | "battvoltmult" | "baud_rate" | "baudrate" | "bus" + | "bypass_alarm" + | "bypass_forbidding" + | "bypass_when_off" | "cable" | "cablepower" | "chargetime" | "community" + | "constant_phase_angle" + | "converter_mode" + | "cshdelay" | "daysoff" | "daysweek" + | "dev_slave_id" + | "device" + | "device_mfr" + | "device_model" + | "do_convert_deci" | "dumbterm" + | "eco_mode" | "explore" | "fake_lowbatt" + | "fault_1" + | "fault_2" + | "fault_3" + | "fault_4" + | "fault_5" | "flash" | "frequency" | "fruid" | "full_update" + | "hb" | "houroff" | "houron" + | "i2c_address" | "idleload" + | "ignoresab" + | "input_fault_voltage" | "input_timeout" + | "interruptonly" + | "interruptsize" | "langid_fix" + | "lb" + | "limited_runtime_on_battery" | "linevoltage" | "load.off" | "load.on" @@ -87,27 +153,45 @@ let ups_fields = "driver" | "login" | "lowbatt" | "manufacturer" + | "max_bypass_freq" + | "max_bypass_volt" | "max_load" + | "max_polls_without_data" + | "maxreport" | "methodOfFlowControl" | "mfr" | "mibs" + | "min_bypass_freq" + | "min_bypass_volt" | "mincharge" | "minruntime" + | "mod_byte_to_s" + | "mod_byte_to_us" + | "mod_resp_to_s" + | "mod_resp_to_us" | "model" | "modelname" + | "nobt" | "nohang" | "nombattvolt" + | "nominal_cell_voltage" | "norating" + | "noscanlangid" | "notification" | "notransferoids" | "novendor" | "nowarn_noimp" | "numOfBytesFromUPS" + | "number_of_battery_cells" | "offdelay" | "oldmac" | "ondelay" + | "onlinedischarge" | "output_pace" + | "output_phase_angle" + | "output_voltages" | "password" + | "pins_shutdown_mode" | "pollfreq" | "pollonly" | "powerup" @@ -119,28 +203,49 @@ let ups_fields = "driver" | "productid" | "protocol" | "rebootdelay" + | "recharge_time" + | "reset_to_default" + | "rio_slave_id" + | "runtime_full" + | "runtime_half" | "runtimecal" | "sdtime" | "sdtype" | "secLevel" | "secName" - | "sensorid" + | "semistaticfreq" + | "ser_baud_rate" + | "ser_data_bit" + | "ser_parity" + | "ser_stop_bit" | "serial" | "serialnumber" + | "series" | "shutdownArguments" | "shutdown_delay" + | "shutdown_duration" + | "shutdown_timer" | "silent" + | "site_fault_detection" + | "slave_address" + | "snmp_retries" + | "snmp_timeout" | "snmp_version" | "startdelay" | "status_only" + | "stayoff" | "subdriver" | "subscribe" + | "symmetrathreephase" + | "testing" | "testtime" | "timeout" + | "ttymode" | "type" | "ups.delay.shutdown" | "ups.delay.start" | "upstype" + | "usb_set_altinterface" | "usd" | "use_crlf" | "use_pre_lf" @@ -150,6 +255,7 @@ let ups_fields = "driver" | "vendorid" | "voltage" | "wait" + | "work_range_type" | "wugrace" diff --git a/scripts/augeas/nutupsconf.aug.tpl b/scripts/augeas/nutupsconf.aug.tpl index 24f6bcc..0f7c66c 100644 --- a/scripts/augeas/nutupsconf.aug.tpl +++ b/scripts/augeas/nutupsconf.aug.tpl @@ -33,7 +33,10 @@ let ups_sep = IniFile.sep IniFile.sep_re IniFile.sep_default let ups_global = "chroot" | "driverpath" | "maxstartdelay" + | "maxretry" + | "retrydelay" | "pollinterval" + | "synchronous" | "user" let ups_fields = "driver" @@ -41,7 +44,9 @@ let ups_fields = "driver" | "sdorder" | "desc" | "nolock" + | "ignorelb" | "maxstartdelay" + | "synchronous" @SPECIFIC_DRV_VARS@ let ups_entry = IniFile.indented_entry (ups_global|ups_fields) ups_sep ups_comment diff --git a/scripts/augeas/nutupsdconf.aug.in b/scripts/augeas/nutupsdconf.aug.in index d3aef1d..35bf896 100644 --- a/scripts/augeas/nutupsdconf.aug.in +++ b/scripts/augeas/nutupsdconf.aug.in @@ -39,6 +39,8 @@ let comment = Util.comment let path = word let upsd_maxage = [ opt_spc . key "MAXAGE" . sep_spc . store num . eol ] +let upsd_trackingdelay = [ opt_spc . key "TRACKINGDELAY" . sep_spc . store num . eol ] +let upsd_allow_no_device = [ opt_spc . key "ALLOW_NO_DEVICE" . sep_spc . store num . eol ] let upsd_statepath = [ opt_spc . key "STATEPATH" . sep_spc . store path . eol ] let upsd_listen = [ opt_spc . key "LISTEN" . sep_spc . [ label "interface" . store ip ] @@ -49,13 +51,15 @@ let upsd_certfile = [ opt_spc . key "CERTFILE" . sep_spc . store path . eol ] (************************************************************************ * MAXAGE seconds + * TRACKINGDELAY seconds + * ALLOW_NO_DEVICE Boolean * STATEPATH path * LISTEN interface port * Multiple LISTEN addresses may be specified. The default is to bind to 0.0.0.0 if no LISTEN addresses are specified. * LISTEN 127.0.0.1 LISTEN 192.168.50.1 LISTEN ::1 LISTEN 2001:0db8:1234:08d3:1319:8a2e:0370:7344 * *************************************************************************) -let upsd_other = upsd_maxage | upsd_statepath | upsd_listen_list | upsd_maxconn | upsd_certfile +let upsd_other = upsd_maxage | upsd_trackingdelay | upsd_allow_no_device | upsd_statepath | upsd_listen_list | upsd_maxconn | upsd_certfile let upsd_lns = (upsd_other|comment|empty)* diff --git a/scripts/augeas/nutupsdusers.aug.in b/scripts/augeas/nutupsdusers.aug.in index 242e64e..a138b61 100644 --- a/scripts/augeas/nutupsdusers.aug.in +++ b/scripts/augeas/nutupsdusers.aug.in @@ -67,7 +67,7 @@ let upsd_users_instcmds = [ del_spc let upsd_users_upsmon = [ del_spc . key "upsmon" . sep_spc - . store /master|slave/ . eol ] + . store /master|primary|slave|secondary/ . eol ] let upsd_users_title = IniFile.indented_title IniFile.record_re diff --git a/scripts/augeas/tests/test_nut.aug b/scripts/augeas/tests/test_nut.aug index 4153ed7..084fd84 100644 --- a/scripts/augeas/tests/test_nut.aug +++ b/scripts/augeas/tests/test_nut.aug @@ -27,6 +27,8 @@ test NutUpsConf.ups_lns get ups_conf = let upsd_conf = " MAXAGE 30 +TRACKINGDELAY 600 +ALLOW_NO_DEVICE 1 LISTEN 0.0.0.0 3493 MAXCONN 1024 " @@ -34,6 +36,8 @@ MAXCONN 1024 test NutUpsdConf.upsd_lns get upsd_conf = { } { "MAXAGE" = "30" } + { "TRACKINGDELAY" = "600" } + { "ALLOW_NO_DEVICE" = "1" } { "LISTEN" { "interface" = "0.0.0.0" } { "port" = "3493" } } @@ -50,13 +54,13 @@ let upsd_users = " instcmds = test.panel.start instcmds = test.panel.stop - [monmaster] + [upswired] password = blah - upsmon master + upsmon primary - [monslave] + [observer] password = abcd - upsmon slave + upsmon secondary " test NutUpsdUsers.upsd_users_lns get upsd_users = @@ -73,16 +77,16 @@ test NutUpsdUsers.upsd_users_lns get upsd_users = { "instcmds" = "test.panel.start" } { "instcmds" = "test.panel.stop" } { } } - { "monmaster" + { "upswired" { "password" = "blah" } - { "upsmon" = "master" } + { "upsmon" = "primary" } { } } - { "monslave" + { "observer" { "password" = "abcd" } - { "upsmon" = "slave" } } + { "upsmon" = "secondary" } } let upsmon_conf = " -MONITOR testups@localhost 1 monmaster blah master +MONITOR testups@localhost 1 upswired blah primary MINSUPPLIES 1 SHUTDOWNCMD /sbin/shutdown -h +0 @@ -103,9 +107,9 @@ test NutUpsmonConf.upsmon_lns get upsmon_conf = { "upsname" = "testups" } { "hostname" = "localhost" } } { "powervalue" = "1" } - { "username" = "monmaster" } + { "username" = "upswired" } { "password" = "blah" } - { "type" = "master" } } + { "type" = "primary" } } { } { "MINSUPPLIES" = "1" } { "SHUTDOWNCMD" = "/sbin/shutdown -h +0" } diff --git a/scripts/devd/Makefile.am b/scripts/devd/Makefile.am new file mode 100644 index 0000000..c1c8097 --- /dev/null +++ b/scripts/devd/Makefile.am @@ -0,0 +1,26 @@ + +if WITH_DEVD + devdconfdir = $(devddir) + devdconf_DATA = +if WITH_USB + devdconf_DATA += nut-usb.conf +endif +endif + +EXTRA_DIST = README + +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +# Generated by configure script: +DISTCLEANFILES = nut-usb.conf + +# we should never remove this one, apart from a distclean-check +#MAINTAINERCLEANFILES = nut-usbups.rules.in + +# Generated by autogen.sh and needed to run the configure script +# (technically, generated by tools/nut-usbinfo.pl script among +# GENERATED_USB_OS_FILES): +MAINTAINERCLEANFILES += nut-usbups.rules.in +MAINTAINERCLEANFILES += nut-usbups.rules.in.AUTOGEN_WITHOUT +MAINTAINERCLEANFILES += nut-usb.conf.in +MAINTAINERCLEANFILES += nut-usb.conf.in.AUTOGEN_WITHOUT diff --git a/scripts/devd/Makefile.in b/scripts/devd/Makefile.in new file mode 100644 index 0000000..ae87b77 --- /dev/null +++ b/scripts/devd/Makefile.in @@ -0,0 +1,639 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +@WITH_DEVD_TRUE@@WITH_USB_TRUE@am__append_1 = nut-usb.conf +subdir = scripts/devd +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/nut_arg_with.m4 \ + $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ + $(top_srcdir)/m4/nut_check_libavahi.m4 \ + $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ + $(top_srcdir)/m4/nut_check_libgd.m4 \ + $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ + $(top_srcdir)/m4/nut_check_libneon.m4 \ + $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ + $(top_srcdir)/m4/nut_check_libpowerman.m4 \ + $(top_srcdir)/m4/nut_check_libusb.m4 \ + $(top_srcdir)/m4/nut_check_libwrap.m4 \ + $(top_srcdir)/m4/nut_check_os.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ + $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ + $(top_srcdir)/m4/nut_type_socklen_t.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/include/config.h +CONFIG_CLEAN_FILES = nut-usb.conf +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(devdconfdir)" +DATA = $(devdconf_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/nut-usb.conf.in \ + README +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +A2X = @A2X@ +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BINDIR = @BINDIR@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONFPATH = @CONFPATH@ +CPP = @CPP@ +CPPCHECK = @CPPCHECK@ +CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DBLATEX = @DBLATEX@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ +DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ +DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ +DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ +LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ +LIBGD_CFLAGS = @LIBGD_CFLAGS@ +LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ +LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ +LIBIPMI_LIBS = @LIBIPMI_LIBS@ +LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ +LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ +LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ +LIBNEON_LIBS = @LIBNEON_LIBS@ +LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ +LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ +LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ +LIBS = @LIBS@ +LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ +LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ +LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ +LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ +LIBUSB_LIBS = @LIBUSB_LIBS@ +LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ +LIBWRAP_LIBS = @LIBWRAP_LIBS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LN_S_R = @LN_S_R@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OS_NAME = @OS_NAME@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ +RANLIB = @RANLIB@ +RUN_AS_GROUP = @RUN_AS_GROUP@ +RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ +SED = @SED@ +SERLIBS = @SERLIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ +STATEPATH = @STATEPATH@ +STRIP = @STRIP@ +SUN_LIBUSB = @SUN_LIBUSB@ +TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +auglensdir = @auglensdir@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +cgiexecdir = @cgiexecdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +devddir = @devddir@ +docdir = @docdir@ +driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +hotplugdir = @hotplugdir@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +now = @now@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +systemdshutdowndir = @systemdshutdowndir@ +systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +udevdir = @udevdir@ +@WITH_DEVD_TRUE@devdconfdir = $(devddir) +@WITH_DEVD_TRUE@devdconf_DATA = $(am__append_1) +EXTRA_DIST = README + +# we should never remove this one, apart from a distclean-check +#MAINTAINERCLEANFILES = nut-usbups.rules.in + +# Generated by autogen.sh and needed to run the configure script +# (technically, generated by tools/nut-usbinfo.pl script among +# GENERATED_USB_OS_FILES): +MAINTAINERCLEANFILES = Makefile.in .dirstamp nut-usbups.rules.in \ + nut-usbups.rules.in.AUTOGEN_WITHOUT nut-usb.conf.in \ + nut-usb.conf.in.AUTOGEN_WITHOUT + +# Generated by configure script: +DISTCLEANFILES = nut-usb.conf +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/devd/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu scripts/devd/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): +nut-usb.conf: $(top_builddir)/config.status $(srcdir)/nut-usb.conf.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +install-devdconfDATA: $(devdconf_DATA) + @$(NORMAL_INSTALL) + @list='$(devdconf_DATA)'; test -n "$(devdconfdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(devdconfdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(devdconfdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(devdconfdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(devdconfdir)" || exit $$?; \ + done + +uninstall-devdconfDATA: + @$(NORMAL_UNINSTALL) + @list='$(devdconf_DATA)'; test -n "$(devdconfdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(devdconfdir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(DATA) +installdirs: + for dir in "$(DESTDIR)$(devdconfdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-devdconfDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-devdconfDATA + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-devdconfDATA install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ + uninstall-am uninstall-devdconfDATA + +.PRECIOUS: Makefile + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/scripts/devd/README b/scripts/devd/README new file mode 100644 index 0000000..58f9fda --- /dev/null +++ b/scripts/devd/README @@ -0,0 +1,11 @@ +On FreeBSD, devd has a similar role to udev on Linux. The devd.conf file +defines actions to perform when devices are plugged in. + +The tools/nut-usbinfo.pl script generates nut-usb.conf.in by processing USB +macros in all of the drivers. In this case, the defined action for each +matching UPS is to change the permissions such that the NUT drivers can access +the devices without requiring root privileges. You may need to restart devd and +re-plug in the UPS to trigger the actions. + +The format of this configuration file should work with devd on FreeBSD 9.0 and +9.1, at the very least. diff --git a/scripts/devd/nut-usb.conf.in b/scripts/devd/nut-usb.conf.in new file mode 100644 index 0000000..c3c1e6c --- /dev/null +++ b/scripts/devd/nut-usb.conf.in @@ -0,0 +1,1131 @@ +# This file is generated and installed by the Network UPS Tools package. +# Homepage: http://www.networkupstools.org/ + +# SNR-UPS-LID-XXXX UPSes - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0001"; + match "product" "0x0000"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Hewlett Packard +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x0001"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# T500 - bcmxcp_usb +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1f01"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# T750 - bcmxcp_usb +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1f02"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# HP T750 INTL - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1f06"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# HP T1000 INTL - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1f08"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# HP T1500 INTL - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1f09"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# HP R/T 2200 INTL (like SMART2200RMXL2U) - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1f0a"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# HP R1500 G2 and G3 INTL - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1fe0"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# HP T750 G2 - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1fe1"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1fe2"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# HP T1500 G3 - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1fe3"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# R/T3000 - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1fe5"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# R/T3000 - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1fe6"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# various models - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1fe7"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# various models - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x03f0"; + match "product" "0x1fe8"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Eaton +# various models - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0463"; + match "product" "0x0001"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# various models - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0463"; + match "product" "0xffff"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Dell +# various models - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x047c"; + match "product" "0xffff"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# ST Microelectronics +# TS Shara UPSes; vendor ID 0x0483 is from ST Microelectronics - with product IDs delegated to different OEMs - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0483"; + match "product" "0x0035"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# USB IDs device table - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0483"; + match "product" "0xa113"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# IBM +# 6000 VA LCD 4U Rack UPS; 5396-1Kx - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04b3"; + match "product" "0x0001"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Riello (Cypress Semiconductor Corp.) +# various models - riello_usb +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04b4"; + match "product" "0x5500"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Minibox +# openUPS Intelligent UPS (minimum required firmware 1.4) - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04d8"; + match "product" "0xd004"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# openUPS Intelligent UPS (minimum required firmware 1.4) - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x04d8"; + match "product" "0xd005"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Belkin +# F6H375-USB - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x050d"; + match "product" "0x0375"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# F6C550-AVR - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x050d"; + match "product" "0x0551"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# F6C1250-TW-RK - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x050d"; + match "product" "0x0750"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# F6C1500-TW-RK - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x050d"; + match "product" "0x0751"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# F6C900-UNV - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x050d"; + match "product" "0x0900"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# F6C100-UNV - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x050d"; + match "product" "0x0910"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# F6C120-UNV - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x050d"; + match "product" "0x0912"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# F6C800-UNV - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x050d"; + match "product" "0x0980"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Regulator PRO-USB - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x050d"; + match "product" "0x0f51"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# F6C1100-UNV, F6C1200-UNV - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x050d"; + match "product" "0x1100"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# APC +# APC AP9584 Serial->USB kit - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x051d"; + match "product" "0x0000"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# various models - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x051d"; + match "product" "0x0002"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# various 5G models - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x051d"; + match "product" "0x0003"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Powerware +# various models - bcmxcp_usb +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0592"; + match "product" "0x0002"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# PW 9140 - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0592"; + match "product" "0x0004"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Agiler UPS - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x05b8"; + match "product" "0x0000"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Delta UPS +# Delta UPS Amplon R Series, Single Phase UPS, 1/2/3 kVA - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x05dd"; + match "product" "0x041b"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Delta/Minuteman Enterprise Plus E1500RM2U - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x05dd"; + match "product" "0xa011"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Belkin F6C1200-UNV/Voltronic Power UPSes - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0665"; + match "product" "0x5161"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Phoenixtec Power Co., Ltd +# Online Yunto YQ450 - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x06da"; + match "product" "0x0002"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Mustek Powermust - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x06da"; + match "product" "0x0003"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Phoenixtec Innova 3/1 T - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x06da"; + match "product" "0x0004"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Phoenixtec Innova RT - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x06da"; + match "product" "0x0005"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Phoenixtec Innova T - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x06da"; + match "product" "0x0201"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Online Zinto A - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x06da"; + match "product" "0x0601"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# PROTECT B / NAS - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x06da"; + match "product" "0xffff"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# iDowell +# iDowell - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x075d"; + match "product" "0x0300"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Cyber Power Systems +# 900AVR/BC900D - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0764"; + match "product" "0x0005"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Dynex DX-800U?, CP1200AVR/BC1200D, CP825AVR-G, CP1000AVRLCD, CP1000PFCLCD, CP1500C, CP550HG, etc. - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0764"; + match "product" "0x0501"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# OR2200LCDRM2U, OR700LCDRM1U, PR6000LCDRTXL5U - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0764"; + match "product" "0x0601"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Sweex 1000VA - richcomm_usb +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0925"; + match "product" "0x1234"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# TrippLite +# e.g. OMNIVS1000, SMART550USB, ... - tripplite_usb +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x0001"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite AVR550U - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x1003"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite AVR750U - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x1007"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite ECO550UPS - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x1008"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite ECO550UPS - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x1009"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite ECO550UPS - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x1010"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite SU3000LCD2UHV - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x1330"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite OMNI1000LCD - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x2005"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite OMNI900LCD - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x2007"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x2008"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite Smart1000LCD - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x2009"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x2010"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x2011"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x2012"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x2013"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x2014"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x3008"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x3009"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x3010"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x3011"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite smart2200RMXL2U - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x3012"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x3013"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x3014"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x3015"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite Smart1500LCD (newer unit) - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x3016"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite AVR750U (newer unit) - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x3024"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite SmartOnline SU1500RTXL2UA (older unit?) - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x4001"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite SmartOnline SU6000RT4U? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x4002"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite SmartOnline SU1500RTXL2ua - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x4003"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. TrippLite SmartOnline SU1000XLA - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x4004"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x4005"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x4006"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x4007"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# e.g. ? - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x09ae"; + match "product" "0x4008"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# PowerCOM +# PowerCOM Vanguard and BNT-xxxAP - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0d9f"; + match "product" "0x0001"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# PowerCOM Vanguard and BNT-xxxAP - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0d9f"; + match "product" "0x0004"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# PowerCOM IMP - IMPERIAL Series - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0d9f"; + match "product" "0x00a2"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# PowerCOM SKP - Smart KING Pro (all Smart series) - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0d9f"; + match "product" "0x00a3"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# PowerCOM WOW - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0d9f"; + match "product" "0x00a4"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# PowerCOM VGD - Vanguard - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0d9f"; + match "product" "0x00a5"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# PowerCOM BNT - Black Knight Pro - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0d9f"; + match "product" "0x00a6"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Unitek Alpha 1200Sx - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x0f03"; + match "product" "0x0001"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Liebert +# Liebert PowerSure PSA UPS - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x10af"; + match "product" "0x0001"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Liebert PowerSure PSI 1440 - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x10af"; + match "product" "0x0004"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Liebert GXT3 - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x10af"; + match "product" "0x0008"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# GE EP series - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x14f0"; + match "product" "0x00c9"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Legrand +# Legrand Keor SP - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x1cb0"; + match "product" "0x0032"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Legrand Daker DK / DK Plus - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x1cb0"; + match "product" "0x0035"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Legrand Keor PDU - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x1cb0"; + match "product" "0x0038"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Arduino +# Arduino Leonardo, Leonardo ETH and Pro Micro - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2341"; + match "product" "0x0036"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Arduino Leonardo, Leonardo ETH and Pro Micro - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2341"; + match "product" "0x8036"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Arduino +# Arduino Leonardo, Leonardo ETH and Pro Micro - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2A03"; + match "product" "0x0036"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Arduino Leonardo, Leonardo ETH and Pro Micro - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2A03"; + match "product" "0x0040"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Arduino Leonardo, Leonardo ETH and Pro Micro - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2A03"; + match "product" "0x8036"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Arduino Leonardo, Leonardo ETH and Pro Micro - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2A03"; + match "product" "0x8040"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# AEG +# PROTECT B / NAS - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2b2d"; + match "product" "0xffff"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Ever +# USB IDs device table - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2e51"; + match "product" "0x0000"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# USB IDs device table - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2e51"; + match "product" "0xffff"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Salicru +# SLC TWIN PRO2<=3KVA per https://github.com/networkupstools/nut/issues/450 - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2e66"; + match "product" "0x0201"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# SLC TWIN PRO2<=3KVA per https://github.com/networkupstools/nut/issues/450 - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2e66"; + match "product" "0x0202"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# SLC TWIN PRO2<=3KVA per https://github.com/networkupstools/nut/issues/450 - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2e66"; + match "product" "0x0203"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# https://www.salicru.com/sps-home.html - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x2e66"; + match "product" "0x0300"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; + +# Powervar +# Powervar - usbhid-ups +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0x4234"; + match "product" "0x0002"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; +# Ablerex 625L USB (Note: earlier best-fit was "krauler_subdriver" before PR #1135) - nutdrv_qx +notify 100 { + match "system" "USB"; + match "subsystem" "DEVICE"; + match "type" "ATTACH"; + match "vendor" "0xffff"; + match "product" "0x0000"; + action "chgrp @RUN_AS_GROUP@ /dev/$cdev; chmod g+rw /dev/$cdev"; +}; diff --git a/scripts/hal/Makefile.am b/scripts/hal/Makefile.am deleted file mode 100644 index f639b22..0000000 --- a/scripts/hal/Makefile.am +++ /dev/null @@ -1,16 +0,0 @@ - -if WITH_HAL - halfdidir = $(HAL_FDI_PATH) - halfdi_DATA = 20-ups-nut-device.fdi -endif - -# FIXME: should be able to use $< here. -20-ups-nut-device.fdi: ups-nut-device.fdi - cp ups-nut-device.fdi $@ - -EXTRA_DIST = ups-nut-device.fdi.in - -DISTCLEANFILES = ups-nut-device.fdi -# we should never remove this one, apart from a distclean-check -#MAINTAINERCLEANFILES = ups-nut-device.fdi.in -CLEANFILES = 20-ups-nut-device.fdi diff --git a/scripts/hal/ups-nut-device.fdi.in b/scripts/hal/ups-nut-device.fdi.in deleted file mode 100644 index 45294b0..0000000 --- a/scripts/hal/ups-nut-device.fdi.in +++ /dev/null @@ -1,601 +0,0 @@ - - - - - - - - battery - battery - hald-addon-blazer_usb - ups - - - - - - - - battery - battery - hald-addon-bcmxcp_usb - ups - - - - battery - battery - hald-addon-bcmxcp_usb - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - - - - - battery - battery - hald-addon-bcmxcp_usb - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - - - battery - battery - hald-addon-blazer_usb - ups - - - - - - battery - battery - hald-addon-blazer_usb - ups - - - - - - - - battery - battery - hald-addon-bcmxcp_usb - ups - - - - battery - battery - hald-addon-blazer_usb - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - - - battery - battery - hald-addon-richcomm_usb - ups - - - - - - - - battery - battery - hald-addon-tripplite_usb - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - - - battery - battery - hald-addon-blazer_usb - ups - - - - - - - - battery - battery - hald-addon-usbhid-ups - ups - - - - - - battery - battery - hald-addon-blazer_usb - ups - - - - - - battery - battery - hald-addon-blazer_usb - ups - - - - - diff --git a/scripts/hotplug/Makefile.am b/scripts/hotplug/Makefile.am index c36cd7d..06a6fe3 100644 --- a/scripts/hotplug/Makefile.am +++ b/scripts/hotplug/Makefile.am @@ -6,6 +6,16 @@ if WITH_HOTPLUG endif EXTRA_DIST = README + +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +# Generated by configure script: DISTCLEANFILES = libhidups + # we should never remove this one, apart from a distclean-check #MAINTAINERCLEANFILES = libhid.usermap + +# Generated by autogen.sh and needed to run the configure script +# (technically, generated by tools/nut-usbinfo.pl script among +# GENERATED_USB_OS_FILES): +MAINTAINERCLEANFILES += libhid.usermap diff --git a/scripts/hotplug/Makefile.in b/scripts/hotplug/Makefile.in index 62bc680..3a11e14 100644 --- a/scripts/hotplug/Makefile.in +++ b/scripts/hotplug/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -17,6 +16,61 @@ VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -37,34 +91,44 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = scripts/hotplug -DIST_COMMON = README $(am__dist_hotplugusb_DATA_DIST) \ - $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ - $(srcdir)/libhidups.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__dist_hotplugusb_DATA_DIST) \ + $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = libhidups @@ -90,19 +154,47 @@ am__nobase_list = $(am__nobase_strip_setup); \ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } am__installdirs = "$(DESTDIR)$(hotplugusbdir)" \ "$(DESTDIR)$(hotplugusbdir)" SCRIPTS = $(hotplugusb_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac am__dist_hotplugusb_DATA_DIST = libhid.usermap DATA = $(dist_hotplugusb_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/libhidups.in README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -113,16 +205,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -131,11 +232,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -145,14 +243,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -163,21 +262,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -191,35 +299,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -230,8 +349,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -250,18 +373,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -275,6 +401,16 @@ udevdir = @udevdir@ @WITH_HOTPLUG_TRUE@dist_hotplugusb_DATA = libhid.usermap @WITH_HOTPLUG_TRUE@hotplugusb_SCRIPTS = libhidups EXTRA_DIST = README + +# we should never remove this one, apart from a distclean-check +#MAINTAINERCLEANFILES = libhid.usermap + +# Generated by autogen.sh and needed to run the configure script +# (technically, generated by tools/nut-usbinfo.pl script among +# GENERATED_USB_OS_FILES): +MAINTAINERCLEANFILES = Makefile.in .dirstamp libhid.usermap + +# Generated by configure script: DISTCLEANFILES = libhidups all: all-am @@ -291,14 +427,13 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/hotplug/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu scripts/hotplug/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -313,8 +448,11 @@ libhidups: $(top_builddir)/config.status $(srcdir)/libhidups.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ install-hotplugusbSCRIPTS: $(hotplugusb_SCRIPTS) @$(NORMAL_INSTALL) - test -z "$(hotplugusbdir)" || $(MKDIR_P) "$(DESTDIR)$(hotplugusbdir)" @list='$(hotplugusb_SCRIPTS)'; test -n "$(hotplugusbdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(hotplugusbdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(hotplugusbdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ @@ -342,9 +480,7 @@ uninstall-hotplugusbSCRIPTS: @list='$(hotplugusb_SCRIPTS)'; test -n "$(hotplugusbdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ - test -n "$$list" || exit 0; \ - echo " ( cd '$(DESTDIR)$(hotplugusbdir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(hotplugusbdir)" && rm -f $$files + dir='$(DESTDIR)$(hotplugusbdir)'; $(am__uninstall_files_from_dir) mostlyclean-libtool: -rm -f *.lo @@ -353,8 +489,11 @@ clean-libtool: -rm -rf .libs _libs install-dist_hotplugusbDATA: $(dist_hotplugusb_DATA) @$(NORMAL_INSTALL) - test -z "$(hotplugusbdir)" || $(MKDIR_P) "$(DESTDIR)$(hotplugusbdir)" @list='$(dist_hotplugusb_DATA)'; test -n "$(hotplugusbdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(hotplugusbdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(hotplugusbdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -368,17 +507,18 @@ uninstall-dist_hotplugusbDATA: @$(NORMAL_UNINSTALL) @list='$(dist_hotplugusb_DATA)'; test -n "$(hotplugusbdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - test -n "$$files" || exit 0; \ - echo " ( cd '$(DESTDIR)$(hotplugusbdir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(hotplugusbdir)" && rm -f $$files -tags: TAGS -TAGS: + dir='$(DESTDIR)$(hotplugusbdir)'; $(am__uninstall_files_from_dir) +tags TAGS: -ctags: CTAGS -CTAGS: +ctags CTAGS: + +cscope cscopelist: -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -425,10 +565,15 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -441,6 +586,7 @@ distclean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am @@ -511,20 +657,21 @@ uninstall-am: uninstall-dist_hotplugusbDATA \ .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ - distclean distclean-generic distclean-libtool distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-dist_hotplugusbDATA \ - install-dvi install-dvi-am install-exec install-exec-am \ - install-hotplugusbSCRIPTS install-html install-html-am \ - install-info install-info-am install-man install-pdf \ - install-pdf-am install-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am \ + install-dist_hotplugusbDATA install-dvi install-dvi-am \ + install-exec install-exec-am install-hotplugusbSCRIPTS \ + install-html install-html-am install-info install-info-am \ + install-man install-pdf install-pdf-am install-ps \ + install-ps-am install-strip installcheck installcheck-am \ + installdirs maintainer-clean maintainer-clean-generic \ + mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ + ps ps-am tags-am uninstall uninstall-am \ uninstall-dist_hotplugusbDATA uninstall-hotplugusbSCRIPTS -# we should never remove this one, apart from a distclean-check -#MAINTAINERCLEANFILES = libhid.usermap +.PRECIOUS: Makefile + # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/scripts/hotplug/libhid.usermap b/scripts/hotplug/libhid.usermap index 53c1519..5a747e3 100644 --- a/scripts/hotplug/libhid.usermap +++ b/scripts/hotplug/libhid.usermap @@ -4,10 +4,12 @@ # libhidups 0x0003 0xVVVV 0xPPPP 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # # usb module match_flags idVendor idProduct bcdDevice_lo bcdDevice_hi bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass bInterfaceSubClass bInterfaceProtocol driver_info -# Krauler UP-M500VA +# SNR-UPS-LID-XXXX UPSes libhidups 0x0003 0x0001 0x0000 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # Hewlett Packard +# e.g. ? +libhidups 0x0003 0x03f0 0x0001 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # T500 libhidups 0x0003 0x03f0 0x1f01 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # T750 @@ -20,10 +22,22 @@ libhidups 0x0003 0x03f0 0x1f08 0x0000 0x0000 0x00 libhidups 0x0003 0x03f0 0x1f09 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # HP R/T 2200 INTL (like SMART2200RMXL2U) libhidups 0x0003 0x03f0 0x1f0a 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 -# HP R1500 G2 INTL +# HP R1500 G2 and G3 INTL libhidups 0x0003 0x03f0 0x1fe0 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # HP T750 G2 libhidups 0x0003 0x03f0 0x1fe1 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# e.g. ? +libhidups 0x0003 0x03f0 0x1fe2 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# HP T1500 G3 +libhidups 0x0003 0x03f0 0x1fe3 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# R/T3000 +libhidups 0x0003 0x03f0 0x1fe5 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# R/T3000 +libhidups 0x0003 0x03f0 0x1fe6 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# various models +libhidups 0x0003 0x03f0 0x1fe7 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# various models +libhidups 0x0003 0x03f0 0x1fe8 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # Eaton # various models @@ -35,6 +49,26 @@ libhidups 0x0003 0x0463 0xffff 0x0000 0x0000 0x00 # various models libhidups 0x0003 0x047c 0xffff 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# ST Microelectronics +# TS Shara UPSes; vendor ID 0x0483 is from ST Microelectronics - with product IDs delegated to different OEMs +libhidups 0x0003 0x0483 0x0035 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# USB IDs device table +libhidups 0x0003 0x0483 0xa113 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 + +# IBM +# 6000 VA LCD 4U Rack UPS; 5396-1Kx +libhidups 0x0003 0x04b3 0x0001 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 + +# Riello (Cypress Semiconductor Corp.) +# various models +libhidups 0x0003 0x04b4 0x5500 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 + +# Minibox +# openUPS Intelligent UPS (minimum required firmware 1.4) +libhidups 0x0003 0x04d8 0xd004 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# openUPS Intelligent UPS (minimum required firmware 1.4) +libhidups 0x0003 0x04d8 0xd005 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 + # Belkin # F6H375-USB libhidups 0x0003 0x050d 0x0375 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 @@ -52,10 +86,14 @@ libhidups 0x0003 0x050d 0x0910 0x0000 0x0000 0x00 libhidups 0x0003 0x050d 0x0912 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # F6C800-UNV libhidups 0x0003 0x050d 0x0980 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Regulator PRO-USB +libhidups 0x0003 0x050d 0x0f51 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # F6C1100-UNV, F6C1200-UNV libhidups 0x0003 0x050d 0x1100 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # APC +# APC AP9584 Serial->USB kit +libhidups 0x0003 0x051d 0x0000 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # various models libhidups 0x0003 0x051d 0x0002 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # various 5G models @@ -68,15 +106,29 @@ libhidups 0x0003 0x0592 0x0002 0x0000 0x0000 0x00 libhidups 0x0003 0x0592 0x0004 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # Agiler UPS libhidups 0x0003 0x05b8 0x0000 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 -# Belkin F6C1200-UNV + +# Delta UPS +# Delta UPS Amplon R Series, Single Phase UPS, 1/2/3 kVA +libhidups 0x0003 0x05dd 0x041b 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Delta/Minuteman Enterprise Plus E1500RM2U +libhidups 0x0003 0x05dd 0xa011 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Belkin F6C1200-UNV/Voltronic Power UPSes libhidups 0x0003 0x0665 0x5161 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 -# Phoenixtec -# various models +# Phoenixtec Power Co., Ltd +# Online Yunto YQ450 libhidups 0x0003 0x06da 0x0002 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # Mustek Powermust libhidups 0x0003 0x06da 0x0003 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 -# various models +# Phoenixtec Innova 3/1 T +libhidups 0x0003 0x06da 0x0004 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Phoenixtec Innova RT +libhidups 0x0003 0x06da 0x0005 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Phoenixtec Innova T +libhidups 0x0003 0x06da 0x0201 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Online Zinto A +libhidups 0x0003 0x06da 0x0601 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# PROTECT B / NAS libhidups 0x0003 0x06da 0xffff 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # iDowell @@ -84,11 +136,11 @@ libhidups 0x0003 0x06da 0xffff 0x0000 0x0000 0x00 libhidups 0x0003 0x075d 0x0300 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # Cyber Power Systems -# 900AVR/BC900D, CP1200AVR/BC1200D +# 900AVR/BC900D libhidups 0x0003 0x0764 0x0005 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 -# Dynex DX-800U? +# Dynex DX-800U?, CP1200AVR/BC1200D, CP825AVR-G, CP1000AVRLCD, CP1000PFCLCD, CP1500C, CP550HG, etc. libhidups 0x0003 0x0764 0x0501 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 -# OR2200LCDRM2U +# OR2200LCDRM2U, OR700LCDRM1U, PR6000LCDRTXL5U libhidups 0x0003 0x0764 0x0601 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # Sweex 1000VA libhidups 0x0003 0x0925 0x1234 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 @@ -106,6 +158,8 @@ libhidups 0x0003 0x09ae 0x1008 0x0000 0x0000 0x00 libhidups 0x0003 0x09ae 0x1009 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # e.g. TrippLite ECO550UPS libhidups 0x0003 0x09ae 0x1010 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# e.g. TrippLite SU3000LCD2UHV +libhidups 0x0003 0x09ae 0x1330 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # e.g. TrippLite OMNI1000LCD libhidups 0x0003 0x09ae 0x2005 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # e.g. TrippLite OMNI900LCD @@ -140,6 +194,10 @@ libhidups 0x0003 0x09ae 0x3013 0x0000 0x0000 0x00 libhidups 0x0003 0x09ae 0x3014 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # e.g. ? libhidups 0x0003 0x09ae 0x3015 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# e.g. TrippLite Smart1500LCD (newer unit) +libhidups 0x0003 0x09ae 0x3016 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# e.g. TrippLite AVR750U (newer unit) +libhidups 0x0003 0x09ae 0x3024 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # e.g. TrippLite SmartOnline SU1500RTXL2UA (older unit?) libhidups 0x0003 0x09ae 0x4001 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # e.g. TrippLite SmartOnline SU6000RT4U? @@ -158,7 +216,9 @@ libhidups 0x0003 0x09ae 0x4007 0x0000 0x0000 0x00 libhidups 0x0003 0x09ae 0x4008 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # PowerCOM -# PowerCOM BNT-xxxAP +# PowerCOM Vanguard and BNT-xxxAP +libhidups 0x0003 0x0d9f 0x0001 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# PowerCOM Vanguard and BNT-xxxAP libhidups 0x0003 0x0d9f 0x0004 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # PowerCOM IMP - IMPERIAL Series libhidups 0x0003 0x0d9f 0x00a2 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 @@ -176,7 +236,59 @@ libhidups 0x0003 0x0f03 0x0001 0x0000 0x0000 0x00 # Liebert # Liebert PowerSure PSA UPS libhidups 0x0003 0x10af 0x0001 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Liebert PowerSure PSI 1440 +libhidups 0x0003 0x10af 0x0004 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Liebert GXT3 +libhidups 0x0003 0x10af 0x0008 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 # GE EP series libhidups 0x0003 0x14f0 0x00c9 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 -# Ablerex 625L USB + +# Legrand +# Legrand Keor SP +libhidups 0x0003 0x1cb0 0x0032 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Legrand Daker DK / DK Plus +libhidups 0x0003 0x1cb0 0x0035 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Legrand Keor PDU +libhidups 0x0003 0x1cb0 0x0038 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 + +# Arduino +# Arduino Leonardo, Leonardo ETH and Pro Micro +libhidups 0x0003 0x2341 0x0036 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Arduino Leonardo, Leonardo ETH and Pro Micro +libhidups 0x0003 0x2341 0x8036 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 + +# Arduino +# Arduino Leonardo, Leonardo ETH and Pro Micro +libhidups 0x0003 0x2A03 0x0036 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Arduino Leonardo, Leonardo ETH and Pro Micro +libhidups 0x0003 0x2A03 0x0040 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Arduino Leonardo, Leonardo ETH and Pro Micro +libhidups 0x0003 0x2A03 0x8036 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Arduino Leonardo, Leonardo ETH and Pro Micro +libhidups 0x0003 0x2A03 0x8040 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 + +# AEG +# PROTECT B / NAS +libhidups 0x0003 0x2b2d 0xffff 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 + +# Ever +# USB IDs device table +libhidups 0x0003 0x2e51 0x0000 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# USB IDs device table +libhidups 0x0003 0x2e51 0xffff 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 + +# Salicru +# SLC TWIN PRO2<=3KVA per https://github.com/networkupstools/nut/issues/450 +libhidups 0x0003 0x2e66 0x0201 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# SLC TWIN PRO2<=3KVA per https://github.com/networkupstools/nut/issues/450 +libhidups 0x0003 0x2e66 0x0202 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# SLC TWIN PRO2<=3KVA per https://github.com/networkupstools/nut/issues/450 +libhidups 0x0003 0x2e66 0x0203 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# https://www.salicru.com/sps-home.html +libhidups 0x0003 0x2e66 0x0300 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 + +# Powervar +# Powervar +libhidups 0x0003 0x4234 0x0002 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 +# Ablerex 625L USB (Note: earlier best-fit was "krauler_subdriver" before PR #1135) libhidups 0x0003 0xffff 0x0000 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x00000000 diff --git a/scripts/java/Makefile.am b/scripts/java/Makefile.am deleted file mode 100644 index 83f5c1d..0000000 --- a/scripts/java/Makefile.am +++ /dev/null @@ -1,15 +0,0 @@ -# TODO: Java / Maven build integration - -EXTRA_DIST = README \ -jNut/pom.xml \ -jNut/README \ -jNut/src/main/java/org/networkupstools/jnut/Client.java \ -jNut/src/main/java/org/networkupstools/jnut/Command.java \ -jNut/src/main/java/org/networkupstools/jnut/Device.java \ -jNut/src/main/java/org/networkupstools/jnut/NutException.java \ -jNut/src/main/java/org/networkupstools/jnut/StringLineSocket.java \ -jNut/src/main/java/org/networkupstools/jnut/Variable.java \ -jNut/src/test/java/org/networkupstools/jnut/ClientTest.java \ -jNutList/pom.xml \ -jNutList/README \ -jNutList/src/main/java/org/networkupstools/jnutlist/AppList.java diff --git a/scripts/java/README b/scripts/java/README deleted file mode 100644 index 6996f1d..0000000 --- a/scripts/java/README +++ /dev/null @@ -1,19 +0,0 @@ -Java NUT Client files -~~~~~~~~~~~~~~~~~~~~~ - -This directory contains various NUT Client related java source files, written by -Emilien Kia, sponsored by Eaton, and released under GPL v2. - -* "jNut": this directory contains maven project and source files for jNut, -which is a Java abstraction bundle to access NUT server(s). -You can use it in Java programs to access NUT's upsd data server in a simple -way, without having to know the NUT protocol. - -* "jNutList": this directory contains maven project and source files for -jNutList, a simple Java example program using jNut which connect to an UPSD, -lists its ups and their variables and commands. - -* "jNutWebAPI": this directory contains maven project and source files for -jNutWebAPI, a simple Java web archive to access nut informations via REST -web services. - diff --git a/scripts/java/jNut/README b/scripts/java/jNut/README deleted file mode 100644 index b68eef2..0000000 --- a/scripts/java/jNut/README +++ /dev/null @@ -1,77 +0,0 @@ -jNut library -~~~~~~~~~~~~ - -This directory contains source files for the jNut library, -which is a Java abstraction bundle to access NUT server(s). -You can use it in Java programs to access NUT's upsd data server in a simple -way, without having to know the NUT protocol. - -jNut building requirements -^^^^^^^^^^^^^^^^^^^^^^^^^^ - -jNut requires to be build : -- A Java JDK 6 correctly set in environment (ie bin folder in path) -- A Maven 3 installation set in environment (ie bin folder in path) with -sufficient configuration (internet connection, local repository) to let maven -get all plugins to make processes. - -jNut is written in Java SE 1.4 and is tuned to be compiled to Java 1.4 code -level so most of environment can use it. - -jNut building -^^^^^^^^^^^^^ - -Once JDK and Maven installed and configured, just go into the jNut directory -and type: - - mvn install - -The produced package called 'jNut-x.x-xxx.jar' is located in 'target' -subdirectory. - -jNut javadoc -^^^^^^^^^^^^ - -You can generate jNut javadoc by typing: - - mvn javadoc:javadoc - -Documentation will be generated in 'target/site/apidocs' subdirectory and its -entry point is located at 'target/site/apidocs/index.html'. - -Workspace cleaning -^^^^^^^^^^^^^^^^^^ - -The jNut workspace can be cleaned by removing the 'target' subdirectory or by -typing: - - mvn clean - -Unit test notes -^^^^^^^^^^^^^^^ - -jNut sources embed some unit tests in the 'src/test/java' subdirectory. -These tests are based on JUnit and are executed between compilation and -packaging phases at each build. - -Implementation notes -^^^^^^^^^^^^^^^^^^^^ - -Currently, jNut is not thread safe. It is not protected against concurrent -queries but queries to different clients can be done in parallel as there are -done on different sockets. - -Moreover, jNut have no connection preservation system so servers can break down -connections due to timeout. -Application using retrieved data must forget them when a disconnection occurs. -If the application want to maintain the connection, it must implement a -ping-pong mecanism itself. - -At present time, jNut do not support SSL connection. It is planned for near -future. - -Changelog -^^^^^^^^^ -* "0.2": Add nut-scanner. -* "0.1": Initial version with basic dialog with UPSD. - diff --git a/scripts/java/jNut/pom.xml b/scripts/java/jNut/pom.xml deleted file mode 100644 index b53b426..0000000 --- a/scripts/java/jNut/pom.xml +++ /dev/null @@ -1,40 +0,0 @@ - - 4.0.0 - - org.networkupstools - jNut - 0.2-SNAPSHOT - jar - - jNut - http://maven.apache.org - - - UTF-8 - - - - - junit - junit - 3.8.1 - test - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 2.3.2 - - 1.4 - 1.4 - - - - - - diff --git a/scripts/java/jNut/src/main/java/org/networkupstools/jnut/Client.java b/scripts/java/jNut/src/main/java/org/networkupstools/jnut/Client.java deleted file mode 100644 index 474f812..0000000 --- a/scripts/java/jNut/src/main/java/org/networkupstools/jnut/Client.java +++ /dev/null @@ -1,619 +0,0 @@ -/* Client.java - - Copyright (C) 2011 Eaton - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -package org.networkupstools.jnut; - -import java.io.IOException; -import java.net.UnknownHostException; -import java.util.ArrayList; - -/** - * A jNut client is start point to dialog to UPSD. - * It can connect to an UPSD then retrieve its device list. - * It support authentication by login/password. - *

- * You can directly create and connect a client by using the - * Client(String host, int port, String login, String passwd) constructor - * or use a three phase construction: - *

    - *
  • empty constructor - *
  • setting host, port, login and password with setters - *
  • call empty connect() - *
- *

- * Objects retrieved by Client are attached (directly or indirectly) to it. - * If the connection is closed, attached objects must not be used anymore (GC). - *

- * Note: The jNut Client does not support any reconnection nor ping mechanism, - * so the calling application must know the UPSD can timeout the connection. - *

- * Note: Retrieved values are not valid along the time, they are valid at the - * precise moment they are retrieved. - * - * @author Emilien Kia - */ -public class Client { - - /** - * Host to which connect. - * Network name or IP. - * Default to "127.0.0.1" - */ - private String host = "127.0.0.1"; - - /** - * IP port. - * Default to 3493 - */ - private int port = 3493; - - /** - * Login to use to connect to UPSD. - */ - private String login = null; - - /** - * Password to use to connect to UPSD. - */ - private String passwd = null; - - /** - * Communication socket - */ - private StringLineSocket socket = null; - - - /** - * Get the host name or address to which client is (or will be) connected. - * @return Host name or address. - */ - public String getHost() { - return host; - } - - /** - * Set the host name (or address) to which the client will intend to connect to at next connection. - * @param host New host name or address. - */ - public void setHost(String host) { - this.host = host; - } - - /** - * Get the login with which the client is (or will be connected). - * @return The login. - */ - public String getLogin() { - return login; - } - - /** - * Set the login with which the client will intend to connect. - * @param login New login. - */ - public void setLogin(String login) { - this.login = login; - } - - /** - * Get the password with which the client is (or will be connected). - * @return The password. - */ - public String getPasswd() { - return passwd; - } - - /** - * Set the password with which the client will intend to connect. - * @param passwd New password. - */ - public void setPasswd(String passwd) { - this.passwd = passwd; - } - - /** - * Get the port to which client is (or will be) connected. - * @return Port number. - */ - public int getPort() { - return port; - } - - /** - * Set the port to which client is (or will be) connected. - * @param port Port number. - */ - public void setPort(int port) { - this.port = port; - } - - - - /** - * Default constructor. - */ - public Client() - { - } - - /** - * Connection constructor. - * Construct the Client object and intend to connect. - * Throw an exception if cannot connect. - * @param host Host to which connect. - * @param port IP port. - * @param login Login to use to connect to UPSD. - * @param passwd Password to use to connect to UPSD. - */ - public Client(String host, int port, String login, String passwd) throws IOException, UnknownHostException, NutException - { - connect(host, port, login, passwd); - } - - /** - * Intent to connect and authenticate to an UPSD with specified parameters. - * Throw an exception if cannot connect. - * @param host Host to which connect. - * @param port IP port. - * @param login Login to use to connect to UPSD. - * @param passwd Password to use to connect to UPSD. - */ - public void connect(String host, int port, String login, String passwd) throws IOException, UnknownHostException, NutException - { - this.host = host; - this.port = port; - this.login = login; - this.passwd = passwd; - connect(); - } - - /** - * Intent to connect to an UPSD with specified parameters without authentication. - * Throw an exception if cannot connect. - * @param host Host to which connect. - * @param port IP port. - */ - public void connect(String host, int port) throws IOException, UnknownHostException, NutException - { - this.host = host; - this.port = port; - connect(); - } - - /** - * Connection to UPSD with already specified parameters. - * Throw an exception if cannot connect. - */ - public void connect() throws IOException, UnknownHostException, NutException - { - // Force disconnect if another connection is alive. - if(socket!=null) - disconnect(); - - socket = new StringLineSocket(host, port); - - authenticate(); - } - - /** - * Intend to authenticate with specified login and password, overriding - * already defined ones. - * @param login - * @param passwd - * @throws IOException - * @throws NutException - */ - public void authenticate(String login, String passwd) throws IOException, NutException - { - this.login = login; - this.passwd = passwd; - authenticate(); - } - - /** - * Intend to authenticate with alread set login and password. - * @throws IOException - * @throws NutException - */ - public void authenticate() throws IOException, NutException - { - // Send login - if(login!=null && !login.isEmpty()) - { - String res = query("USERNAME", login); - if(!res.startsWith("OK")) - { - // Normaly response should be OK or ERR and nothing else. - throw new NutException(NutException.UnknownResponse, "Unknown response in Client.connect (USERNAME) : " + res); - } - } - // Send password - if(passwd!=null && !passwd.isEmpty()) - { - String res = query("PASSWORD", passwd); - if(!res.startsWith("OK")) - { - // Normaly response should be OK or ERR and nothing else. - throw new NutException(NutException.UnknownResponse, "Unknown response in Client.connect (PASSWORD) : " + res); - } - } - } - - /** - * Test if the client is connected to the UPSD. - * Note: it does not detect if the connection have been closed by server. - * @return True if connected. - */ - public boolean isConnected() - { - return socket!=null && socket.isConnected(); - } - - /** - * Disconnect. - */ - public void disconnect() - { - if(socket!=null) - { - try - { - if(socket.isConnected()) - socket.close(); - } - catch(IOException e) - { - e.printStackTrace(); - } - socket = null; - } - } - - /** - * Log out. - */ - public void logout() - { - if(socket!=null) - { - try - { - if(socket.isConnected()) - { - socket.write("LOGOUT"); - socket.close(); - } - } - catch(IOException e) - { - e.printStackTrace(); - } - socket = null; - } - } - - /** - * Merge an array of stings into on string, with a space ' ' separator. - * @param str First string to merge - * @param strings Additionnal strings to merge - * @param sep Separator. - * @return The merged string, empty if no source string. - */ - static String merge(String str, String[] strings) - { - String res = str; - if(strings!=null) - { - for(int n=0; n ""'. - * @param source String source to split. - * @return String couple with name and value. - */ - static String[] splitNameValueString(String source) - { - int pos = source.indexOf(' '); - if(pos<1) - return null; - String name = source.substring(0, pos); - String value = extractDoublequotedValue(source.substring(pos+1)); - if(value==null) - return null; - String[] res = new String[2]; - res[0] = name; - res[1] = value; - return res; - } - - /** - * Intend to extract a value from its doublequoted and escaped representation. - * @param source Source string to convert. - * @return Extracted value - */ - static String extractDoublequotedValue(String source) - { - // Test doublequote at begin and end of string, then remove them. - if(!(source.startsWith("\"") && source.endsWith("\""))) - return null; - source = source.substring(1, source.length()-1); - // Unescape it. - return unescape(source); - } - - /** - * Escape string with backslashes. - * @param str String to escape. - * @return Escaped string. - */ - static String escape(String str) - { - // Replace a backslash by two backslash (regexp) - str = str.replaceAll("\\\\", "\\\\\\\\"); - // Replace a doublequote by backslash-doublequote (regexp) - str = str.replaceAll("\"", "\\\\\""); - return str; - } - - /** - * Unescape string with backslashes. - * @param str String to unescape. - * @return Unescaped string. - */ - static String unescape(String str) - { - // Replace a backslash-doublequote by doublequote (regexp) - str = str.replaceAll("\\\\\"", "\""); - // Replace two backslash by a backslash (regexp) - str = str.replaceAll("\\\\\\\\", "\\\\"); - return str; - } - - /** - * Detect an UPSD ERR line. - * If found, parse it, construct and throw an NutException - * @param str Line to analyse. - * @throws NutException - */ - private void detectError(String str) throws NutException - { - if(str.startsWith("ERR ")) - { - String[] arr = str.split(" ", 3); - switch(arr.length) - { - case 2: - throw new NutException(arr[1]); - case 3: - throw new NutException(arr[1], arr[2]); - default: - throw new NutException(); - } - } - } - - /** - * Send a query line then read the response. - * Helper around query(String). - * @param query Query to send. - * @param subquery Sub query to send. - * @return The reply. - * @throws IOException - */ - protected String query(String query, String subquery) throws IOException, NutException - { - return query(query + " " + subquery); - } - - /** - * Send a query line then read the response. - * Helper around query(String, String ...). - * @param query Query to send. - * @param subquery Sub query to send. - * @param params Optionnal additionnal parameters. - * @return The reply. - * @throws IOException - */ - protected String query(String query, String subquery, String[] params) throws IOException, NutException - { - return query(query + " " + subquery, params); - } - - /** - * Send a query line then read the response. - * @param query Query to send. - * @param params Optionnal additionnal parameters. - * @return The reply. - * @throws IOException - */ - protected String query(String query, String [] params) throws IOException, NutException - { - query = merge(query, params); - return query(query); - } - - /** - * Send a query line then read the response. - * @param query Query to send. - * @return The reply. - * @throws IOException - */ - protected String query(String query) throws IOException, NutException - { - if(!isConnected()) - return null; - - socket.write(query); - String res = socket.read(); - detectError(res); - return res; - } - - /** - * Send a GET query line then read the reply and validate the response. - * @param subcmd GET subcommand to send. - * @param param Extra parameters - * @return GET result return by UPSD, without the subcommand and param prefix. - * @throws IOException - */ - protected String get(String subcmd, String param) throws IOException, NutException - { - String[] params = {param}; - return get(subcmd, params); - } - - /** - * Send a GET query line then read the reply and validate the response. - * @param subcmd GET subcommand to send. - * @param params Eventual extra parameters. - * @return GET result return by UPSD, without the subcommand and param prefix. - * @throws IOException - */ - protected String get(String subcmd, String [] params) throws IOException, NutException - { - if(!isConnected()) - return null; - - subcmd = merge(subcmd, params); - socket.write("GET " + subcmd); - String res = socket.read(); - if(res==null) - return null; - detectError(res); - if(res.startsWith(subcmd + " ")) - { - return res.substring(subcmd.length()+1); - } - else - { - return null; - } - } - - /** - * Send a LIST query line then read replies and validate them. - * @param subcmd LIST subcommand to send. - * @return LIST results return by UPSD, without the subcommand and param prefix. - * @throws IOException - */ - protected String[] list(String subcmd) throws IOException, NutException - { - return list(subcmd, (String[])null); - } - - /** - * Send a LIST query line then read replies and validate them. - * @param subcmd LIST subcommand to send. - * @param param Extra parameters. - * @return LIST results return by UPSD, without the subcommand and param prefix. - * @throws IOException - */ - protected String[] list(String subcmd, String param) throws IOException, NutException - { - String[] params = {param}; - return list(subcmd, params); - } - - /** - * Send a LIST query line then read replies and validate them. - * @param subcmd LIST subcommand to send. - * @param params Eventual extra parameters. - * @return LIST results return by UPSD, without the subcommand and param prefix. - * @throws IOException - */ - protected String[] list(String subcmd, String [] params) throws IOException, NutException - { - if(!isConnected()) - return null; - - subcmd = merge(subcmd, params); - socket.write("LIST " + subcmd); - String res = socket.read(); - if(res==null) - return null; - detectError(res); - if(!res.startsWith("BEGIN LIST " + subcmd)) - return null; - - ArrayList/**/ list = new ArrayList/**/(); - int sz = subcmd.length()+1; - while(true) - { - res = socket.read(); - detectError(res); - if(!res.startsWith(subcmd + " ")) - break; - list.add(res.substring(sz)); - } - if(!res.equals("END LIST " + subcmd)) - return null; - - return (String[])list.toArray(new String[list.size()]); - } - - - /** - * Returns the list of available devices from the NUT server. - * @return List of devices, empty if nothing, - * null if not connected or failed. - * - */ - public Device[] getDeviceList() throws IOException, NutException - { - String[] res = list("UPS"); - if(res==null) - return null; - - ArrayList/**/ list = new ArrayList/**/(); - for(int i=0; i - * It can be used to retrieve description and execute commands. - * A Command object can be retrieved from Device instance and can not be constructed directly. - * - * @author Emilien Kia - */ -public class Command { - /** - * Device to which command is attached - */ - Device device = null; - - /** - * Command name - */ - String name = null; - - /** - * Internally create a command. - * @param name Command name. - * @param device Device to which the command is attached. - */ - protected Command(String name, Device device) - { - this.device = device; - this.name = name; - } - - /** - * Return the device to which the command can be executed. - * @return Attached device. - */ - public Device getDevice() { - return device; - } - - /** - * Return the command name. - * @return Command name. - */ - public String getName() { - return name; - } - - /** - * Retrieve the command description from UPSD and store it in cache. - * @return Command description - * @throws IOException - */ - public String getDescription() throws IOException, NutException { - if(device!=null && device.getClient()!=null) - { - String[] params = {device.getName(), name}; - String res = device.getClient().get("CMDDESC", params); - return res!=null?Client.extractDoublequotedValue(res):null; - } - return null; - } - - /** - * Execute the instant command. - * @throws IOException - */ - public void execute() throws IOException, NutException { - if(device!=null && device.getClient()!=null) - { - String[] params = {device.getName(), name}; - String res = device.getClient().query("INSTCMD", params); - if(!res.equals("OK")) - { - // Normaly response should be OK or ERR and nothing else. - throw new NutException(NutException.UnknownResponse, "Unknown response in Command.execute : " + res); - } - } - } -} diff --git a/scripts/java/jNut/src/main/java/org/networkupstools/jnut/Device.java b/scripts/java/jNut/src/main/java/org/networkupstools/jnut/Device.java deleted file mode 100644 index 8f4fdc1..0000000 --- a/scripts/java/jNut/src/main/java/org/networkupstools/jnut/Device.java +++ /dev/null @@ -1,279 +0,0 @@ -/* Device.java - - Copyright (C) 2011 Eaton - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -package org.networkupstools.jnut; - -import java.io.IOException; -import java.util.ArrayList; - -/** - * Class representing a device attached to a Client. - *

- * It can retrieve its description, its number of logins, its variable and command lists. - * A Device object can be retrieved from Client instance and can not be constructed directly. - * - * @author Emilien Kia - */ -public class Device { - - /** - * Client to which device is attached - */ - Client client = null; - - /** - * Device name - */ - String name = null; - - /** - * Internally create a device. - * @param name Device name. - * @param client Client to which the device is attached. - */ - protected Device(String name, Client client) - { - this.client = client; - this.name = name; - } - - /** - * Return the client to which the device is connected. - * @return Attached client. - */ - public Client getClient() { - return client; - } - - /** - * Return the device name. - * @return Device name. - */ - public String getName() { - return name; - } - - /** - * Retrieve the device description from UPSD and store it in cache. - * @return Device description - * @throws IOException - */ - public String getDescription() throws IOException, NutException { - if(client!=null) - { - return client.get("UPSDESC", name); - } - return null; - } - - /** - * Log in to the ups. - *

- * Use this to log the fact that a system is drawing power from this UPS. - * The upsmon master will wait until the count of attached systems reaches - * 1 - itself. This allows the slaves to shut down first. - *

- * NOTE: You probably shouldn't send this command unless you are upsmon, - * or a upsmon replacement. - * @throws IOException - * @throws NutException - */ - public void login() throws IOException, NutException { - if(client!=null) - { - String res = client.query("LOGIN", name); - if(!res.startsWith("OK")) - { - // Normaly response should be OK or ERR and nothing else. - throw new NutException(NutException.UnknownResponse, "Unknown response in Device.login : " + res); - } - } - } - - /** - * This function doesn't do much by itself. It is used by upsmon to make - * sure that master-level functions like FSD are available if necessary - * @throws IOException - * @throws NutException - */ - public void master() throws IOException, NutException { - if(client!=null) - { - String res = client.query("MASTER", name); - if(!res.startsWith("OK")) - { - // Normaly response should be OK or ERR and nothing else. - throw new NutException(NutException.UnknownResponse, "Unknown response in Device.master : " + res); - } - } - } - - /** - * Set the "forced shutdown" flag. - *

- * upsmon in master mode is the primary user of this function. It sets this - * "forced shutdown" flag on any UPS when it plans to power it off. This is - * done so that slave systems will know about it and shut down before the - * power disappears. - *

- * Setting this flag makes "FSD" appear in a STATUS request for this UPS. - * Finding "FSD" in a status request should be treated just like a "OB LB". - *

- * It should be noted that FSD is currently a latch - once set, there is - * no way to clear it short of restarting upsd or dropping then re-adding - * it in the ups.conf. This may cause issues when upsd is running on a - * system that is not shut down due to the UPS event. - * @throws IOException - * @throws NutException - */ - public void setForcedShutdown() throws IOException, NutException { - if(client!=null) - { - String res = client.query("FSD", name); - if(!res.startsWith("OK")) - { - // Normaly response should be OK or ERR and nothing else. - throw new NutException(NutException.UnknownResponse, "Unknown response in Device.setForcedShutdown : " + res); - } - } - } - - /** - * Return the number of clients which have done LOGIN for this UPS. - * Force to retrieve it from UPSD and store it in cache. - * @return Number of clients, -1 if error. - * @throws IOException - */ - public int getNumLogin() throws IOException, NutException { - if(client!=null) - { - String res = client.get("NUMLOGINS", name); - return res!=null?Integer.parseInt(res):-1; - } - return -1; - } - - - /** - * Return the list of device variables from the NUT server. - * @return List of variables, empty if nothing, - * null if not connected or failed. - * @throws IOException - */ - public Variable[] getVariableList() throws IOException, NutException { - if(client==null) - return null; - - String[] res = client.list("VAR", name); - if(res==null) - return null; - - ArrayList/**/ list = new ArrayList/**/(); - for(int i=0; i*/ list = new ArrayList/**/(); - for(int i=0; i*/ list = new ArrayList/**/(); - for(int i=0; i - * Instance are thrown when an UPSD returns an error with an "ERR" directive. - * Moreover it can ben thrown with some extra errors like: - *

    - *
  • UNKNOWN-RESPONSE : The response is not understood - *
- *

- * A Nut exception has a (standard java exception message) message which correspond - * to error code returns by UPSD (like 'ACCESS-DENIED', 'UNKNOWN-UPS' ...). - * An extra string embed a more descriptive english message. - * - * @author Emilien Kia - */ -public class NutException extends java.lang.Exception{ - - public static String UnknownResponse = "UNKNOWN-RESPONSE"; - - public static String DriverNotConnected = "DRIVER-NOT-CONNECTED"; - - public String extra = ""; - - public NutException() - { - } - - public NutException(String message) - { - super(message); - } - - public NutException(String message, String extra) - { - super(message); - this.extra = extra; - } - - public NutException(Throwable cause) - { - super(cause); - } - - public NutException(String message, Throwable cause) - { - super(message, cause); - } - - public NutException(String message, String extra, Throwable cause) - { - super(message, cause); - this.extra = extra; - } - - /** - * Returns the extra message. - * @return Extra message if any. - */ - public String getExtra() { - return extra; - } - - /** - * Set the extra message. - * @param extra The new extra message. - */ - public void setExtra(String extra) { - this.extra = extra; - } - - /** - * Test is the exception corresponds to the specified name. - * @param name Name to test - * @return True if exception corresponds. - */ - public boolean is(String name) { - return getMessage()!=null&&getMessage().equals(name); - } - - /** - * Format an exception message. - * @return Exception message - */ - public String toString() { - return "[" + getClass().getSimpleName() + "]" + getMessage() + " : " + getExtra(); - } -} diff --git a/scripts/java/jNut/src/main/java/org/networkupstools/jnut/StringLineSocket.java b/scripts/java/jNut/src/main/java/org/networkupstools/jnut/StringLineSocket.java deleted file mode 100644 index fe39b71..0000000 --- a/scripts/java/jNut/src/main/java/org/networkupstools/jnut/StringLineSocket.java +++ /dev/null @@ -1,137 +0,0 @@ -/* StringLineSocket.java - - Copyright (C) 2011 Eaton - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -package org.networkupstools.jnut; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.net.Socket; -import java.net.UnknownHostException; - -/** - * Class representing a socket, internally used to communicate with UPSD. - * Abstract some stream mechanisms. - * - * @author Emilien Kia - */ -class StringLineSocket { - - /** - * Real internal TCP socket. - */ - Socket socket = null; - - /** - * Writer to the socket. - */ - private OutputStreamWriter writer = null; - - /** - * Reader from the socket. - */ - private BufferedReader reader = null; - - /** - * Create a new line socket. - */ - public StringLineSocket(){ - - } - - /** - * Create a new line socket and connect it. - * @param host Host to connect to - * @param port Port to connect to - * @throws UnknownHostException - * @throws IOException - */ - public StringLineSocket(String host, int port) throws UnknownHostException, IOException{ - connect(host, port); - } - - /** - * Connect a new line socket. - * @param host Host to connect to - * @param port Port to connect to - * @throws UnknownHostException - * @throws IOException - */ - public void connect(String host, int port) throws UnknownHostException, IOException{ - socket = new Socket(host, port); - if(socket!=null) - { - reader = new BufferedReader(new InputStreamReader( - socket.getInputStream())); - writer = new OutputStreamWriter(socket.getOutputStream()); - } - } - - /** - * Close the socket. - */ - public void close() throws IOException{ - if(socket!=null){ - writer.close(); - reader.close(); - socket.close(); - socket = null; - writer = null; - reader = null; - } - } - - /** - * Test if the soecket is connected. - * @return True if connected. - */ - public boolean isConnected() { - return socket!=null && socket.isConnected() && !socket.isClosed(); - } - - /** - * Write a line follow by a '\n' character. - * @param line - * @throws IOException - */ - public void write(String line) throws IOException - { - if(isConnected()) - { - writer.write(line + "\n"); - writer.flush(); - } - } - - /** - * Read a line terminated by a '\n'. - * @return The line without the ending '\n' - * @throws IOException - */ - public String read() throws IOException - { - if(isConnected()) - { - String res = reader.readLine(); - return res; - } - return ""; - } - -} diff --git a/scripts/java/jNut/src/main/java/org/networkupstools/jnut/Variable.java b/scripts/java/jNut/src/main/java/org/networkupstools/jnut/Variable.java deleted file mode 100644 index 07c7c37..0000000 --- a/scripts/java/jNut/src/main/java/org/networkupstools/jnut/Variable.java +++ /dev/null @@ -1,120 +0,0 @@ -/* Variable.java - - Copyright (C) 2011 Eaton - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -package org.networkupstools.jnut; - -import java.io.IOException; - -/** - * Class representing a variable of a device. - *

- * It can be used to get and set its value (if possible). - * A Variable object can be retrieved from Device instance and can not be constructed directly. - * - * @author Emilien Kia - */ -public class Variable { - /** - * Device to which variable is attached - */ - Device device = null; - - /** - * Variable name - */ - String name = null; - - /** - * Internally create a variable. - * @param name Variable name. - * @param device Device to which the variable is attached. - */ - protected Variable(String name, Device device) - { - this.device = device; - this.name = name; - } - - /** - * Return the device to which the variable is related. - * @return Attached device. - */ - public Device getDevice() { - return device; - } - - /** - * Return the variable name. - * @return Command name. - */ - public String getName() { - return name; - } - - /** - * Retrieve the variable value from UPSD and store it in cache. - * @return Variable value - * @throws IOException - */ - public String getValue() throws IOException, NutException { - if(device!=null && device.getClient()!=null) - { - String[] params = {device.getName(), name}; - String res = device.getClient().get("VAR", params); - return res!=null?Client.extractDoublequotedValue(res):null; - } - return null; - } - - /** - * Retrieve the variable description from UPSD and store it in cache. - * @return Variable description - * @throws IOException - */ - public String getDescription() throws IOException, NutException { - if(device!=null && device.getClient()!=null) - { - String[] params = {device.getName(), name}; - String res = device.getClient().get("DESC", params); - return res!=null?Client.extractDoublequotedValue(res):null; - } - return null; - } - - /** - * Set the variable value. - * Note the new value can be applied with a little delay depending of UPSD and connection. - * @param value New value for the variable - * @throws IOException - */ - public void setValue(String value) throws IOException, NutException { - if(device!=null && device.getClient()!=null) - { - String[] params = {"VAR", device.getName(), - name, " \"" + Client.escape(value) + "\""}; - String res = device.getClient().query("SET", params); - if(!res.equals("OK")) - { - // Normaly response should be OK or ERR and nothing else. - throw new NutException(NutException.UnknownResponse, "Unknown response in Variable.setValue : " + res); - } - } - } - - // TODO Add query for type and enum values -} diff --git a/scripts/java/jNut/src/test/java/org/networkupstools/jnut/ClientTest.java b/scripts/java/jNut/src/test/java/org/networkupstools/jnut/ClientTest.java deleted file mode 100644 index e3bce72..0000000 --- a/scripts/java/jNut/src/test/java/org/networkupstools/jnut/ClientTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/* ClientTest.java - - Copyright (C) 2011 Eaton - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -package org.networkupstools.jnut; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** - * Unit test for simple App. - */ -public class ClientTest extends TestCase -{ - /** - * Create the test case - * - * @param testName name of the test case - */ - public ClientTest( String testName ) - { - super( testName ); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() - { - return new TestSuite( ClientTest.class ); - } - - /** - * Escape function test. - */ - public void testEscape() - { - assertEquals("Empty string", "", Client.escape("")); - assertEquals("Simple string", "hello", Client.escape("hello")); - assertEquals("Internal doublequote", "he\\\"llo", Client.escape("he\"llo")); - assertEquals("Internal backslash", "he\\\\llo", Client.escape("he\\llo")); - assertEquals("Internal backslash and doublequote", "he\\\\\\\"llo", Client.escape("he\\\"llo")); - assertEquals("Initial and final doublequote", "\\\"hello\\\"", Client.escape("\"hello\"")); - } - - /** - * Unescape function test. - */ - public void testUnescape() - { - assertEquals("Empty string", "", Client.unescape("")); - assertEquals("Simple string", "hello", Client.unescape("hello")); - assertEquals("Internal doublequote", "he\"llo", Client.unescape("he\\\"llo")); - assertEquals("Internal backslash", "he\\llo", Client.unescape("he\\\\llo")); - assertEquals("Internal backslash and doublequote", "he\\\"llo", Client.unescape("he\\\\\\\"llo")); - assertEquals("Initial and final doublequote", "\"hello\"", Client.unescape("\\\"hello\\\"")); - } - - /** - * extractDoublequotedValue function test. - */ - public void testExtractDoublequotedValue() - { - assertNull("Empty string", Client.extractDoublequotedValue("")); - assertNull("Non doublequoted string", Client.extractDoublequotedValue("hello")); - assertNull("No begining doublequote", Client.extractDoublequotedValue("hello\"")); - assertNull("No ending doublequote", Client.extractDoublequotedValue("\"hello")); - assertEquals("Simple string", "hello", Client.extractDoublequotedValue("\"hello\"")); - assertEquals("String with doublequote", "he\"llo", Client.extractDoublequotedValue("\"he\\\"llo\"")); - assertEquals("String with backslash", "he\\llo", Client.extractDoublequotedValue("\"he\\\\llo\"")); - assertEquals("String with backslash and doublequote", "he\\\"llo", Client.extractDoublequotedValue("\"he\\\\\\\"llo\"")); - } - - /** - * splitNameValueString function test. - */ - public void testSplitNameValueString() - { - String[] res; - assertNull("Empty string", Client.splitNameValueString("")); - assertNull("One word string", Client.splitNameValueString("name")); - assertNull("Non doublequoted string", Client.extractDoublequotedValue("name value")); - assertNull("No begining doublequote", Client.extractDoublequotedValue("name value\"")); - assertNull("No ending doublequote", Client.extractDoublequotedValue("name \"value")); - res = Client.splitNameValueString("name \"value\""); - assertEquals("Simple name/value (name)", "name", res[0]); - assertEquals("Simple name/value (value)", "value", res[1]); - res = Client.splitNameValueString("name \"complex value\""); - assertEquals("Simple name / complex value (name)", "name", res[0]); - assertEquals("Simple name / complex value (value)", "complex value", res[1]); - res = Client.splitNameValueString("name \"complex\\\\value\""); - assertEquals("Simple name / backslash value (name)", "name", res[0]); - assertEquals("Simple name / backslash value (value)", "complex\\value", res[1]); - res = Client.splitNameValueString("name \"complex\\\"value\""); - assertEquals("Simple name / doublequote value (name)", "name", res[0]); - assertEquals("Simple name / doublequote value (value)", "complex\"value", res[1]); - } -} diff --git a/scripts/java/jNutList/README b/scripts/java/jNutList/README deleted file mode 100644 index 4e52bb8..0000000 --- a/scripts/java/jNutList/README +++ /dev/null @@ -1,30 +0,0 @@ -jNutList example application -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -This directory contains source files for the jNutList application. -It allows to connect to an UPSD then retrieve devices and their variables. -It is a little example application to show how to use jNut. - -jNutList building -^^^^^^^^^^^^^^^^^ - -As jNut, jNutList is a maven project so a maven environment must be set. -Please reffer to jNut building notes. - -jNutList running -^^^^^^^^^^^^^^^^ - -jNutList can be run launching it in a console: - - java -jar jNutList-x.x-xxx-jar-with-dependencies.jar - -Some parameters can be passed : - - java -jar jNutList-x.x-jar-with-dependencies.jar host port login password - -For example: - - java -jar jNutList-x.x-jar-with-dependencies.jar localhost 3493 admin passwd - -By default, host is localhost, port is 3493 and login and password are not -specified. diff --git a/scripts/java/jNutList/pom.xml b/scripts/java/jNutList/pom.xml deleted file mode 100644 index 3558aad..0000000 --- a/scripts/java/jNutList/pom.xml +++ /dev/null @@ -1,62 +0,0 @@ - - 4.0.0 - - org.networkupstools - jNutList - 0.1-SNAPSHOT - jar - - jNutList - http://maven.apache.org - - - UTF-8 - - - - - junit - junit - 3.8.1 - test - - - ${project.groupId} - jNut - 0.1-SNAPSHOT - - - - - - - - maven-assembly-plugin - 2.2.1 - - - jar-with-dependencies - - - - org.networkupstools.jnutlist.AppList - - - - - - make-assembly - package - - single - - - - - - - - diff --git a/scripts/java/jNutList/src/main/java/org/networkupstools/jnutlist/AppList.java b/scripts/java/jNutList/src/main/java/org/networkupstools/jnutlist/AppList.java deleted file mode 100644 index a15452d..0000000 --- a/scripts/java/jNutList/src/main/java/org/networkupstools/jnutlist/AppList.java +++ /dev/null @@ -1,112 +0,0 @@ -/* AppList.java - - Copyright (C) 2011 Eaton - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -package org.networkupstools.jnutlist; - -import java.io.IOException; -import java.net.UnknownHostException; -import org.networkupstools.jnut.*; - - -public class AppList -{ - - public static void main( String[] args ) - { - String host = args.length>=1?args[0]:"localhost"; - int port = args.length>=2?Integer.valueOf(args[1]).intValue():3493; - String login = args.length>=3?args[2]:""; - String pass = args.length>=4?args[3]:""; - - System.out.println( "jNutList connecting to " + login+":"+pass+"@"+host+":"+port ); - - Client client = new Client(); - try { - client.connect(host, port, login, pass); - Device[] devs = client.getDeviceList(); - if(devs!=null) - { - for(int d=0; d&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -37,44 +91,77 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = scripts/python -DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -85,16 +172,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -103,11 +199,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -117,14 +210,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -135,21 +229,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -163,35 +266,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -202,8 +316,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -222,18 +340,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -243,21 +364,34 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ -EXTRA_DIST = README \ - app/gui-1.3.glade \ - app/NUT-Monitor \ - app/nut-monitor.desktop \ - app/nut-monitor.png \ - app/README \ - app/pixmaps/var-rw.png \ - app/pixmaps/on_line.png \ - app/pixmaps/warning.png \ - app/pixmaps/on_battery.png \ - app/pixmaps/var-ro.png \ - app/locale/fr/LC_MESSAGES/NUT-Monitor.mo \ - module/PyNUT.py \ - module/test_nutclient.py +EXTRA_DIST_PY2GTK2 = \ + app/ui/gui-1.3.glade \ + app/NUT-Monitor-py2gtk2.in \ + app/nut-monitor-py2gtk2.desktop +EXTRA_DIST_PY3QT5 = \ + app/ui/aboutdialog1.ui \ + app/ui/dialog1.ui \ + app/ui/dialog2.ui \ + app/ui/window1.ui \ + app/NUT-Monitor-py3qt5.in \ + app/nut-monitor-py3qt5.desktop + + +# TODO: Make py2/py3-only builds, delivered preferred symlinks, etc. optional: +EXTRA_DIST = README app/nut-monitor.appdata.xml \ + app/icons/48x48/nut-monitor.png \ + app/icons/64x64/nut-monitor.png \ + app/icons/256x256/nut-monitor.png \ + app/icons/scalable/nut-monitor.svg app/README \ + app/pixmaps/var-rw.png app/pixmaps/on_line.png \ + app/pixmaps/warning.png app/pixmaps/on_battery.png \ + app/pixmaps/var-ro.png \ + app/locale/fr/LC_MESSAGES/NUT-Monitor.mo \ + app/locale/it/LC_MESSAGES/NUT-Monitor.mo module/PyNUT.py.in \ + module/test_nutclient.py.in $(EXTRA_DIST_PY2GTK2) \ + $(EXTRA_DIST_PY3QT5) +MAINTAINERCLEANFILES = Makefile.in .dirstamp all: all-am .SUFFIXES: @@ -273,14 +407,13 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/python/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu scripts/python/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -297,14 +430,17 @@ mostlyclean-libtool: clean-libtool: -rm -rf .libs _libs -tags: TAGS -TAGS: +tags TAGS: -ctags: CTAGS -CTAGS: +ctags CTAGS: + +cscope cscopelist: -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -348,10 +484,15 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -363,9 +504,10 @@ distclean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am -clean-am: clean-generic clean-libtool mostlyclean-am +clean-am: clean-generic clean-libtool clean-local mostlyclean-am distclean: distclean-am -rm -f Makefile @@ -432,16 +574,22 @@ uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ - distclean distclean-generic distclean-libtool distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-info install-info-am install-man install-pdf \ - install-pdf-am install-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am + clean-local cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am +.PRECIOUS: Makefile + + +clean-local: + rm -rf *.pyc __pycache__ */*.pyc */__pycache__ */*/*.pyc */*/__pycache__ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/scripts/python/README b/scripts/python/README index 15932d9..9a96e71 100644 --- a/scripts/python/README +++ b/scripts/python/README @@ -7,6 +7,7 @@ David Goncalves, and released under GPL v3. * "module": this directory contains PyNUT.py, which is a Python abstraction class to access NUT server(s). You can use it in Python programs to access NUT's upsd data server in a simple way, without having to know the NUT protocol. +The same module should work for Python 2 and Python 3. To import it on Python programs you have to use the following (case sensitive) : 'import PyNUT' @@ -17,16 +18,24 @@ data from an upsd data server. To install the PyNUT module on Debian/Ubuntu, copy it to: /usr/share/python-support/python-pynut/ +For quick tests, just make sure its directory is exported in `PYTHONPATH` +environment variable. + This directory also contains test_nutclient.py, which is a PyNUT test program. For this to be fully functional, you will need to adapt the login, password and -upsname to fit your configuration. +upsname to fit your configuration. A NUT data server should be running for the +test program to verify connection and protocol support. * "app": this directory contains the NUT-Monitor application, that uses the PyNUT class, along with its resources. To install it, you will either need to keep the files together, or to install: -- NUT-Monitor to /usr/bin, /usr/X11R6/bin/ or something like that, -- gui.glade to /usr/share/nut-monitor/, +- Depending on the Python version(s) your system has, put NUT-Monitor-py2gtk2 + or NUT-Monitor-py3qt5 to /usr/bin/, /usr/X11R6/bin/ or something like that + (optionally making a simple NUT-Monitor symlink to the preferred version), +- ui/*.glade (for NUT-Monitor-py2gtk2) or ui/*.ui (for NUT-Monitor-py3qt5) + to /usr/share/nut-monitor/, - nut-monitor.png to something like /usr/share/pixmaps/ -- and nut-monitor.desktop to /usr/share/applications +- finally, nut-monitor-py2gtk2.desktop and/or nut-monitor-py3qt5.desktop + (optionally symlinked as nut-monitor.desktop) to /usr/share/applications/ diff --git a/scripts/python/app/NUT-Monitor b/scripts/python/app/NUT-Monitor-py2gtk2.in similarity index 96% rename from scripts/python/app/NUT-Monitor rename to scripts/python/app/NUT-Monitor-py2gtk2.in index 42d861b..d845e87 100755 --- a/scripts/python/app/NUT-Monitor +++ b/scripts/python/app/NUT-Monitor-py2gtk2.in @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!@PYTHON2@ # -*- coding: utf-8 -*- # 2009-12-27 David Goncalves - Version 1.2 @@ -23,12 +23,16 @@ # # 2010-10-06 David Goncalves - Version 1.3 # Added localisation support +# +# 2015-02-14 Michal Fincham - Version 1.3.1 +# Corrected unsafe permissions on ~/.nut-monitor (Debian #777706) import gtk, gtk.glade, gobject import sys import base64 import os, os.path +import stat import platform import time import threading @@ -44,21 +48,23 @@ gobject.threads_init() class interface : - __widgets = {} - __callbacks = {} - __favorites = {} - __favorites_file = None - __favorites_path = "" - __fav_menu_items = list() - __window_visible = True - __glade_file = None - __connected = False - __ups_handler = None - __ups_commands = None - __ups_vars = None - __ups_rw_vars = None - __gui_thread = None - __current_ups = None + DESIRED_FAVORITES_DIRECTORY_MODE = 0700 + + __widgets = {} + __callbacks = {} + __favorites = {} + __favorites_file = None + __favorites_path = "" + __fav_menu_items = list() + __window_visible = True + __glade_file = None + __connected = False + __ups_handler = None + __ups_commands = None + __ups_vars = None + __ups_rw_vars = None + __gui_thread = None + __current_ups = None def __init__( self ) : @@ -70,7 +76,7 @@ class interface : ( cmd_opts, args ) = opt_parser.parse_args() - self.__glade_file = os.path.join( os.path.dirname( sys.argv[0] ), "gui-1.3.glade" ) + self.__glade_file = os.path.join( os.path.dirname( sys.argv[0] ), "ui/gui-1.3.glade" ) self.__widgets["interface"] = gtk.glade.XML( self.__glade_file, "window1", APP ) self.__widgets["main_window"] = self.__widgets["interface"].get_widget("window1") @@ -480,7 +486,7 @@ class interface : self.gui_status_message( error_msg ) except : - # Failed to get informations from the treeview... skip action + # Failed to get information from the treeview... skip action pass #------------------------------------------------------------------- @@ -528,6 +534,9 @@ class interface : return try : + if ( not stat.S_IMODE( os.stat( self.__favorites_path ).st_mode ) == self.DESIRED_FAVORITES_DIRECTORY_MODE ) : # unsafe pre-1.2 directory found + os.chmod( self.__favorites_path, self.DESIRED_FAVORITES_DIRECTORY_MODE ) + conf = ConfigParser.ConfigParser() conf.read( self.__favorites_file ) for current in conf.sections() : @@ -573,7 +582,7 @@ class interface : # If path does not exists, try to create it if ( not os.path.exists( self.__favorites_file ) ) : try : - os.makedirs( self.__favorites_path, mode=0700 ) + os.makedirs( self.__favorites_path, mode=self.DESIRED_FAVORITES_DIRECTORY_MODE ) except : self.gui_status_message( _("Error while creating configuration folder (%s)") % sys.exc_info()[1] ) @@ -804,7 +813,7 @@ class gui_updater( threading.Thread ) : self.__parent_class.gui_status_notification( _("Device is running on batteries"), "on_battery.png" ) was_online = False - # Check for additionnal informations + # Check for additionnal information for k,v in status_mapper.iteritems() : if vars.get("ups.status").find(k) != -1 : if ( text_right != "" ) : @@ -856,7 +865,7 @@ class gui_updater( threading.Thread ) : self.__parent_class._interface__widgets["progress_battery_load"].set_text( _("Not available") ) if ( vars.has_key( "battery.runtime" ) ) : - autonomy = int(vars.get("battery.runtime",0)) + autonomy = int( float( vars.get( "battery.runtime", 0 ) ) ) if ( autonomy >= 3600 ) : info = time.strftime( _("%H hours %M minutes %S seconds"), time.gmtime( autonomy ) ) diff --git a/scripts/python/app/NUT-Monitor-py3qt5.in b/scripts/python/app/NUT-Monitor-py3qt5.in new file mode 100755 index 0000000..cf1c3b6 --- /dev/null +++ b/scripts/python/app/NUT-Monitor-py3qt5.in @@ -0,0 +1,944 @@ +#!@PYTHON3@ +# -*- coding: utf-8 -*- + +# 2009-12-27 David Goncalves - Version 1.2 +# Total rewrite of NUT-Monitor to optimize GUI interaction. +# Added favorites support (saved to user's home) +# Added status icon on the notification area +# +# 2010-02-26 David Goncalves +# Added UPS vars display and the possibility to change values +# when user double-clicks on a RW var. +# +# 2010-05-01 David Goncalves +# Added support for PyNotify (if available) +# +# 2010-05-05 David Goncalves +# Added support for command line options +# -> --start-hidden +# -> --favorite +# +# NUT-Monitor now tries to detect if there is a NUT server +# on localhost and if there is 1 UPS, connects to it. +# +# 2010-10-06 David Goncalves - Version 1.3 +# Added localisation support +# +# 2015-02-14 Michal Fincham - Version 1.3.1 +# Corrected unsafe permissions on ~/.nut-monitor (Debian #777706) +# +# 2022-02-20 Luke Dashjr - Version 2.0 +# Port to Python 3 with PyQt5. + + +import PyQt5.uic +from PyQt5.QtCore import * +from PyQt5.QtGui import * +from PyQt5.QtWidgets import * +import sys +import base64 +import os, os.path +import stat +import platform +import time +import threading +import optparse +import configparser +import locale +import gettext +import PyNUT + + +class interface : + + DESIRED_FAVORITES_DIRECTORY_MODE = 0o700 + + __widgets = {} + __callbacks = {} + __favorites = {} + __favorites_file = None + __favorites_path = "" + __fav_menu_items = list() + __window_visible = True + __ui_file = None + __connected = False + __ups_handler = None + __ups_commands = None + __ups_vars = None + __ups_rw_vars = None + __gui_thread = None + __current_ups = None + + def __init__( self, argv ) : + + # Before anything, parse command line options if any present... + opt_parser = optparse.OptionParser() + opt_parser.add_option( "-H", "--start-hidden", action="store_true", default=False, dest="hidden", help="Start iconified in tray" ) + opt_parser.add_option( "-F", "--favorite", dest="favorite", help="Load the specified favorite and connect to UPS" ) + + ( cmd_opts, args ) = opt_parser.parse_args() + + + self.__app = QApplication( argv ) + + self.__ui_file = self.__find_res_file( 'ui', "window1.ui" ) + + self.__widgets["interface"] = PyQt5.uic.loadUi( self.__ui_file ) + self.__widgets["main_window"] = self.__widgets["interface"] + self.__widgets["status_bar"] = self.__widgets["interface"].statusbar2 + self.__widgets["ups_host_entry"] = self.__widgets["interface"].entry1 + self.__widgets["ups_port_entry"] = self.__widgets["interface"].spinbutton1 + self.__widgets["ups_refresh_button"] = self.__widgets["interface"].button1 + self.__widgets["ups_authentication_check"] = self.__widgets["interface"].checkbutton1 + self.__widgets["ups_authentication_frame"] = self.__widgets["interface"].hbox1 + self.__widgets["ups_authentication_login"] = self.__widgets["interface"].entry2 + self.__widgets["ups_authentication_password"] = self.__widgets["interface"].entry3 + self.__widgets["ups_list_combo"] = self.__widgets["interface"].combobox1 + self.__widgets["ups_commands_combo"] = self.__widgets["interface"].ups_commands_combo + self.__widgets["ups_commands_button"] = self.__widgets["interface"].button8 + self.__widgets["ups_connect"] = self.__widgets["interface"].button2 + self.__widgets["ups_disconnect"] = self.__widgets["interface"].button7 + self.__widgets["ups_params_box"] = self.__widgets["interface"].vbox6 + self.__widgets["ups_infos"] = self.__widgets["interface"].notebook1 + self.__widgets["ups_vars_tree"] = self.__widgets["interface"].treeview1 + self.__widgets["ups_vars_refresh"] = self.__widgets["interface"].button9 + self.__widgets["ups_status_image"] = self.__widgets["interface"].image1 + self.__widgets["ups_status_left"] = self.__widgets["interface"].label10 + self.__widgets["ups_status_right"] = self.__widgets["interface"].label11 + self.__widgets["ups_status_time"] = self.__widgets["interface"].label15 + self.__widgets["menu_favorites_root"] = self.__widgets["interface"].menu2 + self.__widgets["menu_favorites"] = self.__widgets["interface"].menu2 + self.__widgets["menu_favorites_add"] = self.__widgets["interface"].menuitem4 + self.__widgets["menu_favorites_del"] = self.__widgets["interface"].menuitem5 + self.__widgets["progress_battery_charge"] = self.__widgets["interface"].progressbar1 + self.__widgets["progress_battery_load"] = self.__widgets["interface"].progressbar2 + + # Create the tray icon and connect it to the show/hide method... + self.__widgets["status_icon"] = QSystemTrayIcon( QIcon( self.__find_res_file( "pixmaps", "on_line.png" ) ) ) + self.__widgets["status_icon"].setVisible( True ) + self.__widgets["status_icon"].activated.connect( self.tray_activated ) + + self.__widgets["ups_status_image"].setPixmap( QPixmap( self.__find_res_file( "pixmaps", "on_line.png" ) ) ) + + # Connect interface callbacks actions + self.__widgets["main_window"].destroyed.connect( self.quit ) + self.__widgets["interface"].imagemenuitem1.triggered.connect( self.gui_about_dialog ) + self.__widgets["interface"].imagemenuitem5.triggered.connect( self.quit ) + self.__widgets["ups_host_entry"].textChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_authentication_login"].textChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_authentication_password"].textChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_authentication_check"].stateChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_port_entry"].valueChanged.connect( self.__check_gui_fields ) + self.__widgets["ups_refresh_button"].clicked.connect( self.__update_ups_list ) + self.__widgets["ups_connect"].clicked.connect( self.connect_to_ups ) + self.__widgets["ups_disconnect"].clicked.connect( self.disconnect_from_ups ) + self.__widgets["ups_vars_refresh"].clicked.connect( self.__gui_update_ups_vars_view ) + self.__widgets["menu_favorites_add"].triggered.connect( self.__gui_add_favorite ) + self.__widgets["menu_favorites_del"].triggered.connect( self.__gui_delete_favorite ) + self.__widgets["ups_vars_tree"].doubleClicked.connect( self.__gui_ups_vars_selected ) + + # Remove the dummy combobox entry on UPS List and Commands + self.__widgets["ups_list_combo"].removeItem( 0 ) + + # Set UPS vars treeview properties ----------------------------- + store = QStandardItemModel( 0, 3, self.__widgets["ups_vars_tree"] ) + self.__widgets["ups_vars_tree"].setModel( store ) + self.__widgets["ups_vars_tree"].setHeaderHidden( False ) + self.__widgets["ups_vars_tree"].setRootIsDecorated( False ) + + # Column 0 + store.setHeaderData( 0, Qt.Horizontal, '' ) + + # Column 1 + store.setHeaderData( 1, Qt.Horizontal, _('Var name') ) + + # Column 2 + store.setHeaderData( 2, Qt.Horizontal, _('Value') ) + self.__widgets["ups_vars_tree"].header().setStretchLastSection( True ) + + self.__widgets["ups_vars_tree"].sortByColumn( 1, Qt.AscendingOrder ) + self.__widgets["ups_vars_tree_store"] = store + + self.__widgets["ups_vars_tree"].setMinimumSize( 0, 50 ) + #--------------------------------------------------------------- + + # UPS Commands combo box creation ------------------------------ + ups_commands_height = self.__widgets["ups_commands_combo"].size().height() * 2 + self.__widgets["ups_commands_combo"].setMinimumSize(0, ups_commands_height) + self.__widgets["ups_commands_combo"].setCurrentIndex( 0 ) + + self.__widgets["ups_commands_button"].setMinimumSize(0, ups_commands_height) + self.__widgets["ups_commands_button"].clicked.connect( self.__gui_send_ups_command ) + + self.__widgets["ups_commands_combo_store"] = self.__widgets["ups_commands_combo"] + #--------------------------------------------------------------- + + self.gui_init_unconnected() + + if ( cmd_opts.hidden != True ) : + self.__widgets["main_window"].show() + + # Define favorites path and load favorites + if ( platform.system() == "Linux" ) : + self.__favorites_path = os.path.join( os.environ.get("HOME"), ".nut-monitor" ) + elif ( platform.system() == "Windows" ) : + self.__favorites_path = os.path.join( os.environ.get("USERPROFILE"), "Application Data", "NUT-Monitor" ) + + self.__favorites_file = os.path.join( self.__favorites_path, "favorites.ini" ) + self.__parse_favorites() + + self.gui_status_message( _("Welcome to NUT Monitor") ) + + if ( cmd_opts.favorite != None ) : + if ( cmd_opts.favorite in self.__favorites ) : + self.__gui_load_favorite( fav_name=cmd_opts.favorite ) + self.connect_to_ups() + else : + # Try to scan localhost for available ups and connect to it if there is only one + self.__widgets["ups_host_entry"].setText( "localhost" ) + self.__update_ups_list() + if self.__widgets["ups_list_combo"].count() == 1: + self.connect_to_ups() + + def exec( self ) : + self.__app.exec() + + def __find_res_file( self, ftype, filename ) : + filename = os.path.join( ftype, filename ) + # TODO: Skip checking application directory if installed + path = os.path.join( os.path.dirname( sys.argv[0] ), filename ) + if os.path.exists(path): + return path + path = QStandardPaths.locate(QStandardPaths.AppDataLocation, filename) + if os.path.exists(path): + return path + raise RuntimeError("Cannot find %s resource %s" % (ftype, filename)) + + def __find_icon_file( self ) : + filename = 'nut-monitor.png' + # TODO: Skip checking application directory if installed + path = os.path.join( os.path.dirname( sys.argv[0] ), "icons", "256x256", filename ) + if os.path.exists(path): + return path + path = QStandardPaths.locate(QStandardPaths.AppDataLocation, os.path.join( "icons", "hicolor", "256x256", "apps", filename ) ) + if os.path.exists(path): + return path + raise RuntimeError("Cannot find %s resource %s" % ('icon', filename)) + + # Check if correct fields are filled to enable connection to the UPS + def __check_gui_fields( self, widget=None ) : + # If UPS list contains something, clear it + if self.__widgets["ups_list_combo"].currentIndex() != -1 : + self.__widgets["ups_list_combo"].clear() + self.__widgets["ups_connect"].setEnabled( False ) + self.__widgets["menu_favorites_add"].setEnabled( False ) + + # Host/Port selection + if len( self.__widgets["ups_host_entry"].text() ) > 0 : + sensitive = True + + # If authentication is selected, check that we have a login and password + if self.__widgets["ups_authentication_check"].isChecked() : + if len( self.__widgets["ups_authentication_login"].text() ) == 0 : + sensitive = False + + if len( self.__widgets["ups_authentication_password"].text() ) == 0 : + sensitive = False + + self.__widgets["ups_refresh_button"].setEnabled( sensitive ) + if not sensitive : + self.__widgets["ups_connect"].setEnabled( False ) + self.__widgets["menu_favorites_add"].setEnabled( False ) + else : + self.__widgets["ups_refresh_button"].setEnabled( False ) + self.__widgets["ups_connect"].setEnabled( False ) + self.__widgets["menu_favorites_add"].setEnabled( False ) + + # Use authentication fields... + if self.__widgets["ups_authentication_check"].isChecked() : + self.__widgets["ups_authentication_frame"].setEnabled( True ) + else : + self.__widgets["ups_authentication_frame"].setEnabled( False ) + + self.gui_status_message() + + #------------------------------------------------------------------- + # This method is used to show/hide the main window when user clicks on the tray icon + def tray_activated( self, widget=None, data=None ) : + if self.__window_visible : + self.__widgets["main_window"].hide() + else : + self.__widgets["main_window"].show() + + self.__window_visible = not self.__window_visible + + #------------------------------------------------------------------- + # Change the status icon and tray icon + def change_status_icon( self, icon="on_line", blink=False ) : + self.__widgets["status_icon"].setIcon( QIcon( self.__find_res_file( "pixmaps", "%s.png" % icon ) ) ) + self.__widgets["ups_status_image"].setPixmap( QPixmap( self.__find_res_file( "pixmaps", "%s.png" % icon ) ) ) + # TODO self.__widgets["status_icon"].set_blinking( blink ) + + #------------------------------------------------------------------- + # This method connects to the NUT server and retrieve availables UPSes + # using connection parameters (host, port, login, pass...) + def __update_ups_list( self, widget=None ) : + + host = self.__widgets["ups_host_entry"].text() + port = int( self.__widgets["ups_port_entry"].value() ) + login = None + password = None + + if self.__widgets["ups_authentication_check"].isChecked() : + login = self.__widgets["ups_authentication_login"].text() + password = self.__widgets["ups_authentication_password"].text() + + try : + nut_handler = PyNUT.PyNUTClient( host=host, port=port, login=login, password=password ) + upses = nut_handler.GetUPSList() + + ups_list = list(key.decode('ascii') for key in upses.keys()) + ups_list.sort() + + # If UPS list contains something, clear it + self.__widgets["ups_list_combo"].clear() + + for current in ups_list : + self.__widgets["ups_list_combo"].addItem( current ) + + self.__widgets["ups_list_combo"].setCurrentIndex( 0 ) + + self.__widgets["ups_connect"].setEnabled( True ) + self.__widgets["menu_favorites_add"].setEnabled( True ) + + self.gui_status_message( _("Found {0} devices on {1}").format( len( ups_list ), host ) ) + + except : + error_msg = _("Error connecting to '{0}' ({1})").format( host, sys.exc_info()[1] ) + self.gui_status_message( error_msg ) + + #------------------------------------------------------------------- + # Quit program + def quit( self, widget=None ) : + # If we are connected to an UPS, disconnect first... + if self.__connected : + self.gui_status_message( _("Disconnecting from device") ) + self.disconnect_from_ups() + + self.__app.quit() + + #------------------------------------------------------------------- + # Method called when user wants to add a new favorite entry. It + # displays a dialog to enable user to select the name of the favorite + def __gui_add_favorite( self, widget=None ) : + dialog_ui_file = self.__find_res_file( 'ui', "dialog1.ui" ) + dialog = PyQt5.uic.loadUi( dialog_ui_file ) + + # Define interface callbacks actions + def check_entry(val): + if self.__gui_add_favorite_check_gui_fields(val): + dialog.buttonBox.button(QDialogButtonBox.Ok).setEnabled( True ) + else: + dialog.buttonBox.button(QDialogButtonBox.Ok).setEnabled( False ) + dialog.entry4.textChanged.connect( check_entry ) + + self.__widgets["main_window"].setEnabled( False ) + rc = dialog.exec() + if rc == QDialog.Accepted : + fav_data = {} + fav_data["host"] = self.__widgets["ups_host_entry"].text() + fav_data["port"] = "%d" % self.__widgets["ups_port_entry"].value() + fav_data["ups"] = self.__widgets["ups_list_combo"].currentText() + fav_data["auth"] = self.__widgets["ups_authentication_check"].isChecked() + if fav_data["auth"] : + fav_data["login"] = self.__widgets["ups_authentication_login"].text() + fav_data["password"] = base64.b64encode( self.__widgets["ups_authentication_password"].text().encode('ascii') ).decode('ascii') + + fav_name = dialog.entry4.text() + self.__favorites[ fav_name ] = fav_data + self.__gui_refresh_favorites_menu() + + # Save all favorites + self.__save_favorites() + + self.__widgets["main_window"].setEnabled( True ) + + #------------------------------------------------------------------- + # Method called when user wants to delete an entry from favorites + def __gui_delete_favorite( self, widget=None ) : + dialog_ui_file = self.__find_res_file( 'ui', "dialog2.ui" ) + dialog = PyQt5.uic.loadUi( dialog_ui_file ) + + # Remove the dummy combobox entry on list + dialog.combobox2.removeItem( 0 ) + + favs = list(self.__favorites.keys()) + favs.sort() + for current in favs : + dialog.combobox2.addItem( current ) + + dialog.combobox2.setCurrentIndex( 0 ) + + self.__widgets["main_window"].setEnabled( False ) + rc = dialog.exec() + fav_name = dialog.combobox2.currentText() + self.__widgets["main_window"].setEnabled( True ) + + if ( rc == QDialog.Accepted ) : + # Remove entry, show confirmation dialog + resp = QMessageBox.question( None, self.__widgets["main_window"].windowTitle(), _("Are you sure that you want to remove this favorite ?"), QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes ) + + if ( resp == QMessageBox.Yes ) : + del self.__favorites[ fav_name ] + self.__gui_refresh_favorites_menu() + self.__save_favorites() + self.gui_status_message( _("Removed favorite '%s'") % fav_name ) + + #------------------------------------------------------------------- + # Method called when user selects a favorite from the favorites menu + def __gui_load_favorite( self, fav_name="" ) : + + if ( fav_name in self.__favorites ) : + # If auth is activated, process it before other fields to avoir weird + # reactions with the 'check_gui_fields' function. + if ( self.__favorites[fav_name].get("auth", False ) ) : + self.__widgets["ups_authentication_check"].setChecked( True ) + self.__widgets["ups_authentication_login"].setText( self.__favorites[fav_name].get("login","") ) + self.__widgets["ups_authentication_password"].setText( self.__favorites[fav_name].get("password","") ) + + self.__widgets["ups_host_entry"].setText( self.__favorites[fav_name].get("host","") ) + self.__widgets["ups_port_entry"].setValue( int( self.__favorites[fav_name].get( "port", 3493 ) ) ) + + # Clear UPS list and add current UPS name + self.__widgets["ups_list_combo"].clear() + + self.__widgets["ups_list_combo"].addItem( self.__favorites[fav_name].get("ups","") ) + self.__widgets["ups_list_combo"].setCurrentIndex( 0 ) + + # Activate the connect button + self.__widgets["ups_connect"].setEnabled( True ) + + self.gui_status_message( _("Loaded '%s'") % fav_name ) + + #------------------------------------------------------------------- + # Send the selected command to the UPS + def __gui_send_ups_command( self, widget=None ) : + offset = self.__widgets["ups_commands_combo"].currentIndex() + cmd = self.__ups_commands[ offset ].decode('ascii') + + self.__widgets["main_window"].setEnabled( False ) + resp = QMessageBox.question( None, self.__widgets["main_window"].windowTitle(), _("Are you sure that you want to send '%s' to the device ?") % cmd, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes ) + self.__widgets["main_window"].setEnabled( True ) + + if ( resp == QMessageBox.Yes ) : + try : + self.__ups_handler.RunUPSCommand( self.__current_ups, cmd ) + self.gui_status_message( _("Sent '{0}' command to {1}").format( cmd, self.__current_ups ) ) + + except : + self.gui_status_message( _("Failed to send '{0}' ({1})").format( cmd, sys.exc_info()[1] ) ) + + #------------------------------------------------------------------- + # Method called when user clicks on the UPS vars treeview. If the user + # performs a double click on a RW var, the GUI shows the update var dialog. + def __gui_ups_vars_selected( self, index ) : + if True : + model = self.__widgets["ups_vars_tree_store"] + try : + ups_var = model.data( index.siblingAtColumn(1) ).encode('ascii') + if ( ups_var in self.__ups_rw_vars ) : + # The selected var is RW, then we can show the update dialog + + cur_val = self.__ups_rw_vars.get(ups_var).decode('ascii') + + self.__widgets["main_window"].setEnabled( False ) + new_val, rc = QInputDialog.getText( None, self.__widgets["main_window"].windowTitle(), _("Enter a new value for the variable.

{0} = {1} (current value)").format( ups_var, cur_val), QLineEdit.Normal, cur_val ) + self.__widgets["main_window"].setEnabled( True ) + + if ( rc ) : + try : + self.__ups_handler.SetRWVar( ups=self.__current_ups, var=ups_var.decode('ascii'), value=new_val ) + self.gui_status_message( _("Updated variable on %s") % self.__current_ups ) + + # Change the value on the local dict to update the GUI + new_val = new_val.encode('ascii') + self.__ups_vars[ups_var] = new_val + self.__ups_rw_vars[ups_var] = new_val + self.__gui_update_ups_vars_view() + + except : + error_msg = _("Error updating variable on '{0}' ({1})").format( self.__current_ups, sys.exc_info()[1] ) + self.gui_status_message( error_msg ) + + else : + # User cancelled modification... + error_msg = _("No variable modified on %s - User cancelled") % self.__current_ups + self.gui_status_message( error_msg ) + + except : + # Failed to get information from the treeview... skip action + pass + + #------------------------------------------------------------------- + # Refresh the content of the favorites menu according to the defined favorites + def __gui_refresh_favorites_menu( self ) : + for current in self.__fav_menu_items : + self.__widgets["menu_favorites"].removeAction(current) + + self.__fav_menu_items = list() + + items = list(self.__favorites.keys()) + items.sort() + + for current in items : + menu_item = QAction( current ) + self.__fav_menu_items.append( menu_item ) + self.__widgets["menu_favorites"].addAction( menu_item ) + + menu_item.triggered.connect( lambda: self.__gui_load_favorite( current ) ) + + if len( items ) > 0 : + self.__widgets["menu_favorites_del"].setEnabled( True ) + else : + self.__widgets["menu_favorites_del"].setEnabled( False ) + + #------------------------------------------------------------------- + # In 'add favorites' dialog, this method compares the content of the + # text widget representing the name of the new favorite with existing + # ones. If they match, the 'add' button will be set to non sensitive + # to avoid creating entries with the same name. + def __gui_add_favorite_check_gui_fields( self, fav_name ) : + if ( len( fav_name ) > 0 ) and ( fav_name not in list(self.__favorites.keys()) ) : + return True + else : + return False + + #------------------------------------------------------------------- + # Load and parse favorites + def __parse_favorites( self ) : + + if ( not os.path.exists( self.__favorites_file ) ) : + # There is no favorites files, do nothing + return + + try : + if ( not stat.S_IMODE( os.stat( self.__favorites_path ).st_mode ) == self.DESIRED_FAVORITES_DIRECTORY_MODE ) : # unsafe pre-1.2 directory found + os.chmod( self.__favorites_path, self.DESIRED_FAVORITES_DIRECTORY_MODE ) + + conf = configparser.ConfigParser() + conf.read( self.__favorites_file ) + for current in conf.sections() : + # Check if mandatory fields are present + if ( conf.has_option( current, "host" ) and conf.has_option( current, "ups" ) ) : + # Valid entry found, add it to the list + fav_data = {} + fav_data["host"] = conf.get( current, "host" ) + fav_data["ups"] = conf.get( current, "ups" ) + + if ( conf.has_option( current, "port" ) ) : + fav_data["port"] = conf.get( current, "port" ) + else : + fav_data["port"] = "3493" + + # If auth is defined the section must have login and pass defined + if ( conf.has_option( current, "auth" ) ) : + if( conf.has_option( current, "login" ) and conf.has_option( current, "password" ) ) : + # Add the entry + fav_data["auth"] = conf.getboolean( current, "auth" ) + fav_data["login"] = conf.get( current, "login" ) + + try : + fav_data["password"] = base64.decodebytes( conf.get( current, "password" ).encode('ascii') ).decode('ascii') + + except : + # If the password is not in base64, let the field empty + print(( _("Error parsing favorites, password for '%s' is not in base64\nSkipping password for this entry") % current )) + fav_data["password"] = "" + else : + fav_data["auth"] = False + + self.__favorites[current] = fav_data + self.__gui_refresh_favorites_menu() + + except : + self.gui_status_message( _("Error while parsing favorites file (%s)") % sys.exc_info()[1] ) + + #------------------------------------------------------------------- + # Save favorites to the defined favorites file using ini format + def __save_favorites( self ) : + + # If path does not exists, try to create it + if ( not os.path.exists( self.__favorites_file ) ) : + try : + os.makedirs( self.__favorites_path, mode=self.DESIRED_FAVORITES_DIRECTORY_MODE, exist_ok=True ) + except : + self.gui_status_message( _("Error while creating configuration folder (%s)") % sys.exc_info()[1] ) + + save_conf = configparser.ConfigParser() + for current in list(self.__favorites.keys()) : + save_conf.add_section( current ) + for k, v in self.__favorites[ current ].items() : + if isinstance( v, bool ) : + v = str( v ) + save_conf.set( current, k, v ) + + try : + fh = open( self.__favorites_file, "w" ) + save_conf.write( fh ) + fh.close() + self.gui_status_message( _("Saved favorites...") ) + + except : + self.gui_status_message( _("Error while saving favorites (%s)") % sys.exc_info()[1] ) + + #------------------------------------------------------------------- + # Display the about dialog + def gui_about_dialog( self, widget=None ) : + self.__widgets["main_window"].adjustSize() + dialog_ui_file = self.__find_res_file( 'ui', "aboutdialog1.ui" ) + + dialog = PyQt5.uic.loadUi( dialog_ui_file ) + dialog.icon.setPixmap( QPixmap( self.__find_icon_file() ) ) + + credits_button = QPushButton( dialog ) + credits_button.setText( _("C&redits") ) + credits_button.setIcon( dialog.style().standardIcon( QStyle.SP_MessageBoxInformation ) ) + credits_button.clicked.connect( self.gui_about_credits ) + + licence_button = QPushButton( dialog ) + licence_button.setText( _("&Licence") ) + licence_button.clicked.connect( self.gui_about_licence ) + + dialog.buttonBox.addButton( credits_button, QDialogButtonBox.HelpRole ) + dialog.buttonBox.addButton( licence_button, QDialogButtonBox.HelpRole ) + + self.__widgets["main_window"].setEnabled( False ) + dialog.exec() + self.__widgets["main_window"].setEnabled( True ) + + def gui_about_credits( self ) : + QMessageBox.about( None, _("Credits"), _(""" +Written by: +David Goncalves + +Translated by: +David Goncalves - Français +Daniele Pezzini - Italiano + """).strip() ) + + def gui_about_licence( self ) : + QMessageBox.about( None, _("Licence"), _(""" +Copyright (C) 2010 David Goncalves + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + """).strip() ) + + #------------------------------------------------------------------- + # Display a message on the status bar. The message is also set as + # tooltip to enable users to see long messages. + def gui_status_message( self, msg="" ) : + text = msg + + message_id = self.__widgets["status_bar"].showMessage( text.replace("\n", "") ) + self.__widgets["status_bar"].setToolTip( text ) + + #------------------------------------------------------------------- + # Display a notification using QSystemTrayIcon with an optional icon + def gui_status_notification( self, message="", icon_file="" ) : + if ( icon_file != "" ) : + icon = QIcon( os.path.abspath( self.__find_res_file( "pixmaps", icon_file ) ) ) + else : + icon = None + + self.__widgets["status_icon"].showMessage( "NUT Monitor", message, icon ) + + #------------------------------------------------------------------- + # Connect to the selected UPS using parameters (host,port,login,pass) + def connect_to_ups( self, widget=None ) : + + host = self.__widgets["ups_host_entry"].text() + port = int( self.__widgets["ups_port_entry"].value() ) + login = None + password = None + + if self.__widgets["ups_authentication_check"].isChecked() : + login = self.__widgets["ups_authentication_login"].text() + password = self.__widgets["ups_authentication_password"].text() + + try : + self.__ups_handler = PyNUT.PyNUTClient( host=host, port=port, login=login, password=password ) + + except : + self.gui_status_message( _("Error connecting to '{0}' ({1})").format( host, sys.exc_info()[1] ) ) + self.gui_status_notification( _("Error connecting to '{0}'\n{1}").format( host, sys.exc_info()[1] ), "warning.png" ) + return + + # Check if selected UPS exists on server... + srv_upses = self.__ups_handler.GetUPSList() + self.__current_ups = self.__widgets["ups_list_combo"].currentText() + + if self.__current_ups.encode('ascii') not in srv_upses : + self.gui_status_message( _("Device '%s' not found on server") % self.__current_ups ) + self.gui_status_notification( _("Device '%s' not found on server") % self.__current_ups, "warning.png" ) + return + + self.__connected = True + self.__widgets["ups_connect"].hide() + self.__widgets["ups_disconnect"].show() + self.__widgets["ups_infos"].show() + self.__widgets["ups_params_box"].setEnabled( False ) + self.__widgets["menu_favorites_root"].setEnabled( False ) + self.__widgets["ups_params_box"].hide() + + commands = self.__ups_handler.GetUPSCommands( self.__current_ups ) + self.__ups_commands = list(commands.keys()) + self.__ups_commands.sort() + + # Refresh UPS commands combo box + self.__widgets["ups_commands_combo_store"].clear() + for desc in self.__ups_commands : + # TODO: Style as "%s
%s" + self.__widgets["ups_commands_combo_store"].addItem( "%s\n%s" % ( desc.decode('ascii'), commands[desc].decode('ascii') ) ) + + self.__widgets["ups_commands_combo"].setCurrentIndex( 0 ) + + # Update UPS vars manually before the thread + self.__ups_vars = self.__ups_handler.GetUPSVars( self.__current_ups ) + self.__ups_rw_vars = self.__ups_handler.GetRWVars( self.__current_ups ) + self.__gui_update_ups_vars_view() + + # Try to resize the main window... + # FIXME: For some reason, calling this immediately doesn't work right + QTimer.singleShot(10, self.__widgets["main_window"].adjustSize) + + # Start the GUI updater thread + self.__gui_thread = gui_updater( self ) + self.__gui_thread.start() + + self.gui_status_message( _("Connected to '{0}' on {1}").format( self.__current_ups, host ) ) + + + #------------------------------------------------------------------- + # Refresh UPS vars in the treeview + def __gui_update_ups_vars_view( self, widget=None ) : + if self.__ups_handler : + vars = self.__ups_vars + rwvars = self.__ups_rw_vars + + self.__widgets["ups_vars_tree_store"].removeRows(0, self.__widgets["ups_vars_tree_store"].rowCount()) + + for k,v in vars.items() : + if ( k in rwvars ) : + icon_file = self.__find_res_file( "pixmaps", "var-rw.png" ) + else : + icon_file = self.__find_res_file( "pixmaps", "var-ro.png" ) + + icon = QIcon( icon_file ) + item_icon = QStandardItem(icon, '') + item_icon.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) + item_var_name = QStandardItem( k.decode('ascii') ) + item_var_name.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) + item_var_val = QStandardItem( v.decode('ascii') ) + item_var_val.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren) + self.__widgets["ups_vars_tree_store"].appendRow( (item_icon, item_var_name, item_var_val) ) + self.__widgets["ups_vars_tree"].resizeColumnToContents( 0 ) + self.__widgets["ups_vars_tree"].resizeColumnToContents( 1 ) + + + def gui_init_unconnected( self ) : + self.__connected = False + self.__widgets["ups_connect"].show() + self.__widgets["ups_disconnect"].hide() + self.__widgets["ups_infos"].hide() + self.__widgets["ups_params_box"].setEnabled( True ) + self.__widgets["menu_favorites_root"].setEnabled( True ) + self.__widgets["status_icon"].setToolTip( _("Not connected") ) + self.__widgets["ups_params_box"].show() + + # Try to resize the main window... + self.__widgets["main_window"].adjustSize() + + #------------------------------------------------------------------- + # Disconnect from the UPS + def disconnect_from_ups( self, widget=None ) : + self.gui_init_unconnected() + + # Stop the GUI updater thread + self.__gui_thread.stop_thread() + + del self.__ups_handler + self.gui_status_message( _("Disconnected from '%s'") % self.__current_ups ) + self.change_status_icon( "on_line", blink=False ) + self.__current_ups = None + +#----------------------------------------------------------------------- +# GUI Updater class +# This class updates the main gui with data from connected UPS +class gui_updater : + + __parent_class = None + __stop_thread = False + + def __init__( self, parent_class ) : + threading.Thread.__init__( self ) + self.__parent_class = parent_class + + def start( self ) : + self.__timer = QTimer() + self.__timer.timeout.connect(self.__update) + self.__timer.start(1000) + + def __update( self ) : + + ups = self.__parent_class._interface__current_ups + was_online = True + + # Define a dict containing different UPS status + status_mapper = { b"LB" : "%s" % _("Low batteries"), + b"RB" : "%s" % _("Replace batteries !"), + b"BYPASS" : "Bypass %s" % _("(no battery protection)"), + b"CAL" : _("Performing runtime calibration"), + b"OFF" : "%s (%s)" % ( _("Offline"), _("not providing power to the load") ), + b"OVER" : "%s (%s)" % ( _("Overloaded !"), _("there is too much load for device") ), + b"TRIM" : _("Triming (UPS is triming incoming voltage)"), + b"BOOST" : _("Boost (UPS is boosting incoming voltage)") + } + + if not self.__stop_thread : + try : + vars = self.__parent_class._interface__ups_handler.GetUPSVars( ups ) + self.__parent_class._interface__ups_vars = vars + + # Text displayed on the status frame + text_left = "" + text_right = "" + status_text = "" + + text_left += "%s
" % _("Device status :") + + if ( vars.get(b"ups.status").find(b"OL") != -1 ) : + text_right += "%s" % _("Online") + if not was_online : + self.__parent_class.change_status_icon( "on_line", blink=False ) + was_online = True + + if ( vars.get(b"ups.status").find(b"OB") != -1 ) : + text_right += "%s" % _("On batteries") + if was_online : + self.__parent_class.change_status_icon( "on_battery", blink=True ) + self.__parent_class.gui_status_notification( _("Device is running on batteries"), "on_battery.png" ) + was_online = False + + # Check for additionnal information + for k,v in status_mapper.items() : + if vars.get(b"ups.status").find(k) != -1 : + if ( text_right != "" ) : + text_right += " - %s" % v + else : + text_right += "%s" % v + + # CHRG and DISCHRG cannot be trated with the previous loop ;) + if ( vars.get(b"ups.status").find(b"DISCHRG") != -1 ) : + text_right += " - %s" % _("discharging") + elif ( vars.get(b"ups.status").find(b"CHRG") != -1 ) : + text_right += " - %s" % _("charging") + + status_text += text_right + text_right += "
" + + if ( b"ups.mfr" in vars ) : + text_left += "%s

" % _("Model :") + text_right += "%s
%s
" % ( + vars.get(b"ups.mfr",b"").decode('ascii'), + vars.get(b"ups.model",b"").decode('ascii'), + ) + + if ( b"ups.temperature" in vars ) : + text_left += "%s
" % _("Temperature :") + text_right += "%s
" % int( float( vars.get( b"ups.temperature", 0 ) ) ) + + if ( b"battery.voltage" in vars ) : + text_left += "%s
" % _("Battery voltage :") + text_right += "%sv
" % (vars.get( b"battery.voltage", 0 ).decode('ascii'),) + + self.__parent_class._interface__widgets["ups_status_left"].setText( text_left[:-4] ) + self.__parent_class._interface__widgets["ups_status_right"].setText( text_right[:-4] ) + + # UPS load and battery charge progress bars + self.__parent_class._interface__widgets["progress_battery_charge"].setRange( 0, 100 ) + if ( b"battery.charge" in vars ) : + charge = vars.get( b"battery.charge", "0" ) + self.__parent_class._interface__widgets["progress_battery_charge"].setValue( int( float( charge ) ) ) + self.__parent_class._interface__widgets["progress_battery_charge"].resetFormat() + status_text += "
%s %s%%" % ( _("Battery charge :"), int( float( charge ) ) ) + else : + self.__parent_class._interface__widgets["progress_battery_charge"].setValue( 0 ) + self.__parent_class._interface__widgets["progress_battery_charge"].setFormat( _("Not available") ) + # FIXME: Some themes don't draw text, so swap it with a QLabel? + + self.__parent_class._interface__widgets["progress_battery_load"].setRange( 0, 100 ) + if ( b"ups.load" in vars ) : + load = vars.get( b"ups.load", "0" ) + self.__parent_class._interface__widgets["progress_battery_load"].setValue( int( float( load ) ) ) + self.__parent_class._interface__widgets["progress_battery_load"].resetFormat() + status_text += "
%s %s%%" % ( _("UPS load :"), int( float( load ) ) ) + else : + self.__parent_class._interface__widgets["progress_battery_load"].setValue( 0 ) + self.__parent_class._interface__widgets["progress_battery_load"].setFormat( _("Not available") ) + # FIXME: Some themes don't draw text, so swap it with a QLabel? + + if ( b"battery.runtime" in vars ) : + autonomy = int( float( vars.get( b"battery.runtime", 0 ) ) ) + + if ( autonomy >= 3600 ) : + info = time.strftime( _("%H hours %M minutes %S seconds"), time.gmtime( autonomy ) ) + elif ( autonomy > 300 ) : + info = time.strftime( _("%M minutes %S seconds"), time.gmtime( autonomy ) ) + else : + info = time.strftime( _("%M minutes %S seconds"), time.gmtime( autonomy ) ) + else : + info = _("Not available") + + self.__parent_class._interface__widgets["ups_status_time"].setText( info ) + + # Display UPS status as tooltip for tray icon + self.__parent_class._interface__widgets["status_icon"].setToolTip( status_text ) + + except : + self.__parent_class.gui_status_message( _("Error from '{0}' ({1})").format( ups, sys.exc_info()[1] ) ) + self.__parent_class.gui_status_notification( _("Error from '{0}'\n{1}").format( ups, sys.exc_info()[1] ), "warning.png" ) + + def stop_thread( self ) : + self.__timer.stop() + + +#----------------------------------------------------------------------- +# The main program starts here :-) +if __name__ == "__main__" : + + # Init the localisation + APP = "NUT-Monitor" + DIR = "locale" + + gettext.bindtextdomain( APP, DIR ) + gettext.textdomain( APP ) + _ = gettext.gettext + + for module in ( gettext, ) : + module.bindtextdomain( APP, DIR ) + module.textdomain( APP ) + + gui = interface(sys.argv) + gui.exec() + diff --git a/scripts/python/app/README b/scripts/python/app/README index 2285f2c..163c4ae 100644 --- a/scripts/python/app/README +++ b/scripts/python/app/README @@ -1,7 +1,55 @@ -NUT-Monitor is a graphical application to access and manager UPSes connected to +NUT-Monitor +=========== + +NUT-Monitor is a graphical application to access and manage UPSes connected to a NUT (Network UPS Tools) server. -This application (written in Python + GTK) uses the python-pynut class -(available at http://www.lestat.st) +Dependencies +------------ -David Goncalves +This application (variants written in Python 2 + GTK2, and in Python 3 + Qt5) +uses the python-pynut class (available at http://www.lestat.st), delivered +as PyNUT in the NUT source tree. + +Refer to your OS packaging and/or install custom modules with `pip` (or `pip3`) +to get required dependencies (GTK + GObject or QT5). + +Path to PyNUT module +-------------------- + +For quick tests (e.g. during development), you can run the clients like this: +```` +:; PYTHONPATH=../module/ python2 ./NUT-Monitor-py2gtk2.in +```` +or: +```` +:; PYTHONPATH=../module/ python3 ./NUT-Monitor-py3qt5.in +```` + +Localization +------------ + +For localized UI, also `export LANG=fr_FR.UTF-8` or `export LANG=ru_RU.UTF-8` +(see and feel welcome to improve the choice of languages in `locale` directory). + +NOTE: Currently localization only works for Python 2 client, PRs are welcome. + +Desktop menu integration +------------------------ + +This component ships both implementation-specific `nut-monitor-py2gtk2.desktop` +and `nut-monitor-py3qt5.desktop` files which allows a user to have icons for +both variants separately, as well as the legacy-named `nut-monitor.desktop` +for running the wrapper script `NUT-Monitor` which picks an implementation best +suited for current run-time circumstances. + +Kudos +----- + +NUT-Monitor and PyNUT (for Python 2 syntax) were originally authored +by David Goncalves + +NUT-Monitor was converted to Python 3 + QT5 by Luke Dashjr + +PyNUT was extended, and two variants of NUT-Monitor converged and wrapped +for Python 2+3 dual support by Jim Klimov diff --git a/scripts/python/app/icons/256x256/nut-monitor.png b/scripts/python/app/icons/256x256/nut-monitor.png new file mode 100644 index 0000000..15f472a Binary files /dev/null and b/scripts/python/app/icons/256x256/nut-monitor.png differ diff --git a/scripts/python/app/nut-monitor.png b/scripts/python/app/icons/48x48/nut-monitor.png similarity index 100% rename from scripts/python/app/nut-monitor.png rename to scripts/python/app/icons/48x48/nut-monitor.png diff --git a/scripts/python/app/icons/64x64/nut-monitor.png b/scripts/python/app/icons/64x64/nut-monitor.png new file mode 100644 index 0000000..9d92284 Binary files /dev/null and b/scripts/python/app/icons/64x64/nut-monitor.png differ diff --git a/scripts/python/app/icons/scalable/nut-monitor.svg b/scripts/python/app/icons/scalable/nut-monitor.svg new file mode 100644 index 0000000..c046562 --- /dev/null +++ b/scripts/python/app/icons/scalable/nut-monitor.svg @@ -0,0 +1,490 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + Lapo Calamandrei + + + Battery + + + battery + recharge + power + acpi + apm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/python/app/locale/it/LC_MESSAGES/NUT-Monitor.mo b/scripts/python/app/locale/it/LC_MESSAGES/NUT-Monitor.mo new file mode 100644 index 0000000..5a95c22 Binary files /dev/null and b/scripts/python/app/locale/it/LC_MESSAGES/NUT-Monitor.mo differ diff --git a/scripts/python/app/nut-monitor-py2gtk2.desktop b/scripts/python/app/nut-monitor-py2gtk2.desktop new file mode 100644 index 0000000..130cd06 --- /dev/null +++ b/scripts/python/app/nut-monitor-py2gtk2.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=NUT Monitor +Name[fr]=Moniteur NUT +Comment=Network UPS Tools GUI client (Py2Gtk2) +Comment[fr]=Client graphique pour NUT (Network UPS Tools, Py2Gtk2) +Comment[it]=Client grafico per NUT (Network UPS Tools, Py2Gtk2) +Categories=System;Monitor;HardwareSettings;Settings;GTK +Exec=NUT-Monitor-py2gtk2 +Icon=nut-monitor +Terminal=false +Type=Application diff --git a/scripts/python/app/nut-monitor-py3qt5.desktop b/scripts/python/app/nut-monitor-py3qt5.desktop new file mode 100644 index 0000000..66fcd86 --- /dev/null +++ b/scripts/python/app/nut-monitor-py3qt5.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Name=NUT Monitor +Name[fr]=Moniteur NUT +Comment=Network UPS Tools GUI client (Py3Qt5) +Comment[fr]=Client graphique pour NUT (Network UPS Tools, Py3Qt5) +Comment[it]=Client grafico per NUT (Network UPS Tools, Py3Qt5) +Categories=System;Monitor;HardwareSettings;Settings;Qt +Exec=NUT-Monitor-py3qt5 +Icon=nut-monitor +Terminal=false +Type=Application diff --git a/scripts/python/app/nut-monitor.appdata.xml b/scripts/python/app/nut-monitor.appdata.xml new file mode 100644 index 0000000..da0f163 --- /dev/null +++ b/scripts/python/app/nut-monitor.appdata.xml @@ -0,0 +1,48 @@ + + + + nut-monitor.desktop + CC0-1.0 + GPL-3.0+ + NUT Monitor +

GUI application to monitor UPS status + +

+ NUT Monitor is a GUI application to monitor UPS status, through NUT - + Network UPS Tools. + NUT is a client/server monitoring system that allows computers to + share uninterruptible power supply (UPS) and power distribution unit + (PDU) hardware. Clients access the hardware through the server, and + are notified whenever the power status changes. +

+

NUT Monitor provides the following features:

+
    +
  • Automatically connects to local UPS if there is only one managed
  • +
  • Command line options to start hidden, load a favorite, ...
  • +
  • System tray (notification area) integration, including notifications
  • +
  • Favorites, to store different devices
  • +
  • Display all device variables
  • +
  • Modify writable variables on UPS and devices
  • +
  • Support English and French
  • +
+

+ NUT Monitor requires that you have a running NUT system, that you can + connect to, either locally or remotely. + For more information on NUT: http://www.networkupstools.org/ +

+
+ + + https://www.lestat.st/_media/informatique/projets/nut-monitor/nut-monitor-1.png + + + https://www.lestat.st/_media/informatique/projets/nut-monitor/nut-monitor-2.png + + + https://www.lestat.st/_media/informatique/projets/nut-monitor/nut-monitor-3.png + + + https://www.lestat.st/en/informatique/projets/nut-monitor + david@lestat.st + + diff --git a/scripts/python/app/nut-monitor.desktop b/scripts/python/app/nut-monitor.desktop deleted file mode 100644 index 4229fb5..0000000 --- a/scripts/python/app/nut-monitor.desktop +++ /dev/null @@ -1,11 +0,0 @@ -[Desktop Entry] -Name=NUT Monitor -Name[fr]=Moniteur NUT -Comment=Network UPS Tools GUI client -Comment[fr]=Client graphique pour NUT (Network UPS Tools) -Categories=Application;Network; -Encoding=UTF-8 -Exec=NUT-Monitor -Icon=nut-monitor.png -Terminal=false -Type=Application diff --git a/scripts/python/app/ui/aboutdialog1.ui b/scripts/python/app/ui/aboutdialog1.ui new file mode 100644 index 0000000..c6a6214 --- /dev/null +++ b/scripts/python/app/ui/aboutdialog1.ui @@ -0,0 +1,108 @@ + + + aboutdialog1 + + + About NUT-Monitor + + + true + + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + <h1>NUT-Monitor 1.3.1</h1> +<p>GUI to manage devices connected a NUT server.</p> +<p>For more information about NUT (Network UPS Tools) please visit the author's website.</p> +<p style="margin-bottom: 1.5em">http://www.networkupstools.org</p> +<p style=" font-size:8pt;">Copyright (c) 2010 David Goncalves</p> +<p><a href="http://www.lestat.st/informatique/projets/nut-monitor-en">http://www.lestat.st</a></p> + + + Qt::RichText + + + Qt::AlignCenter + + + true + + + true + + + Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse|Qt::TextBrowserInteraction|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + aboutdialog1 + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + aboutdialog1 + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/scripts/python/app/ui/dialog1.ui b/scripts/python/app/ui/dialog1.ui new file mode 100644 index 0000000..6cf652b --- /dev/null +++ b/scripts/python/app/ui/dialog1.ui @@ -0,0 +1,89 @@ + + + dialog1 + + + + 0 + 0 + 297 + 133 + + + + Dialog + + + true + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Enter a name for this favorite<br><br><font color="#808080">You cannot re-use a name from another entry</font> + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + dialog1 + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + dialog1 + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/scripts/python/app/ui/dialog2.ui b/scripts/python/app/ui/dialog2.ui new file mode 100644 index 0000000..d31542e --- /dev/null +++ b/scripts/python/app/ui/dialog2.ui @@ -0,0 +1,98 @@ + + + dialog2 + + + + 0 + 0 + 229 + 116 + + + + Dialog + + + true + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Please select the favorite that you want to delete from list... + + + true + + + + + + + + None + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + dialog2 + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + dialog2 + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/scripts/python/app/gui-1.3.glade b/scripts/python/app/ui/gui-1.3.glade similarity index 99% rename from scripts/python/app/gui-1.3.glade rename to scripts/python/app/ui/gui-1.3.glade index ffe9b7d..eb42aba 100644 --- a/scripts/python/app/gui-1.3.glade +++ b/scripts/python/app/ui/gui-1.3.glade @@ -1021,7 +1021,7 @@ want to delete from list... center-always normal NUT-Monitor - 1.3 + 1.3.1 Copyright (c) 2010 David Goncalves GUI to manage devices connected a NUT server. @@ -1047,7 +1047,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. David Goncalves <david@lestat.st> - David Goncalves <david@lestat.st> - Français + David Goncalves <david@lestat.st> - Français +Daniele Pezzini <hyouko@gmail.com> - Italiano True diff --git a/scripts/python/app/ui/window1.ui b/scripts/python/app/ui/window1.ui new file mode 100644 index 0000000..1950464 --- /dev/null +++ b/scripts/python/app/ui/window1.ui @@ -0,0 +1,473 @@ + + + window1 + + + + 0 + 0 + 560 + 465 + + + + NUT Monitor + + + + ../../../../../.designer/backup../../../../../.designer/backup + + + + + + + NUT Server + + + Qt::AlignCenter + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + 65535 + + + 3493 + + + + + + + + + + Device : + + + + + + + + None + + + + + + + + Host / Port : + + + + + + + false + + + &Refresh + + + + + + + + + + + + Use authentication + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Login / Password : + + + + + + + + + + QLineEdit::Password + + + + + + + + + + QFrame::HLine + + + QFrame::Sunken + + + + + + + + + + + + false + + + C&onnect + + + + + + + + + + &Disconnect + + + + + + + + + + + + + + + 0 + + + + Device status + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + label + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + label + + + + + + + + + + + + + + Remaining time : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Battery charge : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 24 + + + + + + + Device commands : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + 24 + + + + + + + Current load : + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + N/A + + + Qt::AlignCenter + + + + + + + + + + + + + 0 + 0 + + + + QComboBox::AdjustToContents + + + + + + + &Execute + + + + + + + + + + + + + + + Device vars + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + &Refresh + + + + + + + + + + + + + + + + + + + 0 + 0 + 560 + 24 + + + + + &File + + + + + + + + F&avorites + + + + + + + + + + + + + + + &About + + + + + + + + &Quit + + + Ctrl+Q + + + + + false + + + + + + &Add + + + + + false + + + + + + &Delete + + + + + + diff --git a/scripts/python/module/PyNUT.py b/scripts/python/module/PyNUT.py deleted file mode 100644 index af09fac..0000000 --- a/scripts/python/module/PyNUT.py +++ /dev/null @@ -1,249 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# Copyright (C) 2008 David Goncalves -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . - -# 2008-01-14 David Goncalves -# PyNUT is an abstraction class to access NUT (Network UPS Tools) server. -# -# 2008-06-09 David Goncalves -# Added 'GetRWVars' and 'SetRWVar' commands. -# -# 2009-02-19 David Goncalves -# Changed class PyNUT to PyNUTClient -# -# 2010-07-23 David Goncalves - Version 1.2 -# Changed GetRWVars function that fails is the UPS is not -# providing such vars. - -import telnetlib - -class PyNUTClient : - """ Abstraction class to access NUT (Network UPS Tools) server """ - - __debug = None # Set class to debug mode (prints everything useful for debuging...) - __host = None - __port = None - __login = None - __password = None - __timeout = None - __srv_handler = None - - __version = "1.2" - __release = "2010-07-23" - - - def __init__( self, host="127.0.0.1", port=3493, login=None, password=None, debug=False, timeout=5 ) : - """ Class initialization method - -host : Host to connect (default to localhost) -port : Port where NUT listens for connections (default to 3493) -login : Login used to connect to NUT server (default to None for no authentication) -password : Password used when using authentication (default to None) -debug : Boolean, put class in debug mode (prints everything on console, default to False) -timeout : Timeout used to wait for network response - """ - self.__debug = debug - - if self.__debug : - print( "[DEBUG] Class initialization..." ) - print( "[DEBUG] -> Host = %s (port %s)" % ( host, port ) ) - print( "[DEBUG] -> Login = '%s' / '%s'" % ( login, password ) ) - - self.__host = host - self.__port = port - self.__login = login - self.__password = password - self.__timeout = 5 - - self.__connect() - - # Try to disconnect cleanly when class is deleted ;) - def __del__( self ) : - """ Class destructor method """ - try : - self.__srv_handler.write( "LOGOUT\n" ) - except : - pass - - def __connect( self ) : - """ Connects to the defined server - -If login/pass was specified, the class tries to authenticate. An error is raised -if something goes wrong. - """ - if self.__debug : - print( "[DEBUG] Connecting to host" ) - - self.__srv_handler = telnetlib.Telnet( self.__host, self.__port ) - - if self.__login != None : - self.__srv_handler.write( "USERNAME %s\n" % self.__login ) - result = self.__srv_handler.read_until( "\n", self.__timeout ) - if result[:2] != "OK" : - raise Exception, result.replace( "\n", "" ) - - if self.__password != None : - self.__srv_handler.write( "PASSWORD %s\n" % self.__password ) - result = self.__srv_handler.read_until( "\n", self.__timeout ) - if result[:2] != "OK" : - raise Exception, result.replace( "\n", "" ) - - def GetUPSList( self ) : - """ Returns the list of available UPS from the NUT server - -The result is a dictionary containing 'key->val' pairs of 'UPSName' and 'UPS Description' - """ - if self.__debug : - print( "[DEBUG] GetUPSList from server" ) - - self.__srv_handler.write( "LIST UPS\n" ) - result = self.__srv_handler.read_until( "\n" ) - if result != "BEGIN LIST UPS\n" : - raise Exception, result.replace( "\n", "" ) - - result = self.__srv_handler.read_until( "END LIST UPS\n" ) - ups_list = {} - - for line in result.split( "\n" ) : - if line[:3] == "UPS" : - ups, desc = line[4:-1].split( '"' ) - ups_list[ ups.replace( " ", "" ) ] = desc - - return( ups_list ) - - def GetUPSVars( self, ups="" ) : - """ Get all available vars from the specified UPS - -The result is a dictionary containing 'key->val' pairs of all -available vars. - """ - if self.__debug : - print( "[DEBUG] GetUPSVars called..." ) - - self.__srv_handler.write( "LIST VAR %s\n" % ups ) - result = self.__srv_handler.read_until( "\n" ) - if result != "BEGIN LIST VAR %s\n" % ups : - raise Exception, result.replace( "\n", "" ) - - ups_vars = {} - result = self.__srv_handler.read_until( "END LIST VAR %s\n" % ups ) - offset = len( "VAR %s " % ups ) - end_offset = 0 - ( len( "END LIST VAR %s\n" % ups ) + 1 ) - - for current in result[:end_offset].split( "\n" ) : - var = current[ offset: ].split( '"' )[0].replace( " ", "" ) - data = current[ offset: ].split( '"' )[1] - ups_vars[ var ] = data - - return( ups_vars ) - - def GetUPSCommands( self, ups="" ) : - """ Get all available commands for the specified UPS - -The result is a dict object with command name as key and a description -of the command as value - """ - if self.__debug : - print( "[DEBUG] GetUPSCommands called..." ) - - self.__srv_handler.write( "LIST CMD %s\n" % ups ) - result = self.__srv_handler.read_until( "\n" ) - if result != "BEGIN LIST CMD %s\n" % ups : - raise Exception, result.replace( "\n", "" ) - - ups_cmds = {} - result = self.__srv_handler.read_until( "END LIST CMD %s\n" % ups ) - offset = len( "CMD %s " % ups ) - end_offset = 0 - ( len( "END LIST CMD %s\n" % ups ) + 1 ) - - for current in result[:end_offset].split( "\n" ) : - var = current[ offset: ].split( '"' )[0].replace( " ", "" ) - - # For each var we try to get the available description - try : - self.__srv_handler.write( "GET CMDDESC %s %s\n" % ( ups, var ) ) - temp = self.__srv_handler.read_until( "\n" ) - if temp[:7] != "CMDDESC" : - raise - else : - off = len( "CMDDESC %s %s " % ( ups, var ) ) - desc = temp[off:-1].split('"')[1] - except : - desc = var - - ups_cmds[ var ] = desc - - return( ups_cmds ) - - def GetRWVars( self, ups="" ) : - """ Get a list of all writable vars from the selected UPS - -The result is presented as a dictionary containing 'key->val' pairs - """ - if self.__debug : - print( "[DEBUG] GetUPSVars from '%s'..." % ups ) - - self.__srv_handler.write( "LIST RW %s\n" % ups ) - result = self.__srv_handler.read_until( "\n" ) - if ( result != "BEGIN LIST RW %s\n" % ups ) : - raise Exception, result.replace( "\n", "" ) - - result = self.__srv_handler.read_until( "END LIST RW %s\n" % ups ) - offset = len( "VAR %s" % ups ) - end_offset = 0 - ( len( "END LIST RW %s\n" % ups ) + 1 ) - rw_vars = {} - - try : - for current in result[:end_offset].split( "\n" ) : - var = current[ offset: ].split( '"' )[0].replace( " ", "" ) - data = current[ offset: ].split( '"' )[1] - rw_vars[ var ] = data - - except : - pass - - return( rw_vars ) - - def SetRWVar( self, ups="", var="", value="" ): - """ Set a variable to the specified value on selected UPS - -The variable must be a writable value (cf GetRWVars) and you must have the proper -rights to set it (maybe login/password). - """ - - self.__srv_handler.write( "SET VAR %s %s %s\n" % ( ups, var, value ) ) - result = self.__srv_handler.read_until( "\n" ) - if ( result == "OK\n" ) : - return( "OK" ) - else : - raise Exception, result - - def RunUPSCommand( self, ups="", command="" ) : - """ Send a command to the specified UPS - -Returns OK on success or raises an error - """ - - if self.__debug : - print( "[DEBUG] RunUPSCommand called..." ) - - self.__srv_handler.write( "INSTCMD %s %s\n" % ( ups, command ) ) - result = self.__srv_handler.read_until( "\n" ) - if ( result == "OK\n" ) : - return( "OK" ) - else : - raise Exception, result.replace( "\n", "" ) diff --git a/scripts/python/module/PyNUT.py.in b/scripts/python/module/PyNUT.py.in new file mode 100644 index 0000000..226c0ae --- /dev/null +++ b/scripts/python/module/PyNUT.py.in @@ -0,0 +1,351 @@ +#!@PYTHON@ +# -*- coding: utf-8 -*- + +# Copyright (C) 2008 David Goncalves +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# 2008-01-14 David Goncalves +# PyNUT is an abstraction class to access NUT (Network UPS Tools) server. +# +# 2008-06-09 David Goncalves +# Added 'GetRWVars' and 'SetRWVar' commands. +# +# 2009-02-19 David Goncalves +# Changed class PyNUT to PyNUTClient +# +# 2010-07-23 David Goncalves - Version 1.2 +# Changed GetRWVars function that fails is the UPS is not +# providing such vars. +# +# 2011-07-05 René Martín Rodríguez - Version 1.2.1 +# Added support for FSD, HELP and VER commands +# +# 2012-02-07 René Martín Rodríguez - Version 1.2.2 +# Added support for LIST CLIENTS command +# +# 2014-06-03 george2 - Version 1.3.0 +# Added custom exception class, fixed minor bug, added Python 3 support. +# +# 2021-09-27 Jim Klimov - Version 1.4.0 +# Revise strings used to be byte sequences as required by telnetlib +# in Python 3.9, by spelling out b"STR" or str.encode('ascii'); +# the change was also tested to work with Python 2.7, 3.4, 3.5 and +# 3.7 (to the extent of accompanying test_nutclient.py at least). + +import telnetlib + +class PyNUTError( Exception ) : + """ Base class for custom exceptions """ + + +class PyNUTClient : + """ Abstraction class to access NUT (Network UPS Tools) server """ + + __debug = None # Set class to debug mode (prints everything useful for debuging...) + __host = None + __port = None + __login = None + __password = None + __timeout = None + __srv_handler = None + + __version = "1.4.0" + __release = "2021-09-27" + + + def __init__( self, host="127.0.0.1", port=3493, login=None, password=None, debug=False, timeout=5 ) : + """ Class initialization method + +host : Host to connect (default to localhost) +port : Port where NUT listens for connections (default to 3493) +login : Login used to connect to NUT server (default to None for no authentication) +password : Password used when using authentication (default to None) +debug : Boolean, put class in debug mode (prints everything on console, default to False) +timeout : Timeout used to wait for network response + """ + self.__debug = debug + + if self.__debug : + print( "[DEBUG] Class initialization..." ) + print( "[DEBUG] -> Host = %s (port %s)" % ( host, port ) ) + print( "[DEBUG] -> Login = '%s' / '%s'" % ( login, password ) ) + + self.__host = host + self.__port = port + self.__login = login + self.__password = password + self.__timeout = 5 + + self.__connect() + + # Try to disconnect cleanly when class is deleted ;) + def __del__( self ) : + """ Class destructor method """ + try : + self.__srv_handler.write( b"LOGOUT\n" ) + except : + pass + + def __connect( self ) : + """ Connects to the defined server + +If login/pass was specified, the class tries to authenticate. An error is raised +if something goes wrong. + """ + if self.__debug : + print( "[DEBUG] Connecting to host" ) + + self.__srv_handler = telnetlib.Telnet( self.__host, self.__port ) + + if self.__login != None : + self.__srv_handler.write( ("USERNAME %s\n" % self.__login).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n", self.__timeout ) + if result[:2] != b"OK" : + raise PyNUTError( result.replace( b"\n", b"" ) ) + + if self.__password != None : + self.__srv_handler.write( ("PASSWORD %s\n" % self.__password).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n", self.__timeout ) + if result[:2] != b"OK" : + raise PyNUTError( result.replace( b"\n", b"" ) ) + + def GetUPSList( self ) : + """ Returns the list of available UPS from the NUT server + +The result is a dictionary containing 'key->val' pairs of 'UPSName' and 'UPS Description' + """ + if self.__debug : + print( "[DEBUG] GetUPSList from server" ) + + self.__srv_handler.write( b"LIST UPS\n" ) + result = self.__srv_handler.read_until( b"\n" ) + if result != b"BEGIN LIST UPS\n" : + raise PyNUTError( result.replace( b"\n", b"" ) ) + + result = self.__srv_handler.read_until( b"END LIST UPS\n" ) + ups_list = {} + + for line in result.split( b"\n" ) : + if line[:3] == b"UPS" : + ups, desc = line[4:-1].split( b'"' ) + ups_list[ ups.replace( b" ", b"" ) ] = desc + + return( ups_list ) + + def GetUPSVars( self, ups="" ) : + """ Get all available vars from the specified UPS + +The result is a dictionary containing 'key->val' pairs of all +available vars. + """ + if self.__debug : + print( "[DEBUG] GetUPSVars called..." ) + + self.__srv_handler.write( ("LIST VAR %s\n" % ups).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n" ) + if result != ("BEGIN LIST VAR %s\n" % ups).encode('ascii') : + raise PyNUTError( result.replace( b"\n", b"" ) ) + + ups_vars = {} + result = self.__srv_handler.read_until( ("END LIST VAR %s\n" % ups).encode('ascii') ) + offset = len( ("VAR %s " % ups ).encode('ascii') ) + end_offset = 0 - ( len( ("END LIST VAR %s\n" % ups).encode('ascii') ) + 1 ) + + for current in result[:end_offset].split( b"\n" ) : + var = current[ offset: ].split( b'"' )[0].replace( b" ", b"" ) + data = current[ offset: ].split( b'"' )[1] + ups_vars[ var ] = data + + return( ups_vars ) + + def GetUPSCommands( self, ups="" ) : + """ Get all available commands for the specified UPS + +The result is a dict object with command name as key and a description +of the command as value + """ + if self.__debug : + print( "[DEBUG] GetUPSCommands called..." ) + + self.__srv_handler.write( ("LIST CMD %s\n" % ups).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n" ) + if result != ("BEGIN LIST CMD %s\n" % ups).encode('ascii') : + raise PyNUTError( result.replace( b"\n", b"" ) ) + + ups_cmds = {} + result = self.__srv_handler.read_until( ("END LIST CMD %s\n" % ups).encode('ascii') ) + offset = len( ("CMD %s " % ups).encode('ascii') ) + end_offset = 0 - ( len( ("END LIST CMD %s\n" % ups).encode('ascii') ) + 1 ) + + for current in result[:end_offset].split( b"\n" ) : + var = current[ offset: ].split( b'"' )[0].replace( b" ", b"" ) + + # For each var we try to get the available description + try : + self.__srv_handler.write( ("GET CMDDESC %s %s\n" % ( ups, var )).encode('ascii') ) + temp = self.__srv_handler.read_until( b"\n" ) + if temp[:7] != b"CMDDESC" : + raise PyNUTError + else : + off = len( ("CMDDESC %s %s " % ( ups, var )).encode('ascii') ) + desc = temp[off:-1].split(b'"')[1] + except : + desc = var + + ups_cmds[ var ] = desc + + return( ups_cmds ) + + def GetRWVars( self, ups="" ) : + """ Get a list of all writable vars from the selected UPS + +The result is presented as a dictionary containing 'key->val' pairs + """ + if self.__debug : + print( "[DEBUG] GetUPSVars from '%s'..." % ups ) + + self.__srv_handler.write( ("LIST RW %s\n" % ups).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n" ) + if ( result != ("BEGIN LIST RW %s\n" % ups).encode('ascii') ) : + raise PyNUTError( result.replace( b"\n", b"" ) ) + + result = self.__srv_handler.read_until( ("END LIST RW %s\n" % ups).encode('ascii') ) + offset = len( ("VAR %s" % ups).encode('ascii') ) + end_offset = 0 - ( len( ("END LIST RW %s\n" % ups).encode('ascii') ) + 1 ) + rw_vars = {} + + try : + for current in result[:end_offset].split( b"\n" ) : + var = current[ offset: ].split( b'"' )[0].replace( b" ", b"" ) + data = current[ offset: ].split( b'"' )[1] + rw_vars[ var ] = data + + except : + pass + + return( rw_vars ) + + def SetRWVar( self, ups="", var="", value="" ): + """ Set a variable to the specified value on selected UPS + +The variable must be a writable value (cf GetRWVars) and you must have the proper +rights to set it (maybe login/password). + """ + + self.__srv_handler.write( ("SET VAR %s %s %s\n" % ( ups, var, value )).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n" ) + if ( result == b"OK\n" ) : + return( "OK" ) + else : + raise PyNUTError( result ) + + def RunUPSCommand( self, ups="", command="" ) : + """ Send a command to the specified UPS + +Returns OK on success or raises an error + """ + + if self.__debug : + print( "[DEBUG] RunUPSCommand called..." ) + + self.__srv_handler.write( ("INSTCMD %s %s\n" % ( ups, command )).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n" ) + if ( result == b"OK\n" ) : + return( "OK" ) + else : + raise PyNUTError( result.replace( b"\n", b"" ) ) + + def FSD( self, ups="") : + """ Send FSD command + +Returns OK on success or raises an error + +NOTE: API changed since NUT 2.8.0 to replace MASTER with PRIMARY +(and backwards-compatible alias handling) + """ + + if self.__debug : + print( "[DEBUG] PRIMARY called..." ) + + self.__srv_handler.write( ("PRIMARY %s\n" % ups).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n" ) + if ( result != b"OK PRIMARY-GRANTED\n" ) : + if self.__debug : + print( "[DEBUG] Retrying: MASTER called..." ) + self.__srv_handler.write( ("MASTER %s\n" % ups).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n" ) + if ( result != b"OK MASTER-GRANTED\n" ) : + raise PyNUTError( ( "Primary level functions are not available", "" ) ) + + if self.__debug : + print( "[DEBUG] FSD called..." ) + self.__srv_handler.write( ("FSD %s\n" % ups).encode('ascii') ) + result = self.__srv_handler.read_until( b"\n" ) + if ( result == b"OK FSD-SET\n" ) : + return( "OK" ) + else : + raise PyNUTError( result.replace( b"\n", b"" ) ) + + def help(self) : + """ Send HELP command + """ + + if self.__debug : + print( "[DEBUG] HELP called..." ) + + self.__srv_handler.write( b"HELP\n" ) + return self.__srv_handler.read_until( b"\n" ) + + def ver(self) : + """ Send VER command + """ + + if self.__debug : + print( "[DEBUG] VER called..." ) + + self.__srv_handler.write( b"VER\n" ) + return self.__srv_handler.read_until( b"\n" ) + + def ListClients( self, ups = None ) : + """ Returns the list of connected clients from the NUT server + +The result is a dictionary containing 'key->val' pairs of 'UPSName' and a list of clients + """ + if self.__debug : + print( "[DEBUG] ListClients from server" ) + + if ups and (ups not in self.GetUPSList()): + raise PyNUTError( "%s is not a valid UPS" % ups ) + + if ups: + self.__srv_handler.write( ("LIST CLIENTS %s\n" % ups).encode('ascii') ) + else: + self.__srv_handler.write( b"LIST CLIENTS\n" ) + result = self.__srv_handler.read_until( b"\n" ) + if result != b"BEGIN LIST CLIENTS\n" : + raise PyNUTError( result.replace( b"\n", b"" ) ) + + result = self.__srv_handler.read_until( b"END LIST CLIENTS\n" ) + ups_list = {} + + for line in result.split( b"\n" ): + if line[:6] == b"CLIENT" : + host, ups = line[7:].split(b' ') + ups.replace(b' ', b'') + if not ups in ups_list: + ups_list[ups] = [] + ups_list[ups].append(host) + + return( ups_list ) diff --git a/scripts/python/module/test_nutclient.py b/scripts/python/module/test_nutclient.py.in similarity index 68% rename from scripts/python/module/test_nutclient.py rename to scripts/python/module/test_nutclient.py.in index 39993e0..22e6adb 100755 --- a/scripts/python/module/test_nutclient.py +++ b/scripts/python/module/test_nutclient.py.in @@ -1,22 +1,31 @@ -#!/usr/bin/env python +#!@PYTHON@ # -*- coding: utf-8 -*- # This source code is provided for testing/debuging purpose ;) import PyNUT import sys +import os if __name__ == "__main__" : + NUT_PORT = int(os.getenv('NUT_PORT', '3493')) + NUT_USER = os.getenv('NUT_USER', None) + NUT_PASS = os.getenv('NUT_PASS', None) print( "PyNUTClient test..." ) - nut = PyNUT.PyNUTClient( debug=True ) - #nut = PyNUT.PyNUTClient( login="upsadmin", password="upsadmin", debug=True ) + #nut = PyNUT.PyNUTClient( debug=True, port=NUT_PORT ) + nut = PyNUT.PyNUTClient( login=NUT_USER, password=NUT_PASS, debug=True, port=NUT_PORT ) + #nut = PyNUT.PyNUTClient( login="upsadmin", password="upsadmin", debug=True, port=NUT_PORT ) print( 80*"-" + "\nTesting 'GetUPSList' :") result = nut.GetUPSList( ) print( "\033[01;33m%s\033[0m\n" % result ) - print( 80*"-" + "\nTesting 'GetUPSVars' :") + # [dummy] + # driver = dummy-ups + # desc = "Test device" + # port = /src/nut/data/evolution500.seq + print( 80*"-" + "\nTesting 'GetUPSVars' for 'dummy' (should be registered in upsd.conf) :") result = nut.GetUPSVars( "dummy" ) print( "\033[01;33m%s\033[0m\n" % result ) diff --git a/scripts/subdriver/gen-snmp-subdriver.sh b/scripts/subdriver/gen-snmp-subdriver.sh new file mode 100755 index 0000000..f7ccf44 --- /dev/null +++ b/scripts/subdriver/gen-snmp-subdriver.sh @@ -0,0 +1,474 @@ +#!/bin/bash +# +# an auxiliary script to produce a "stub" snmp-ups subdriver from +# SNMP data from a real agent or from dump files +# +# Version: 0.13 +# +# See also: docs/snmp-subdrivers.txt +# +# Copyright (C) +# 2011 - 2012 Arnaud Quette +# 2015 - 2022 Eaton (author: Arnaud Quette ) +# 2011 Jim Klimov +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# +# TODO: +# - Prepend sysDescription (.1.3.6.1.2.1.1.1.0) to have some more visibility +# - extend to SNMP v3 (auth.) + +usage() { + echo "Usage: $0 [options] [file]" + echo "Options:" + echo " -h, --help -- show this message and quit" + echo " -n name -- subdriver name (use natural capitalization)" + echo " -M DIRLIST -- colon separated list of directories to also search for MIBs" + echo " -k -- keep temporary files (for debugging)" + echo "" + echo "mode 1: get SNMP data from a real agent" + echo " -H host_address -- SNMP host IP address or name" + echo " -c community -- SNMP v1 community name (default: public)" + echo " -s XXXX -- override SNMP OID entry point (sysOID). Ex: '.1.3.6.1.4.1.534.10'" + echo "" + echo "mode 2: get data from files (snmpwalk dumps of 'sysOID' subtree)" + echo " -s XXXX -- SNMP OID entry point (sysOID). Ex: '.1.3.6.1.4.1.534.6.6.7'" + echo " file1 file2 -- read from files instead of an host (using Net SNMP)" + echo " file1: numeric SNMP walk (snmpwalk -On ... )" + echo " file2: string SNMP walk (snmpwalk -Os ... )" + echo "" + echo "mode 3: get data from 1 file (numeric snmpwalk dump of the whole SNMP tree)" + echo " The sysOID is extracted from the dump, and only the pointed subtree is used" + echo " A MIB file MUST be provided, and is used to produce the string SNMP walk" + echo " file1 -- read from file instead of an host (using Net SNMP)" + echo " file1: numeric SNMP walk (snmpwalk -On ... )" + echo "" + + echo "Notes:" + echo " For both modes, prefer to copy the specific MIB file(s) for your device in the $0 script directory" + echo " So that it is automatically taken into account for the string name resolution of OIDs" + echo " Otherwise, use \"-M.\" option" + echo "" + echo "Example:" + echo "mode 1: $0 -H 192.168.0.1 -n mibname -c mycommunity" + echo "mode 2: (using sysOID .1.3.6.1.4.1.534.6.6.7)" + echo " snmpwalk -On -v1 -c mycommunity 192.168.0.1 .1.3.6.1.4.1.534.6.6.7 2>/dev/null 1> numeric-walk-file" + echo " snmpwalk -Os -v1 -m ALL -M+. -c mycommunity 192.168.0.1 .1.3.6.1.4.1.534.6.6.7 2>/dev/null 1> string-walk-file" + echo " $0 -s .1.3.6.1.4.1.534.6.6.7 numeric-walk-file string-walk-file" + echo "mode 3:" + echo " snmpwalk -On -v1 -c mycommunity 192.168.0.1 .1 2>/dev/null 1> numeric-walk-file" + echo " $0 numeric-walk-file" + echo "" + echo " You may alos need to install additional packages:" + echo " - 'snmp' package (on Debian) for the base commands (snmpget, snmpwalk, snmptranslate)" + echo " - 'snmp-mibs-downloader' package (on Debian) to get all standard MIBs" +} + +# variables +DRIVER="" +KEEP="" +HOSTNAME="" +MIBS_DIRLIST="+." +COMMUNITY="public" +DEVICE_SYSOID="" +SYSOID="" +MODE=0 + +# constants +NAME=gen-snmp-subdriver +TMPDIR="${TEMPDIR:-/tmp}" +SYSOID_NUMBER=".1.3.6.1.2.1.1.2.0" +DEBUG="`mktemp "$TMPDIR/$NAME-DEBUG.XXXXXX"`" +DFL_NUMWALKFILE="`mktemp "$TMPDIR/$NAME-NUMWALK.XXXXXX"`" +DFL_STRWALKFILE="`mktemp "$TMPDIR/$NAME-STRWALK.XXXXXX"`" +TMP_NUMWALKFILE="`mktemp "$TMPDIR/$NAME-TMP-NUMWALK.XXXXXX"`" +TMP_STRWALKFILE="`mktemp "$TMPDIR/$NAME-TMP-STRWALK.XXXXXX"`" + +get_snmp_data() { + # 1) get the sysOID (points the mfr specif MIB), apart if there's an override + if [ -z "$SYSOID" ] + then + SYSOID="`snmpget -On -v1 -c "$COMMUNITY" -Ov "$HOSTNAME" "$SYSOID_NUMBER" | cut -d' ' -f2`" + echo "sysOID retrieved: ${SYSOID}" + else + echo "Using the provided sysOID override ($SYSOID)" + fi + DEVICE_SYSOID="$SYSOID" + + OID_COUNT=0 + while (test "$OID_COUNT" -eq 0) + do + # 2) get the content of the mfr specif MIB + echo "Retrieving SNMP information. This may take some time" + snmpwalk -On -v1 -c "$COMMUNITY" "$HOSTNAME" "$SYSOID" 2>/dev/null 1> "$DFL_NUMWALKFILE" + snmpwalk -Os -v1 -m ALL -M"$MIBS_DIRLIST" -c "$COMMUNITY" "$HOSTNAME" "$SYSOID" 2>/dev/null 1> "$DFL_STRWALKFILE" + + # 3) test return value of the walk, and possibly ramp-up the path to get something. + # The sysOID mechanism only works if we're pointed somehow in the right direction + # i.e. doesn't work if sysOID is .1.3.6.1.4.1.705.1 and data is at .1.3.6.1.4.1.534... + # Ex: sysOID = ".1.X.Y.Z" + # try with ".1.X.Y.Z", if fails try with .1.X.Y", if fails try with .1.X"... + OID_COUNT="`cat $NUMWALKFILE | wc -l`" + if [ $OID_COUNT -eq 0 ]; then + # ramp-up the provided sysOID by removing the last .x part + SYSOID=${SYSOID%.*} + echo "Warning: sysOID provided no data! Trying with a level up using $SYSOID" + fi + done + return $OID_COUNT +} + +generate_C() { + # create file names, lowercase + LDRIVER="`echo "$DRIVER" | tr A-Z a-z`" + UDRIVER="`echo "$DRIVER" | tr a-z A-Z`" + # keep dashes in name for files + CFILE="$LDRIVER-mib.c" + HFILE="$LDRIVER-mib.h" + # but replace with underscores for the structures and defines + LDRIVER="`echo "$LDRIVER" | tr - _`" + UDRIVER="`echo "$UDRIVER" | tr - _`" + + # generate header file + # NOTE: with <<-EOF leading TABs are all stripped + echo "Creating $HFILE" + cat > "$HFILE" <<-EOF + /* ${HFILE} - subdriver to monitor ${DRIVER} SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2016 Arnaud Quette + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + #ifndef ${UDRIVER}_MIB_H + #define ${UDRIVER}_MIB_H + + #include "main.h" + #include "snmp-ups.h" + + extern mib2nut_info_t ${LDRIVER}; + + #endif /* ${UDRIVER}_MIB_H */ + EOF + + # generate source file + # create heading boilerblate + # NOTE: with <<-EOF leading TABs are all stripped + echo "Creating $CFILE" + cat > "$CFILE" <<-EOF + /* ${CFILE} - subdriver to monitor ${DRIVER} SNMP devices with NUT + * + * Copyright (C) + * 2011 - 2016 Arnaud Quette + * + * Note: this subdriver was initially generated as a "stub" by the + * gen-snmp-subdriver script. It must be customized! + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + #include "${HFILE}" + + #define ${UDRIVER}_MIB_VERSION "0.1" + + #define ${UDRIVER}_SYSOID "${DEVICE_SYSOID}" + + /* To create a value lookup structure (as needed on the 2nd line of the example + * below), use the following kind of declaration, outside of the present snmp_info_t[]: + * static info_lkp_t onbatt_info[] = { + * { 1, "OB" }, + * { 2, "OL" }, + * { 0, NULL } + * }; + */ + + /* ${UDRIVER} Snmp2NUT lookup table */ + static snmp_info_t ${LDRIVER}_mib[] = { + + /* Data format: + * { info_type, info_flags, info_len, OID, dfl, flags, oid2info }, + * + * info_type: NUT INFO_ or CMD_ element name + * info_flags: flags to set in addinfo + * info_len: length of strings if ST_FLAG_STRING, multiplier otherwise + * OID: SNMP OID or NULL + * dfl: default value + * flags: snmp-ups internal flags (FIXME: ...) + * oid2info: lookup table between OID and NUT values + * + * Example: + * { "input.voltage", 0, 0.1, ".1.3.6.1.4.1.705.1.6.2.1.2.1", "", SU_INPUT_1, NULL }, + * { "ups.status", ST_FLAG_STRING, SU_INFOSIZE, ".1.3.6.1.4.1.705.1.7.3.0", "", SU_FLAG_OK | SU_STATUS_BATT, onbatt_info }, + * + * To create a value lookup structure (as needed on the 2nd line), use the + * following kind of declaration, outside of the present snmp_info_t[]: + * static info_lkp_t onbatt_info[] = { + * { 1, "OB" }, + * { 2, "OL" }, + * { 0, NULL } + * }; + */ + + /* standard MIB items; if the vendor MIB contains better OIDs for + * this (e.g. with daisy-chain support), consider adding those here + */ + EOF + + # Same file, indented text (TABs not stripped): + cat >> "$CFILE" < /dev/null + if [ $? -eq 0 ]; then + ST_FLAG_TYPE="ST_FLAG_STRING" + SU_INFOSIZE="SU_INFOSIZE" + else + ST_FLAG_TYPE="0" + SU_INFOSIZE="1" + fi + # get the matching numeric OID + NUM_OID="`sed -n "${LINENB}p" "${NUMWALKFILE}" | cut -d' ' -f1`" + printf "\t/* ${FULL_STR_OID} */\n\t{ \"unmapped.${STR_OID}\", ${ST_FLAG_TYPE}, ${SU_INFOSIZE}, \"${NUM_OID}\", NULL, SU_FLAG_OK, NULL },\n" + done < "${STRWALKFILE}" >> "${CFILE}" + + # append footer (TABs not stripped): + cat >> "$CFILE" <&2 + exit 1 + fi +done + +# check that the needed parameters are provided, depending on the mode +if [ -z "$NUMWALKFILE" ]; then + # mode 1: directly get SNMP data from a real agent + echo "Mode 1 selected" + MODE=1 + NUMWALKFILE="$DFL_NUMWALKFILE" + STRWALKFILE="$DFL_STRWALKFILE" + + # check if Net SNMP is available + if [ -z "`command -v snmpget`" -o -z "`command -v snmpwalk`" ] && \ + [ -z "`which snmpget`" -o -z "`which snmpwalk`" ]; then + echo "Net SNMP not found! snmpget and snmpwalk commands are required." >&2 + exit 1 + fi + # hostname is also mandatory + while [ -z "$HOSTNAME" ]; do + printf "\n\tPlease enter the SNMP host IP address or name.\n" + read -p "SNMP host IP name or address: " HOSTNAME < /dev/tty + if echo "$HOSTNAME" | egrep -q '[^a-zA-Z0-9.-]'; then + echo "Please use only letters, digits, dash and period character" + HOSTNAME="" + fi + done + # get data from the agent + get_snmp_data +else + # no string walk provided, so mode 3 + if [ -z "$STRWALKFILE" ]; then + # mode 3: get data from 1 file, + # Filter according to sysOID on the specific subtree + # Generate the numeric SNMP walk using this output + # then use snmptranslate to get the string OIDs and generated the string SNMP walk + echo "Mode 3 selected" + MODE=3 + RAWWALKFILE="$NUMWALKFILE" + NUMWALKFILE="$DFL_NUMWALKFILE" + STRWALKFILE="$DFL_STRWALKFILE" + + # check for actual file existence + if [ ! -f "$RAWWALKFILE" ]; then + echo "SNMP walk dump file is missing on disk. Try --help for more info." >&2 + exit 1 + fi + # Extract the sysOID + # Format is "1.3.6.1.2.1.1.2.0 = OID: 1.3.6.1.4.1.4555.1.1.1" + DEVICE_SYSOID="`grep 1.3.6.1.2.1.1.2.0 "$RAWWALKFILE" | cut -d' ' -f4`" + if [ -n "$DEVICE_SYSOID" ]; then + echo "Found sysOID $DEVICE_SYSOID" + else + echo "SNMP sysOID is missing in file. Try --help for more info." >&2 + exit 1 + fi + + # Switch to the entry point, and extract the subtree + # Extract the numeric walk + echo -n "Extracting numeric SNMP walk..." + grep "$DEVICE_SYSOID" "$RAWWALKFILE" | egrep -v "1.3.6.1.2.1.1.2.0" 2>/dev/null 1> "$NUMWALKFILE" + echo " done" + + # Create the string walk from a translation of the numeric one + echo -n "Converting string SNMP walk..." + while IFS=' = ' read NUM_OID OID_VALUE + do + STR_OID="`snmptranslate -Os -m ALL -M+. "$NUM_OID" 2>/dev/null`" + # Uncomment the below line to get debug logs + #echo "Got: $STR_OID = $OID_VALUE" + printf "." + echo "$STR_OID = $OID_VALUE" >> "$STRWALKFILE" + done < "$NUMWALKFILE" + echo " done" + else + # mode 2: get data from files + echo "Mode 2 selected" + MODE=2 + + # get sysOID value from command line, if needed + while [ -z "$SYSOID" ]; do + echo " +Please enter the value of sysOID, as displayed by snmp-ups. For example '.1.3.6.1.4.1.2254.2.4'. +You can get it using: snmpget -v1 -c XXX $SYSOID_NUMBER" + read -p "Value of sysOID: " SYSOID < /dev/tty + if echo "$SYSOID" | egrep -q '[^0-9.]'; then + echo "Please use only the numeric form, with dots and digits" + SYSOID="" + fi + done + # check for actual files existence + if [ ! -f "$NUMWALKFILE" -o ! -f "$STRWALKFILE" ]; then + echo "SNMP walk dump files are missing on disk. Try --help for more info." >&2 + exit 1 + fi + fi +fi + +# delete temporary files: this is called just before exiting. +cleanup () { + rm -f "$DEBUG $DFL_NUMWALKFILE $TMP_NUMWALKFILE $DFL_STRWALKFILE $TMP_STRWALKFILE" +} +if [ -n "$KEEP" ]; then + trap cleanup EXIT +fi + +# prompt use for name of driver +while [ -z "$DRIVER" ]; do + echo " +Please enter a name for this driver. Use only letters and numbers. Use +natural (upper- and lowercase) capitalization, e.g., 'Belkin', 'APC'." + read -p "Name of subdriver: " DRIVER < /dev/tty + if echo "$DRIVER" | egrep -q '[^a-zA-Z0-9]'; then + echo "Please use only letters and digits" + DRIVER="" + fi +done + +# remove blank and "End of MIB" lines +egrep -e "^[[:space:]]?$" -e "End of MIB" -v "${NUMWALKFILE}" > "${TMP_NUMWALKFILE}" +egrep -e "^[[:space:]]?$" -e "End of MIB" -v "${STRWALKFILE}" > "${TMP_STRWALKFILE}" +NUMWALKFILE="${TMP_NUMWALKFILE}" +STRWALKFILE="${TMP_STRWALKFILE}" + +# FIXME: sanity checks (! -z contents -a same `wc -l`) +NUM_OID_COUNT="`cat "$NUMWALKFILE" | wc -l`" +STR_OID_COUNT="`cat "$STRWALKFILE" | wc -l`" + +echo "SNMP OIDs extracted = $NUM_OID_COUNT / $NUM_OID_COUNT" + +generate_C + +# Display the remaining tasks +cat < ]*- VendorID: \([0-9a-fA-F]*\).*/\1/p' | tail -1` -PRODUCTID=`cat "$FILE" | sed -n 's/[> ]*- ProductID: \([0-9a-fA-F]*\).*/\1/p' | tail -1` +VENDORID=`cat "$FILE" | sed -n 's/.*- VendorID: \([0-9a-fA-F]*\).*/\1/p' | tail -1` +PRODUCTID=`cat "$FILE" | sed -n 's/.*- ProductID: \([0-9a-fA-F]*\).*/\1/p' | tail -1` # prompt for productid, vendorid if necessary if [ -z "$VENDORID" ]; then @@ -103,7 +103,7 @@ CFILE="$LDRIVER-hid.c" HFILE="$LDRIVER-hid.h" # extract Usage Table -cat "$FILE" | sed -n 's/[> ]*Path: \([^,][^,]*\), Type:.*/\1/p' > "$UTABLE" +cat "$FILE" | sed -n 's/.*Path: \([^,][^,]*\), Type:.*/\1/p' > "$UTABLE" # extract Usage codes cat "$UTABLE" | tr '.' $'\n' | sort -u > "$USAGES" @@ -163,12 +163,16 @@ cat > "$CFILE" < + * 2003 - 2012 Arnaud Quette * 2005 - 2006 Peter Selinger * 2008 - 2009 Arjen de Korte + * 2013 Charles Lepple + * + * TODO: Add year and name for new subdriver author (contributor) + * Mention in docs/acknowledgements.txt if this is a vendor contribution * * Note: this subdriver was initially generated as a "stub" by the - * path-to-subdriver script. It must be customized. + * gen-usbhid-subdriver script. It must be customized. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -202,7 +206,7 @@ static usb_device_id_t ${LDRIVER}_usb_device_table[] = { { USB_DEVICE(${UDRIVER}_VENDORID, 0x${PRODUCTID}), NULL }, /* Terminating entry */ - { -1, -1, NULL } + { 0, 0, NULL } }; @@ -217,7 +221,7 @@ EOF cat "$SUBST" | sed 's/\(.*\) \(.*\)/\t{ "\2",\t0x\1 },/' >> "$CFILE" cat >> "$CFILE" <> "$CFILE" <> "$CFILE" <VendorID, hd->ProductID); + int status = is_usb_device_supported(${LDRIVER}_usb_device_table, hd); switch (status) { @@ -292,6 +296,7 @@ subdriver_t ${LDRIVER}_subdriver = { ${LDRIVER}_format_model, ${LDRIVER}_format_mfr, ${LDRIVER}_format_serial, + fix_report_desc, /* may optionally be customized, see cps-hid.c for example */ }; EOF @@ -299,8 +304,8 @@ cat < $@ - -nutshutdown: nutshutdown.in - sed -e 's,@sbindir\@,$(sbindir),g' $< > $@ systemdsystemunit_DATA = \ - nut-driver.service \ + nut-driver-enumerator.service \ + nut-driver-enumerator.path \ + nut-driver@.service \ nut-monitor.service \ - nut-server.service + nut-server.service \ + nut-driver.target \ + nut.target -systemdsystemshutdown_SCRIPTS = nutshutdown +systemdtmpfiles_DATA = \ + nut-common.tmpfiles + +EXTRA_DIST += nut-driver.target nut.target + +systemdshutdown_SCRIPTS = nutshutdown + +libexec_SCRIPTS = ../upsdrvsvcctl/nut-driver-enumerator.sh + +sbin_SCRIPTS = ../upsdrvsvcctl/upsdrvsvcctl else -EXTRA_DIST += nut-driver.service.in nut-monitor.service.in \ - nut-server.service.in nutshutdown.in +EXTRA_DIST += \ + nut-driver@.service.in nut-monitor.service.in \ + nut-server.service.in nutshutdown.in nut-driver.target nut.target \ + nut-driver-enumerator.path.in nut-driver-enumerator.service.in + +# NOTE: Do not EXTRA_DIST nut-common.tmpfiles.in - it is generated per build endif +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +# Generated by autogen.sh and needed to run the configure script: +MAINTAINERCLEANFILES += nut-common.tmpfiles.in diff --git a/scripts/systemd/Makefile.in b/scripts/systemd/Makefile.in index 28dc04a..2039aa4 100644 --- a/scripts/systemd/Makefile.in +++ b/scripts/systemd/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -17,6 +16,61 @@ VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -36,39 +90,56 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ -@HAVE_SYSTEMD_FALSE@am__append_1 = nut-driver.service.in nut-monitor.service.in \ -@HAVE_SYSTEMD_FALSE@ nut-server.service.in nutshutdown.in +@HAVE_SYSTEMD_TRUE@am__append_1 = nut-driver.target nut.target +@HAVE_SYSTEMD_FALSE@am__append_2 = \ +@HAVE_SYSTEMD_FALSE@ nut-driver@.service.in nut-monitor.service.in \ +@HAVE_SYSTEMD_FALSE@ nut-server.service.in nutshutdown.in nut-driver.target nut.target \ +@HAVE_SYSTEMD_FALSE@ nut-driver-enumerator.path.in nut-driver-enumerator.service.in subdir = scripts/systemd -DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h -CONFIG_CLEAN_FILES = +CONFIG_CLEAN_FILES = nut-common.tmpfiles nut-driver@.service \ + nut-monitor.service nut-server.service \ + nut-driver-enumerator.service nut-driver-enumerator.path \ + nutshutdown CONFIG_CLEAN_VPATH_FILES = am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ @@ -91,18 +162,56 @@ am__nobase_list = $(am__nobase_strip_setup); \ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__installdirs = "$(DESTDIR)$(systemdsystemshutdowndir)" \ - "$(DESTDIR)$(systemdsystemunitdir)" -SCRIPTS = $(systemdsystemshutdown_SCRIPTS) +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__installdirs = "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" \ + "$(DESTDIR)$(systemdshutdowndir)" \ + "$(DESTDIR)$(systemdsystemunitdir)" \ + "$(DESTDIR)$(systemdtmpfilesdir)" +SCRIPTS = $(libexec_SCRIPTS) $(sbin_SCRIPTS) \ + $(systemdshutdown_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = SOURCES = DIST_SOURCES = -DATA = $(systemdsystemunit_DATA) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(systemdsystemunit_DATA) $(systemdtmpfiles_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(srcdir)/nut-common.tmpfiles.in \ + $(srcdir)/nut-driver-enumerator.path.in \ + $(srcdir)/nut-driver-enumerator.service.in \ + $(srcdir)/nut-driver@.service.in \ + $(srcdir)/nut-monitor.service.in \ + $(srcdir)/nut-server.service.in $(srcdir)/nutshutdown.in \ + README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -113,16 +222,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -131,11 +249,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -145,14 +260,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -163,21 +279,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -191,35 +316,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -230,8 +366,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -250,18 +390,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -271,13 +414,27 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ -EXTRA_DIST = README $(am__append_1) +EXTRA_DIST = README $(am__append_1) $(am__append_2) @HAVE_SYSTEMD_TRUE@systemdsystemunit_DATA = \ -@HAVE_SYSTEMD_TRUE@ nut-driver.service \ +@HAVE_SYSTEMD_TRUE@ nut-driver-enumerator.service \ +@HAVE_SYSTEMD_TRUE@ nut-driver-enumerator.path \ +@HAVE_SYSTEMD_TRUE@ nut-driver@.service \ @HAVE_SYSTEMD_TRUE@ nut-monitor.service \ -@HAVE_SYSTEMD_TRUE@ nut-server.service +@HAVE_SYSTEMD_TRUE@ nut-server.service \ +@HAVE_SYSTEMD_TRUE@ nut-driver.target \ +@HAVE_SYSTEMD_TRUE@ nut.target -@HAVE_SYSTEMD_TRUE@systemdsystemshutdown_SCRIPTS = nutshutdown +@HAVE_SYSTEMD_TRUE@systemdtmpfiles_DATA = \ +@HAVE_SYSTEMD_TRUE@ nut-common.tmpfiles + +@HAVE_SYSTEMD_TRUE@systemdshutdown_SCRIPTS = nutshutdown +@HAVE_SYSTEMD_TRUE@libexec_SCRIPTS = ../upsdrvsvcctl/nut-driver-enumerator.sh +@HAVE_SYSTEMD_TRUE@sbin_SCRIPTS = ../upsdrvsvcctl/upsdrvsvcctl + +# NOTE: Do not EXTRA_DIST nut-common.tmpfiles.in - it is generated per build + +# Generated by autogen.sh and needed to run the configure script: +MAINTAINERCLEANFILES = Makefile.in .dirstamp nut-common.tmpfiles.in all: all-am .SUFFIXES: @@ -293,14 +450,13 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/systemd/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu scripts/systemd/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -311,10 +467,27 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): -install-systemdsystemshutdownSCRIPTS: $(systemdsystemshutdown_SCRIPTS) +nut-common.tmpfiles: $(top_builddir)/config.status $(srcdir)/nut-common.tmpfiles.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nut-driver@.service: $(top_builddir)/config.status $(srcdir)/nut-driver@.service.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nut-monitor.service: $(top_builddir)/config.status $(srcdir)/nut-monitor.service.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nut-server.service: $(top_builddir)/config.status $(srcdir)/nut-server.service.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nut-driver-enumerator.service: $(top_builddir)/config.status $(srcdir)/nut-driver-enumerator.service.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nut-driver-enumerator.path: $(top_builddir)/config.status $(srcdir)/nut-driver-enumerator.path.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nutshutdown: $(top_builddir)/config.status $(srcdir)/nutshutdown.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +install-libexecSCRIPTS: $(libexec_SCRIPTS) @$(NORMAL_INSTALL) - test -z "$(systemdsystemshutdowndir)" || $(MKDIR_P) "$(DESTDIR)$(systemdsystemshutdowndir)" - @list='$(systemdsystemshutdown_SCRIPTS)'; test -n "$(systemdsystemshutdowndir)" || list=; \ + @list='$(libexec_SCRIPTS)'; test -n "$(libexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ @@ -332,19 +505,87 @@ install-systemdsystemshutdownSCRIPTS: $(systemdsystemshutdown_SCRIPTS) while read type dir files; do \ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ test -z "$$files" || { \ - echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(systemdsystemshutdowndir)$$dir'"; \ - $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(systemdsystemshutdowndir)$$dir" || exit $$?; \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \ } \ ; done -uninstall-systemdsystemshutdownSCRIPTS: +uninstall-libexecSCRIPTS: @$(NORMAL_UNINSTALL) - @list='$(systemdsystemshutdown_SCRIPTS)'; test -n "$(systemdsystemshutdowndir)" || exit 0; \ + @list='$(libexec_SCRIPTS)'; test -n "$(libexecdir)" || exit 0; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 's,.*/,,;$(transform)'`; \ - test -n "$$list" || exit 0; \ - echo " ( cd '$(DESTDIR)$(systemdsystemshutdowndir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(systemdsystemshutdowndir)" && rm -f $$files + dir='$(DESTDIR)$(libexecdir)'; $(am__uninstall_files_from_dir) +install-sbinSCRIPTS: $(sbin_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(sbin_SCRIPTS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(sbindir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-sbinSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(sbin_SCRIPTS)'; test -n "$(sbindir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(sbindir)'; $(am__uninstall_files_from_dir) +install-systemdshutdownSCRIPTS: $(systemdshutdown_SCRIPTS) + @$(NORMAL_INSTALL) + @list='$(systemdshutdown_SCRIPTS)'; test -n "$(systemdshutdowndir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(systemdshutdowndir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(systemdshutdowndir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + if test -f "$$d$$p"; then echo "$$d$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n' \ + -e 'h;s|.*|.|' \ + -e 'p;x;s,.*/,,;$(transform)' | sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1; } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) { files[d] = files[d] " " $$1; \ + if (++n[d] == $(am__install_max)) { \ + print "f", d, files[d]; n[d] = 0; files[d] = "" } } \ + else { print "f", d "/" $$4, $$1 } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_SCRIPT) $$files '$(DESTDIR)$(systemdshutdowndir)$$dir'"; \ + $(INSTALL_SCRIPT) $$files "$(DESTDIR)$(systemdshutdowndir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-systemdshutdownSCRIPTS: + @$(NORMAL_UNINSTALL) + @list='$(systemdshutdown_SCRIPTS)'; test -n "$(systemdshutdowndir)" || exit 0; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 's,.*/,,;$(transform)'`; \ + dir='$(DESTDIR)$(systemdshutdowndir)'; $(am__uninstall_files_from_dir) mostlyclean-libtool: -rm -f *.lo @@ -353,8 +594,11 @@ clean-libtool: -rm -rf .libs _libs install-systemdsystemunitDATA: $(systemdsystemunit_DATA) @$(NORMAL_INSTALL) - test -z "$(systemdsystemunitdir)" || $(MKDIR_P) "$(DESTDIR)$(systemdsystemunitdir)" @list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(systemdsystemunitdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(systemdsystemunitdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -368,17 +612,39 @@ uninstall-systemdsystemunitDATA: @$(NORMAL_UNINSTALL) @list='$(systemdsystemunit_DATA)'; test -n "$(systemdsystemunitdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - test -n "$$files" || exit 0; \ - echo " ( cd '$(DESTDIR)$(systemdsystemunitdir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(systemdsystemunitdir)" && rm -f $$files -tags: TAGS -TAGS: + dir='$(DESTDIR)$(systemdsystemunitdir)'; $(am__uninstall_files_from_dir) +install-systemdtmpfilesDATA: $(systemdtmpfiles_DATA) + @$(NORMAL_INSTALL) + @list='$(systemdtmpfiles_DATA)'; test -n "$(systemdtmpfilesdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(systemdtmpfilesdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(systemdtmpfilesdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(systemdtmpfilesdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(systemdtmpfilesdir)" || exit $$?; \ + done -ctags: CTAGS -CTAGS: +uninstall-systemdtmpfilesDATA: + @$(NORMAL_UNINSTALL) + @list='$(systemdtmpfiles_DATA)'; test -n "$(systemdtmpfilesdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(systemdtmpfilesdir)'; $(am__uninstall_files_from_dir) +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -412,7 +678,7 @@ check-am: all-am check: check-am all-am: Makefile $(SCRIPTS) $(DATA) installdirs: - for dir in "$(DESTDIR)$(systemdsystemshutdowndir)" "$(DESTDIR)$(systemdsystemunitdir)"; do \ + for dir in "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(systemdshutdowndir)" "$(DESTDIR)$(systemdsystemunitdir)" "$(DESTDIR)$(systemdtmpfilesdir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: install-am @@ -425,10 +691,15 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -440,6 +711,7 @@ distclean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am @@ -460,14 +732,14 @@ info: info-am info-am: -install-data-am: install-systemdsystemshutdownSCRIPTS \ - install-systemdsystemunitDATA +install-data-am: install-systemdshutdownSCRIPTS \ + install-systemdsystemunitDATA install-systemdtmpfilesDATA install-dvi: install-dvi-am install-dvi-am: -install-exec-am: +install-exec-am: install-libexecSCRIPTS install-sbinSCRIPTS install-html: install-html-am @@ -505,32 +777,31 @@ ps: ps-am ps-am: -uninstall-am: uninstall-systemdsystemshutdownSCRIPTS \ - uninstall-systemdsystemunitDATA +uninstall-am: uninstall-libexecSCRIPTS uninstall-sbinSCRIPTS \ + uninstall-systemdshutdownSCRIPTS \ + uninstall-systemdsystemunitDATA uninstall-systemdtmpfilesDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ - distclean distclean-generic distclean-libtool distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-info install-info-am install-man install-pdf \ - install-pdf-am install-ps install-ps-am install-strip \ - install-systemdsystemshutdownSCRIPTS \ - install-systemdsystemunitDATA installcheck installcheck-am \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am \ + install-libexecSCRIPTS install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-sbinSCRIPTS install-strip \ + install-systemdshutdownSCRIPTS install-systemdsystemunitDATA \ + install-systemdtmpfilesDATA installcheck installcheck-am \ installdirs maintainer-clean maintainer-clean-generic \ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ - ps ps-am uninstall uninstall-am \ - uninstall-systemdsystemshutdownSCRIPTS \ - uninstall-systemdsystemunitDATA + ps ps-am tags-am uninstall uninstall-am \ + uninstall-libexecSCRIPTS uninstall-sbinSCRIPTS \ + uninstall-systemdshutdownSCRIPTS \ + uninstall-systemdsystemunitDATA uninstall-systemdtmpfilesDATA +.PRECIOUS: Makefile -@HAVE_SYSTEMD_TRUE@%.service: %.service.in -@HAVE_SYSTEMD_TRUE@ sed -e 's,@sbindir\@,$(sbindir),g' $< > $@ - -@HAVE_SYSTEMD_TRUE@nutshutdown: nutshutdown.in -@HAVE_SYSTEMD_TRUE@ sed -e 's,@sbindir\@,$(sbindir),g' $< > $@ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/scripts/systemd/README b/scripts/systemd/README index c8930ab..b006358 100644 --- a/scripts/systemd/README +++ b/scripts/systemd/README @@ -4,5 +4,10 @@ Service Manager. These files are automatically installed, upon detection (at configure time) of a systemd enabled system. -Contributed by Michal Hlavinka +This also uses the nut-driver-enumerator.sh (service and implementation +method) and upsdrvsvcctl (tool) to manage NUT drivers as service instances +located in ../upsdrvsvcctl/ source subdirectory. + +Contributed by Michal Hlavinka +Updated 2016-2018 by Michal Hrusecky and Jim Klimov diff --git a/scripts/systemd/nut-common.tmpfiles.in b/scripts/systemd/nut-common.tmpfiles.in new file mode 100644 index 0000000..e69e784 --- /dev/null +++ b/scripts/systemd/nut-common.tmpfiles.in @@ -0,0 +1,5 @@ +# State file (e.g. upsd to driver) and pidfile location for NUT: +d @STATEPATH@/nut 0770 @RUN_AS_USER@ @RUN_AS_GROUP@ - - +X @STATEPATH@/nut +d @PIDPATH@/nut 0770 @RUN_AS_USER@ @RUN_AS_GROUP@ - - +X @PIDPATH@/nut diff --git a/scripts/systemd/nut-driver-enumerator.path.in b/scripts/systemd/nut-driver-enumerator.path.in new file mode 100644 index 0000000..11f3406 --- /dev/null +++ b/scripts/systemd/nut-driver-enumerator.path.in @@ -0,0 +1,7 @@ +# Trigger restart of nut-driver-enumerator.service whenever ups.conf is edited + +[Path] +PathModified=@CONFPATH@/ups.conf + +[Install] +WantedBy=nut.target diff --git a/scripts/systemd/nut-driver-enumerator.service.in b/scripts/systemd/nut-driver-enumerator.service.in new file mode 100644 index 0000000..51e6d15 --- /dev/null +++ b/scripts/systemd/nut-driver-enumerator.service.in @@ -0,0 +1,30 @@ +[Unit] +# This unit starts early in system lifecycle to set up nut-driver instances. +# End-user may also restart this unit after editing ups.conf to automatically +# un-register or add new instances as appropriate. +Description=Network UPS Tools - enumeration of configure-file devices into systemd unit instances +After=local-fs.target +Before=nut-driver.target +PartOf=nut.target + +[Service] +### Script needs privileges to restart units +#User=@RUN_AS_USER@ +#Group=@RUN_AS_GROUP@ +User=root +SyslogIdentifier=%N +# it is expected that the process has to exit before systemd starts follow-up +# units; it should not be a problem for those +Type=oneshot +# Currently systemd does not support restarting of oneshot services, and does +# not seem to guarantee that other services would only start after the script +# completes, for a non-oneshot case. The script itself handles restarting of +# nut-server which is the primary concerned dependency at the moment, so we +# don't want it to fail the unit (when it can't restart). +Environment=REPORT_RESTART_42=no +EnvironmentFile=-@CONFPATH@/nut.conf +ExecStart=@NUT_LIBEXECDIR@/nut-driver-enumerator.sh +ExecReload=@NUT_LIBEXECDIR@/nut-driver-enumerator.sh + +[Install] +WantedBy=nut.target diff --git a/scripts/systemd/nut-driver.service.in b/scripts/systemd/nut-driver.service.in deleted file mode 100644 index eef3480..0000000 --- a/scripts/systemd/nut-driver.service.in +++ /dev/null @@ -1,10 +0,0 @@ -[Unit] -Description=Network UPS Tools - power device driver controller -After=local-fs.target network.target -StopWhenUnneeded=yes - -[Service] -ExecStart=/sbin/upsdrvctl start -ExecStop=/sbin/upsdrvctl stop -Type=forking - diff --git a/scripts/systemd/nut-driver.target b/scripts/systemd/nut-driver.target new file mode 100644 index 0000000..d994ab9 --- /dev/null +++ b/scripts/systemd/nut-driver.target @@ -0,0 +1,8 @@ +[Unit] +Description=Network UPS Tools - target for power device drivers on this system +After=local-fs.target +# network.target +PartOf=nut.target + +[Install] +WantedBy=nut.target diff --git a/scripts/systemd/nut-driver@.service.in b/scripts/systemd/nut-driver@.service.in new file mode 100644 index 0000000..3f1d028 --- /dev/null +++ b/scripts/systemd/nut-driver@.service.in @@ -0,0 +1,67 @@ +[Unit] +Description=Network UPS Tools - device driver for %I +After=local-fs.target + +# Note: If the "Before" line below is uncommented, the target unit +# would only become initialized after the driver units are all in +# a final state (active, failed, ...) and would allow nut-server +# (upsd) to start up and represent those devices on the network. +# With this constraint commented away, the nut-server should start +# earlier, but may initially report some devices as Not connected +# (they should appear when drivers complete their initialization - +# e.g. snmp walks of large MIBs can take a while): +#Before=nut-driver.target + +# Propagate stopping of the target: +PartOf=nut-driver.target + +# Note: The choice of "network.target" allows to schedule this unit +# roughly when the network stack of this OS is ready (e.g. that the +# subsequent `upsd` will have a `0.0.0.0` or a `localhost` to bind +# to); however this target does not ensure availability of a real +# connection or final IP addresses. Drivers that require network as +# a media for interaction with UPSes (snmp-ups, netxml-ups, ipmi etc.) +# may want to extend this unit with `Requires=network-online.target` +# instead. Also note that *generally* this should not be a problem, +# since the drivers have a few retries with timeouts during startup, +# and typically by the time the box gets an IP address, the driver +# is still retrying to start and will succeed. +# Extending the unit does not require *this* file to be edited, you +# can instead drop in an additional piece of configuration, e.g. add +# a `/etc/systemd/system/nut-driver@.service.d/network.conf` with: +# [Unit] +# Requires=network-online.target +# After=network-online.target +# If your `upsd` requires specific IP addresses to be available before +# starting, a `/etc/systemd/system/nut-driver.target.d/network.conf` +# can be used in a similar manner. +# Finally note that "nut-driver-enumerator.service" should take care of this. + +[Service] +EnvironmentFile=-@CONFPATH@/nut.conf +SyslogIdentifier=%N +ExecStart=/bin/sh -c 'NUTDEV="`@NUT_LIBEXECDIR@/nut-driver-enumerator.sh --get-device-for-service %i`" && [ -n "$NUTDEV" ] || { echo "FATAL: Could not find a NUT device section for service unit %i" >&2 ; exit 1 ; } ; @SBINDIR@/upsdrvctl start "$NUTDEV"' +ExecStop=/bin/sh -c 'NUTDEV="`@NUT_LIBEXECDIR@/nut-driver-enumerator.sh --get-device-for-service %i`" && [ -n "$NUTDEV" ] || { echo "FATAL: Could not find a NUT device section for service unit %i" >&2 ; exit 1 ; } ; @SBINDIR@/upsdrvctl stop "$NUTDEV"' +# Restart really always, do not stop trying: +StartLimitInterval=0 +Restart=always +# Protract the "hold-off" interval, so if the device connection is +# lost, the driver does not reapidly restart and fail too many times, +# and then systemd would keep the unit failed without further retries. +# Notably, this helps start "dummy-ups" drivers retranslating local +# devices (so getting a chicken-and-egg problem for driver-upsd-driver +# orderly series of initializations). More details in NUT issue #779. +RestartSec=15s +Type=forking +# Note: If you customize the "maxstartdelay" in ups.conf or in your +# NUT compilation defaults, so it exceeds the default systemd unit +# startup timeout (typically 90 sec), then make sure to set a slightly +# longer systemd timeout for the nut-driver unit instances. You can +# do this by populating a drop-in configuration, so it is not later +# overwritten by updates to your NUT package -- create a dir+file: +# /etc/systemd/system/nut-driver@.service.d/timeout.conf with lines: +# [Service] +# TimeoutStartSec=190s + +[Install] +WantedBy=nut-driver.target diff --git a/scripts/systemd/nut-monitor.service.in b/scripts/systemd/nut-monitor.service.in index 8a7f3b2..4ee216e 100644 --- a/scripts/systemd/nut-monitor.service.in +++ b/scripts/systemd/nut-monitor.service.in @@ -1,11 +1,26 @@ [Unit] Description=Network UPS Tools - power device monitor and shutdown controller After=local-fs.target network.target nut-server.service +# Note: We do not specify Requires nut-server.service because +# the `upsd` daemon(s) may be running on a different machine +# (connected to the UPSes) than the `upsmon` shutdown protector. +# The "Wants" directive would try to start the nut-server but +# would not abort if that attempt fails for whatever reason. +Wants=nut-server.service +# Extending the unit does not require *this* file to be edited, you +# can instead drop in an additional piece of configuration, e.g. add +# a `/etc/systemd/system/nut-monitor.service.d/network.conf` with: +# [Unit] +# Requires=network-online.target +# After=network-online.target +PartOf=nut.target [Service] -ExecStart=/usr/sbin/upsmon -PIDFile=/var/run/nut/upsmon.pid -Type=forking +EnvironmentFile=-@CONFPATH@/nut.conf +SyslogIdentifier=%N +ExecStart=@SBINDIR@/upsmon -F +ExecReload=@SBINDIR@/upsmon -c reload +PIDFile=@PIDPATH@/upsmon.pid [Install] -WantedBy=multi-user.target +WantedBy=nut.target diff --git a/scripts/systemd/nut-server.service.in b/scripts/systemd/nut-server.service.in index 38b1322..713f5c4 100644 --- a/scripts/systemd/nut-server.service.in +++ b/scripts/systemd/nut-server.service.in @@ -1,12 +1,29 @@ [Unit] Description=Network UPS Tools - power devices information server -After=local-fs.target network.target nut-driver.service -Requires=nut-driver.service +After=local-fs.target network.target nut-driver.target +# We don't Require drivers to be successfully started! This would be +# a change of behavior compared to init SysV, and could prevent from +# accessing successfully started, at least to audit a system. +Wants=nut-driver.target +# The `upsd` is a networked service (even if bound to a `localhost`) +# so it requires that the OS has some notion of networking already. +# Extending the unit does not require *this* file to be edited, you +# can instead drop in an additional piece of configuration, e.g. add +# a `/etc/systemd/system/nut-server.service.d/network.conf` with: +# [Unit] +# Requires=network-online.target +# After=network-online.target +Requires=network.target Before=nut-monitor.service +PartOf=nut.target [Service] -ExecStart=/usr/sbin/upsd -Type=forking +EnvironmentFile=-@CONFPATH@/nut.conf +SyslogIdentifier=%N +# Note: foreground mode by default skips writing a PID file (and +# needs Type=simple); can use "-FF" here to create one anyway: +ExecStart=@SBINDIR@/upsd -F +ExecReload=@SBINDIR@/upsd -c reload -P $MAINPID [Install] -WantedBy=multi-user.target +WantedBy=nut.target diff --git a/scripts/systemd/nut.target b/scripts/systemd/nut.target new file mode 100644 index 0000000..f744d29 --- /dev/null +++ b/scripts/systemd/nut.target @@ -0,0 +1,8 @@ +[Unit] +Description=Network UPS Tools - target for power device drivers, data server and monitoring client (if enabled) on this system +After=local-fs.target nut-driver.target nut-server.service nut-monitor.service +Wants=local-fs.target nut-driver.target nut-server.service nut-monitor.service +# network.target + +[Install] +WantedBy=multi-user.target diff --git a/scripts/systemd/nutshutdown.in b/scripts/systemd/nutshutdown.in old mode 100644 new mode 100755 index 3406592..ace2485 --- a/scripts/systemd/nutshutdown.in +++ b/scripts/systemd/nutshutdown.in @@ -1,2 +1,23 @@ #!/bin/sh -upsmon -K >/dev/null 2>&1 && upsdrvctl shutdown + +# This script requires both nut-server (drivers) +# and nut-client (upsmon) to be present locally +# and on mounted filesystems +[ -x "@SBINDIR@/upsmon" ] && [ -x "@SBINDIR@/upsdrvctl" ] || exit + +if @SBINDIR@/upsmon -K >/dev/null 2>&1; then + # The argument may be anything compatible with sleep + # (not necessarily a non-negative integer) + wait_delay="`/bin/sed -ne 's#^ *POWEROFF_WAIT= *\(.*\)$#\1#p' @CONFPATH@/nut.conf`" || wait_delay="" + + @SBINDIR@/upsdrvctl shutdown + + if [ -n "$wait_delay" ] ; then + /bin/sleep $wait_delay + # We need to pass --force twice here to bypass systemd and execute the + # reboot directly ourself. + /bin/systemctl reboot --force --force + fi +fi + +exit 0 diff --git a/scripts/udev/Makefile.am b/scripts/udev/Makefile.am index fcd6eeb..5d299a7 100644 --- a/scripts/udev/Makefile.am +++ b/scripts/udev/Makefile.am @@ -3,7 +3,7 @@ if WITH_UDEV udevrulesdir = $(udevdir)/rules.d udevrules_DATA = if WITH_USB - udevrules_DATA += 52-nut-usbups.rules + udevrules_DATA += 62-nut-usbups.rules endif if WITH_IPMI udevrules_DATA += 52-nut-ipmipsu.rules @@ -12,13 +12,23 @@ endif EXTRA_DIST = README -52-nut-usbups.rules: nut-usbups.rules - cp nut-usbups.rules 52-nut-usbups.rules +62-nut-usbups.rules: nut-usbups.rules + cp nut-usbups.rules $@ 52-nut-ipmipsu.rules: nut-ipmipsu.rules - cp nut-ipmipsu.rules 52-nut-ipmipsu.rules + cp nut-ipmipsu.rules $@ +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +# Generated by configure script: DISTCLEANFILES = nut-usbups.rules nut-ipmipsu.rules -CLEANFILES = 52-nut-usbups.rules 52-nut-ipmipsu.rules + +CLEANFILES = 62-nut-usbups.rules 52-nut-ipmipsu.rules + # we should never remove this one, apart from a distclean-check #MAINTAINERCLEANFILES = nut-usbups.rules.in + +# Generated by autogen.sh and needed to run the configure script +# (technically, generated by tools/nut-usbinfo.pl script among +# GENERATED_USB_OS_FILES): +MAINTAINERCLEANFILES += nut-usbups.rules.in nut-usbups.rules.in.AUTOGEN_WITHOUT diff --git a/scripts/udev/Makefile.in b/scripts/udev/Makefile.in index be711f3..70061d2 100644 --- a/scripts/udev/Makefile.in +++ b/scripts/udev/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -16,6 +15,61 @@ @SET_MAKE@ VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -35,42 +89,69 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ -@WITH_UDEV_TRUE@@WITH_USB_TRUE@am__append_1 = 52-nut-usbups.rules +@WITH_UDEV_TRUE@@WITH_USB_TRUE@am__append_1 = 62-nut-usbups.rules @WITH_IPMI_TRUE@@WITH_UDEV_TRUE@am__append_2 = 52-nut-ipmipsu.rules subdir = scripts/udev -DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ - $(srcdir)/nut-ipmipsu.rules.in $(srcdir)/nut-usbups.rules.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = nut-ipmipsu.rules nut-usbups.rules CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -92,14 +173,26 @@ am__nobase_list = $(am__nobase_strip_setup); \ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } am__installdirs = "$(DESTDIR)$(udevrulesdir)" DATA = $(udevrules_DATA) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/nut-ipmipsu.rules.in \ + $(srcdir)/nut-usbups.rules.in README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -110,16 +203,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -128,11 +230,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -142,14 +241,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -160,21 +260,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -188,35 +297,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -227,8 +347,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -247,18 +371,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -271,8 +398,19 @@ udevdir = @udevdir@ @WITH_UDEV_TRUE@udevrulesdir = $(udevdir)/rules.d @WITH_UDEV_TRUE@udevrules_DATA = $(am__append_1) $(am__append_2) EXTRA_DIST = README + +# we should never remove this one, apart from a distclean-check +#MAINTAINERCLEANFILES = nut-usbups.rules.in + +# Generated by autogen.sh and needed to run the configure script +# (technically, generated by tools/nut-usbinfo.pl script among +# GENERATED_USB_OS_FILES): +MAINTAINERCLEANFILES = Makefile.in .dirstamp nut-usbups.rules.in \ + nut-usbups.rules.in.AUTOGEN_WITHOUT + +# Generated by configure script: DISTCLEANFILES = nut-usbups.rules nut-ipmipsu.rules -CLEANFILES = 52-nut-usbups.rules 52-nut-ipmipsu.rules +CLEANFILES = 62-nut-usbups.rules 52-nut-ipmipsu.rules all: all-am .SUFFIXES: @@ -288,14 +426,13 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/udev/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu scripts/udev/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -318,8 +455,11 @@ clean-libtool: -rm -rf .libs _libs install-udevrulesDATA: $(udevrules_DATA) @$(NORMAL_INSTALL) - test -z "$(udevrulesdir)" || $(MKDIR_P) "$(DESTDIR)$(udevrulesdir)" @list='$(udevrules_DATA)'; test -n "$(udevrulesdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(udevrulesdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(udevrulesdir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -333,17 +473,18 @@ uninstall-udevrulesDATA: @$(NORMAL_UNINSTALL) @list='$(udevrules_DATA)'; test -n "$(udevrulesdir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - test -n "$$files" || exit 0; \ - echo " ( cd '$(DESTDIR)$(udevrulesdir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(udevrulesdir)" && rm -f $$files -tags: TAGS -TAGS: + dir='$(DESTDIR)$(udevrulesdir)'; $(am__uninstall_files_from_dir) +tags TAGS: -ctags: CTAGS -CTAGS: +ctags CTAGS: + +cscope cscopelist: -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -390,10 +531,15 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -407,6 +553,7 @@ distclean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am @@ -476,25 +623,26 @@ uninstall-am: uninstall-udevrulesDATA .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ - distclean distclean-generic distclean-libtool distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-info install-info-am install-man install-pdf \ - install-pdf-am install-ps install-ps-am install-strip \ - install-udevrulesDATA installcheck installcheck-am installdirs \ - maintainer-clean maintainer-clean-generic mostlyclean \ - mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - uninstall uninstall-am uninstall-udevrulesDATA + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip install-udevrulesDATA installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool pdf pdf-am ps ps-am tags-am uninstall \ + uninstall-am uninstall-udevrulesDATA + +.PRECIOUS: Makefile -52-nut-usbups.rules: nut-usbups.rules - cp nut-usbups.rules 52-nut-usbups.rules +62-nut-usbups.rules: nut-usbups.rules + cp nut-usbups.rules $@ 52-nut-ipmipsu.rules: nut-ipmipsu.rules - cp nut-ipmipsu.rules 52-nut-ipmipsu.rules -# we should never remove this one, apart from a distclean-check -#MAINTAINERCLEANFILES = nut-usbups.rules.in + cp nut-ipmipsu.rules $@ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/scripts/udev/README b/scripts/udev/README index a3b5567..ff6a1a0 100644 --- a/scripts/udev/README +++ b/scripts/udev/README @@ -1,6 +1,6 @@ Desc: Udev script for NUT USB and IPMI drivers File: scripts/udev/README -Date: 25 July 2011 +Date: 31 July 2014 Auth: Arnaud Quette This document introduces the Linux udev script for NUT USB @@ -32,7 +32,7 @@ Manual installation To install them manually, copy the rules file(s) to /etc/udev/rules.d (or /lib/udev/rules.d on newer systems) using the command(s): -$ cp -f nut-usbups.rules /etc/udev/rules.d/52-nut-usbups.rules +$ cp -f nut-usbups.rules /etc/udev/rules.d/62-nut-usbups.rules $ cp -f nut-ipmipsu.rules /etc/udev/rules.d/52-nut-ipmipsu.rules You will need to refresh the bus to avoid a reboot for these rules to be diff --git a/scripts/udev/nut-usbups.rules.in b/scripts/udev/nut-usbups.rules.in index 2faf9a2..e073853 100644 --- a/scripts/udev/nut-usbups.rules.in +++ b/scripts/udev/nut-usbups.rules.in @@ -1,15 +1,17 @@ # This file is generated and installed by the Network UPS Tools package. -ACTION!="add|change", GOTO="nut-usbups_rules_end" +ACTION=="remove", GOTO="nut-usbups_rules_end" SUBSYSTEM=="usb_device", GOTO="nut-usbups_rules_real" SUBSYSTEM=="usb", GOTO="nut-usbups_rules_real" -SUBSYSTEM!="usb", GOTO="nut-usbups_rules_end" +GOTO="nut-usbups_rules_end" LABEL="nut-usbups_rules_real" -# Krauler UP-M500VA - blazer_usb +# SNR-UPS-LID-XXXX UPSes - nutdrv_qx ATTR{idVendor}=="0001", ATTR{idProduct}=="0000", MODE="664", GROUP="@RUN_AS_GROUP@" # Hewlett Packard +# e.g. ? - usbhid-ups +ATTR{idVendor}=="03f0", ATTR{idProduct}=="0001", MODE="664", GROUP="@RUN_AS_GROUP@" # T500 - bcmxcp_usb ATTR{idVendor}=="03f0", ATTR{idProduct}=="1f01", MODE="664", GROUP="@RUN_AS_GROUP@" # T750 - bcmxcp_usb @@ -22,10 +24,22 @@ ATTR{idVendor}=="03f0", ATTR{idProduct}=="1f08", MODE="664", GROUP="@RUN_AS_GROU ATTR{idVendor}=="03f0", ATTR{idProduct}=="1f09", MODE="664", GROUP="@RUN_AS_GROUP@" # HP R/T 2200 INTL (like SMART2200RMXL2U) - usbhid-ups ATTR{idVendor}=="03f0", ATTR{idProduct}=="1f0a", MODE="664", GROUP="@RUN_AS_GROUP@" -# HP R1500 G2 INTL - usbhid-ups +# HP R1500 G2 and G3 INTL - usbhid-ups ATTR{idVendor}=="03f0", ATTR{idProduct}=="1fe0", MODE="664", GROUP="@RUN_AS_GROUP@" # HP T750 G2 - usbhid-ups ATTR{idVendor}=="03f0", ATTR{idProduct}=="1fe1", MODE="664", GROUP="@RUN_AS_GROUP@" +# e.g. ? - usbhid-ups +ATTR{idVendor}=="03f0", ATTR{idProduct}=="1fe2", MODE="664", GROUP="@RUN_AS_GROUP@" +# HP T1500 G3 - usbhid-ups +ATTR{idVendor}=="03f0", ATTR{idProduct}=="1fe3", MODE="664", GROUP="@RUN_AS_GROUP@" +# R/T3000 - usbhid-ups +ATTR{idVendor}=="03f0", ATTR{idProduct}=="1fe5", MODE="664", GROUP="@RUN_AS_GROUP@" +# R/T3000 - usbhid-ups +ATTR{idVendor}=="03f0", ATTR{idProduct}=="1fe6", MODE="664", GROUP="@RUN_AS_GROUP@" +# various models - usbhid-ups +ATTR{idVendor}=="03f0", ATTR{idProduct}=="1fe7", MODE="664", GROUP="@RUN_AS_GROUP@" +# various models - usbhid-ups +ATTR{idVendor}=="03f0", ATTR{idProduct}=="1fe8", MODE="664", GROUP="@RUN_AS_GROUP@" # Eaton # various models - usbhid-ups @@ -37,6 +51,26 @@ ATTR{idVendor}=="0463", ATTR{idProduct}=="ffff", MODE="664", GROUP="@RUN_AS_GROU # various models - usbhid-ups ATTR{idVendor}=="047c", ATTR{idProduct}=="ffff", MODE="664", GROUP="@RUN_AS_GROUP@" +# ST Microelectronics +# TS Shara UPSes; vendor ID 0x0483 is from ST Microelectronics - with product IDs delegated to different OEMs - nutdrv_qx +ATTR{idVendor}=="0483", ATTR{idProduct}=="0035", MODE="664", GROUP="@RUN_AS_GROUP@" +# USB IDs device table - usbhid-ups +ATTR{idVendor}=="0483", ATTR{idProduct}=="a113", MODE="664", GROUP="@RUN_AS_GROUP@" + +# IBM +# 6000 VA LCD 4U Rack UPS; 5396-1Kx - usbhid-ups +ATTR{idVendor}=="04b3", ATTR{idProduct}=="0001", MODE="664", GROUP="@RUN_AS_GROUP@" + +# Riello (Cypress Semiconductor Corp.) +# various models - riello_usb +ATTR{idVendor}=="04b4", ATTR{idProduct}=="5500", MODE="664", GROUP="@RUN_AS_GROUP@" + +# Minibox +# openUPS Intelligent UPS (minimum required firmware 1.4) - usbhid-ups +ATTR{idVendor}=="04d8", ATTR{idProduct}=="d004", MODE="664", GROUP="@RUN_AS_GROUP@" +# openUPS Intelligent UPS (minimum required firmware 1.4) - usbhid-ups +ATTR{idVendor}=="04d8", ATTR{idProduct}=="d005", MODE="664", GROUP="@RUN_AS_GROUP@" + # Belkin # F6H375-USB - usbhid-ups ATTR{idVendor}=="050d", ATTR{idProduct}=="0375", MODE="664", GROUP="@RUN_AS_GROUP@" @@ -54,10 +88,14 @@ ATTR{idVendor}=="050d", ATTR{idProduct}=="0910", MODE="664", GROUP="@RUN_AS_GROU ATTR{idVendor}=="050d", ATTR{idProduct}=="0912", MODE="664", GROUP="@RUN_AS_GROUP@" # F6C800-UNV - usbhid-ups ATTR{idVendor}=="050d", ATTR{idProduct}=="0980", MODE="664", GROUP="@RUN_AS_GROUP@" +# Regulator PRO-USB - usbhid-ups +ATTR{idVendor}=="050d", ATTR{idProduct}=="0f51", MODE="664", GROUP="@RUN_AS_GROUP@" # F6C1100-UNV, F6C1200-UNV - usbhid-ups ATTR{idVendor}=="050d", ATTR{idProduct}=="1100", MODE="664", GROUP="@RUN_AS_GROUP@" # APC +# APC AP9584 Serial->USB kit - usbhid-ups +ATTR{idVendor}=="051d", ATTR{idProduct}=="0000", MODE="664", GROUP="@RUN_AS_GROUP@" # various models - usbhid-ups ATTR{idVendor}=="051d", ATTR{idProduct}=="0002", MODE="664", GROUP="@RUN_AS_GROUP@" # various 5G models - usbhid-ups @@ -68,17 +106,31 @@ ATTR{idVendor}=="051d", ATTR{idProduct}=="0003", MODE="664", GROUP="@RUN_AS_GROU ATTR{idVendor}=="0592", ATTR{idProduct}=="0002", MODE="664", GROUP="@RUN_AS_GROUP@" # PW 9140 - usbhid-ups ATTR{idVendor}=="0592", ATTR{idProduct}=="0004", MODE="664", GROUP="@RUN_AS_GROUP@" -# Agiler UPS - blazer_usb +# Agiler UPS - nutdrv_qx ATTR{idVendor}=="05b8", ATTR{idProduct}=="0000", MODE="664", GROUP="@RUN_AS_GROUP@" -# Belkin F6C1200-UNV - blazer_usb + +# Delta UPS +# Delta UPS Amplon R Series, Single Phase UPS, 1/2/3 kVA - usbhid-ups +ATTR{idVendor}=="05dd", ATTR{idProduct}=="041b", MODE="664", GROUP="@RUN_AS_GROUP@" +# Delta/Minuteman Enterprise Plus E1500RM2U - usbhid-ups +ATTR{idVendor}=="05dd", ATTR{idProduct}=="a011", MODE="664", GROUP="@RUN_AS_GROUP@" +# Belkin F6C1200-UNV/Voltronic Power UPSes - nutdrv_qx ATTR{idVendor}=="0665", ATTR{idProduct}=="5161", MODE="664", GROUP="@RUN_AS_GROUP@" -# Phoenixtec -# various models - bcmxcp_usb +# Phoenixtec Power Co., Ltd +# Online Yunto YQ450 - nutdrv_qx ATTR{idVendor}=="06da", ATTR{idProduct}=="0002", MODE="664", GROUP="@RUN_AS_GROUP@" -# Mustek Powermust - blazer_usb +# Mustek Powermust - nutdrv_qx ATTR{idVendor}=="06da", ATTR{idProduct}=="0003", MODE="664", GROUP="@RUN_AS_GROUP@" -# various models - usbhid-ups +# Phoenixtec Innova 3/1 T - nutdrv_qx +ATTR{idVendor}=="06da", ATTR{idProduct}=="0004", MODE="664", GROUP="@RUN_AS_GROUP@" +# Phoenixtec Innova RT - nutdrv_qx +ATTR{idVendor}=="06da", ATTR{idProduct}=="0005", MODE="664", GROUP="@RUN_AS_GROUP@" +# Phoenixtec Innova T - nutdrv_qx +ATTR{idVendor}=="06da", ATTR{idProduct}=="0201", MODE="664", GROUP="@RUN_AS_GROUP@" +# Online Zinto A - nutdrv_qx +ATTR{idVendor}=="06da", ATTR{idProduct}=="0601", MODE="664", GROUP="@RUN_AS_GROUP@" +# PROTECT B / NAS - usbhid-ups ATTR{idVendor}=="06da", ATTR{idProduct}=="ffff", MODE="664", GROUP="@RUN_AS_GROUP@" # iDowell @@ -86,11 +138,11 @@ ATTR{idVendor}=="06da", ATTR{idProduct}=="ffff", MODE="664", GROUP="@RUN_AS_GROU ATTR{idVendor}=="075d", ATTR{idProduct}=="0300", MODE="664", GROUP="@RUN_AS_GROUP@" # Cyber Power Systems -# 900AVR/BC900D, CP1200AVR/BC1200D - usbhid-ups +# 900AVR/BC900D - usbhid-ups ATTR{idVendor}=="0764", ATTR{idProduct}=="0005", MODE="664", GROUP="@RUN_AS_GROUP@" -# Dynex DX-800U? - usbhid-ups +# Dynex DX-800U?, CP1200AVR/BC1200D, CP825AVR-G, CP1000AVRLCD, CP1000PFCLCD, CP1500C, CP550HG, etc. - usbhid-ups ATTR{idVendor}=="0764", ATTR{idProduct}=="0501", MODE="664", GROUP="@RUN_AS_GROUP@" -# OR2200LCDRM2U - usbhid-ups +# OR2200LCDRM2U, OR700LCDRM1U, PR6000LCDRTXL5U - usbhid-ups ATTR{idVendor}=="0764", ATTR{idProduct}=="0601", MODE="664", GROUP="@RUN_AS_GROUP@" # Sweex 1000VA - richcomm_usb ATTR{idVendor}=="0925", ATTR{idProduct}=="1234", MODE="664", GROUP="@RUN_AS_GROUP@" @@ -108,6 +160,8 @@ ATTR{idVendor}=="09ae", ATTR{idProduct}=="1008", MODE="664", GROUP="@RUN_AS_GROU ATTR{idVendor}=="09ae", ATTR{idProduct}=="1009", MODE="664", GROUP="@RUN_AS_GROUP@" # e.g. TrippLite ECO550UPS - usbhid-ups ATTR{idVendor}=="09ae", ATTR{idProduct}=="1010", MODE="664", GROUP="@RUN_AS_GROUP@" +# e.g. TrippLite SU3000LCD2UHV - usbhid-ups +ATTR{idVendor}=="09ae", ATTR{idProduct}=="1330", MODE="664", GROUP="@RUN_AS_GROUP@" # e.g. TrippLite OMNI1000LCD - usbhid-ups ATTR{idVendor}=="09ae", ATTR{idProduct}=="2005", MODE="664", GROUP="@RUN_AS_GROUP@" # e.g. TrippLite OMNI900LCD - usbhid-ups @@ -142,6 +196,10 @@ ATTR{idVendor}=="09ae", ATTR{idProduct}=="3013", MODE="664", GROUP="@RUN_AS_GROU ATTR{idVendor}=="09ae", ATTR{idProduct}=="3014", MODE="664", GROUP="@RUN_AS_GROUP@" # e.g. ? - usbhid-ups ATTR{idVendor}=="09ae", ATTR{idProduct}=="3015", MODE="664", GROUP="@RUN_AS_GROUP@" +# e.g. TrippLite Smart1500LCD (newer unit) - usbhid-ups +ATTR{idVendor}=="09ae", ATTR{idProduct}=="3016", MODE="664", GROUP="@RUN_AS_GROUP@" +# e.g. TrippLite AVR750U (newer unit) - usbhid-ups +ATTR{idVendor}=="09ae", ATTR{idProduct}=="3024", MODE="664", GROUP="@RUN_AS_GROUP@" # e.g. TrippLite SmartOnline SU1500RTXL2UA (older unit?) - usbhid-ups ATTR{idVendor}=="09ae", ATTR{idProduct}=="4001", MODE="664", GROUP="@RUN_AS_GROUP@" # e.g. TrippLite SmartOnline SU6000RT4U? - usbhid-ups @@ -160,7 +218,9 @@ ATTR{idVendor}=="09ae", ATTR{idProduct}=="4007", MODE="664", GROUP="@RUN_AS_GROU ATTR{idVendor}=="09ae", ATTR{idProduct}=="4008", MODE="664", GROUP="@RUN_AS_GROUP@" # PowerCOM -# PowerCOM BNT-xxxAP - usbhid-ups +# PowerCOM Vanguard and BNT-xxxAP - usbhid-ups +ATTR{idVendor}=="0d9f", ATTR{idProduct}=="0001", MODE="664", GROUP="@RUN_AS_GROUP@" +# PowerCOM Vanguard and BNT-xxxAP - usbhid-ups ATTR{idVendor}=="0d9f", ATTR{idProduct}=="0004", MODE="664", GROUP="@RUN_AS_GROUP@" # PowerCOM IMP - IMPERIAL Series - usbhid-ups ATTR{idVendor}=="0d9f", ATTR{idProduct}=="00a2", MODE="664", GROUP="@RUN_AS_GROUP@" @@ -172,15 +232,67 @@ ATTR{idVendor}=="0d9f", ATTR{idProduct}=="00a4", MODE="664", GROUP="@RUN_AS_GROU ATTR{idVendor}=="0d9f", ATTR{idProduct}=="00a5", MODE="664", GROUP="@RUN_AS_GROUP@" # PowerCOM BNT - Black Knight Pro - usbhid-ups ATTR{idVendor}=="0d9f", ATTR{idProduct}=="00a6", MODE="664", GROUP="@RUN_AS_GROUP@" -# Unitek Alpha 1200Sx - blazer_usb +# Unitek Alpha 1200Sx - nutdrv_qx ATTR{idVendor}=="0f03", ATTR{idProduct}=="0001", MODE="664", GROUP="@RUN_AS_GROUP@" # Liebert # Liebert PowerSure PSA UPS - usbhid-ups ATTR{idVendor}=="10af", ATTR{idProduct}=="0001", MODE="664", GROUP="@RUN_AS_GROUP@" -# GE EP series - blazer_usb +# Liebert PowerSure PSI 1440 - usbhid-ups +ATTR{idVendor}=="10af", ATTR{idProduct}=="0004", MODE="664", GROUP="@RUN_AS_GROUP@" +# Liebert GXT3 - usbhid-ups +ATTR{idVendor}=="10af", ATTR{idProduct}=="0008", MODE="664", GROUP="@RUN_AS_GROUP@" +# GE EP series - nutdrv_qx ATTR{idVendor}=="14f0", ATTR{idProduct}=="00c9", MODE="664", GROUP="@RUN_AS_GROUP@" -# Ablerex 625L USB - blazer_usb + +# Legrand +# Legrand Keor SP - usbhid-ups +ATTR{idVendor}=="1cb0", ATTR{idProduct}=="0032", MODE="664", GROUP="@RUN_AS_GROUP@" +# Legrand Daker DK / DK Plus - nutdrv_qx +ATTR{idVendor}=="1cb0", ATTR{idProduct}=="0035", MODE="664", GROUP="@RUN_AS_GROUP@" +# Legrand Keor PDU - usbhid-ups +ATTR{idVendor}=="1cb0", ATTR{idProduct}=="0038", MODE="664", GROUP="@RUN_AS_GROUP@" + +# Arduino +# Arduino Leonardo, Leonardo ETH and Pro Micro - usbhid-ups +ATTR{idVendor}=="2341", ATTR{idProduct}=="0036", MODE="664", GROUP="@RUN_AS_GROUP@" +# Arduino Leonardo, Leonardo ETH and Pro Micro - usbhid-ups +ATTR{idVendor}=="2341", ATTR{idProduct}=="8036", MODE="664", GROUP="@RUN_AS_GROUP@" + +# Arduino +# Arduino Leonardo, Leonardo ETH and Pro Micro - usbhid-ups +ATTR{idVendor}=="2A03", ATTR{idProduct}=="0036", MODE="664", GROUP="@RUN_AS_GROUP@" +# Arduino Leonardo, Leonardo ETH and Pro Micro - usbhid-ups +ATTR{idVendor}=="2A03", ATTR{idProduct}=="0040", MODE="664", GROUP="@RUN_AS_GROUP@" +# Arduino Leonardo, Leonardo ETH and Pro Micro - usbhid-ups +ATTR{idVendor}=="2A03", ATTR{idProduct}=="8036", MODE="664", GROUP="@RUN_AS_GROUP@" +# Arduino Leonardo, Leonardo ETH and Pro Micro - usbhid-ups +ATTR{idVendor}=="2A03", ATTR{idProduct}=="8040", MODE="664", GROUP="@RUN_AS_GROUP@" + +# AEG +# PROTECT B / NAS - usbhid-ups +ATTR{idVendor}=="2b2d", ATTR{idProduct}=="ffff", MODE="664", GROUP="@RUN_AS_GROUP@" + +# Ever +# USB IDs device table - usbhid-ups +ATTR{idVendor}=="2e51", ATTR{idProduct}=="0000", MODE="664", GROUP="@RUN_AS_GROUP@" +# USB IDs device table - usbhid-ups +ATTR{idVendor}=="2e51", ATTR{idProduct}=="ffff", MODE="664", GROUP="@RUN_AS_GROUP@" + +# Salicru +# SLC TWIN PRO2<=3KVA per https://github.com/networkupstools/nut/issues/450 - usbhid-ups +ATTR{idVendor}=="2e66", ATTR{idProduct}=="0201", MODE="664", GROUP="@RUN_AS_GROUP@" +# SLC TWIN PRO2<=3KVA per https://github.com/networkupstools/nut/issues/450 - usbhid-ups +ATTR{idVendor}=="2e66", ATTR{idProduct}=="0202", MODE="664", GROUP="@RUN_AS_GROUP@" +# SLC TWIN PRO2<=3KVA per https://github.com/networkupstools/nut/issues/450 - usbhid-ups +ATTR{idVendor}=="2e66", ATTR{idProduct}=="0203", MODE="664", GROUP="@RUN_AS_GROUP@" +# https://www.salicru.com/sps-home.html - usbhid-ups +ATTR{idVendor}=="2e66", ATTR{idProduct}=="0300", MODE="664", GROUP="@RUN_AS_GROUP@" + +# Powervar +# Powervar - usbhid-ups +ATTR{idVendor}=="4234", ATTR{idProduct}=="0002", MODE="664", GROUP="@RUN_AS_GROUP@" +# Ablerex 625L USB (Note: earlier best-fit was "krauler_subdriver" before PR #1135) - nutdrv_qx ATTR{idVendor}=="ffff", ATTR{idProduct}=="0000", MODE="664", GROUP="@RUN_AS_GROUP@" LABEL="nut-usbups_rules_end" diff --git a/scripts/ufw/Makefile.am b/scripts/ufw/Makefile.am new file mode 100644 index 0000000..dc72ff3 --- /dev/null +++ b/scripts/ufw/Makefile.am @@ -0,0 +1,2 @@ +MAINTAINERCLEANFILES = Makefile.in .dirstamp +CLEANFILES = *-spellchecked diff --git a/scripts/hal/Makefile.in b/scripts/ufw/Makefile.in similarity index 65% rename from scripts/hal/Makefile.in rename to scripts/ufw/Makefile.in index e8125fd..039dbc9 100644 --- a/scripts/hal/Makefile.in +++ b/scripts/ufw/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -14,8 +13,62 @@ # PARTICULAR PURPOSE. @SET_MAKE@ - VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -35,69 +88,79 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ -subdir = scripts/hal -DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \ - $(srcdir)/ups-nut-device.fdi.in +subdir = scripts/ufw ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h -CONFIG_CLEAN_FILES = ups-nut-device.fdi +CONFIG_CLEAN_FILES = nut.ufw.profile CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = SOURCES = DIST_SOURCES = -am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; -am__vpath_adj = case $$p in \ - $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ - *) f=$$p;; \ - esac; -am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; -am__install_max = 40 -am__nobase_strip_setup = \ - srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` -am__nobase_strip = \ - for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" -am__nobase_list = $(am__nobase_strip_setup); \ - for p in $$list; do echo "$$p $$p"; done | \ - sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ - $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ - if (++n[$$2] == $(am__install_max)) \ - { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ - END { for (dir in files) print dir, files[dir] }' -am__base_list = \ - sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ - sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__installdirs = "$(DESTDIR)$(halfdidir)" -DATA = $(halfdi_DATA) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/nut.ufw.profile.in \ + README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -108,16 +171,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -126,11 +198,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -140,14 +209,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -158,21 +228,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -186,35 +265,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -225,8 +315,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -245,18 +339,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -266,13 +363,8 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ -@WITH_HAL_TRUE@halfdidir = $(HAL_FDI_PATH) -@WITH_HAL_TRUE@halfdi_DATA = 20-ups-nut-device.fdi -EXTRA_DIST = ups-nut-device.fdi.in -DISTCLEANFILES = ups-nut-device.fdi -# we should never remove this one, apart from a distclean-check -#MAINTAINERCLEANFILES = ups-nut-device.fdi.in -CLEANFILES = 20-ups-nut-device.fdi +MAINTAINERCLEANFILES = Makefile.in .dirstamp +CLEANFILES = *-spellchecked all: all-am .SUFFIXES: @@ -285,17 +377,16 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/hal/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/ufw/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu scripts/hal/Makefile -.PRECIOUS: Makefile + $(AUTOMAKE) --gnu scripts/ufw/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -306,7 +397,7 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): -ups-nut-device.fdi: $(top_builddir)/config.status $(srcdir)/ups-nut-device.fdi.in +nut.ufw.profile: $(top_builddir)/config.status $(srcdir)/nut.ufw.profile.in cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: @@ -314,34 +405,17 @@ mostlyclean-libtool: clean-libtool: -rm -rf .libs _libs -install-halfdiDATA: $(halfdi_DATA) - @$(NORMAL_INSTALL) - test -z "$(halfdidir)" || $(MKDIR_P) "$(DESTDIR)$(halfdidir)" - @list='$(halfdi_DATA)'; test -n "$(halfdidir)" || list=; \ - for p in $$list; do \ - if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ - echo "$$d$$p"; \ - done | $(am__base_list) | \ - while read files; do \ - echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(halfdidir)'"; \ - $(INSTALL_DATA) $$files "$(DESTDIR)$(halfdidir)" || exit $$?; \ - done +tags TAGS: -uninstall-halfdiDATA: - @$(NORMAL_UNINSTALL) - @list='$(halfdi_DATA)'; test -n "$(halfdidir)" || list=; \ - files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - test -n "$$files" || exit 0; \ - echo " ( cd '$(DESTDIR)$(halfdidir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(halfdidir)" && rm -f $$files -tags: TAGS -TAGS: +ctags CTAGS: -ctags: CTAGS -CTAGS: +cscope cscopelist: -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -373,11 +447,8 @@ distdir: $(DISTFILES) done check-am: all-am check: check-am -all-am: Makefile $(DATA) +all-am: Makefile installdirs: - for dir in "$(DESTDIR)$(halfdidir)"; do \ - test -z "$$dir" || $(MKDIR_P) "$$dir"; \ - done install: install-am install-exec: install-exec-am install-data: install-data-am @@ -388,10 +459,15 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -400,11 +476,11 @@ clean-generic: distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) - -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am @@ -425,7 +501,7 @@ info: info-am info-am: -install-data-am: install-halfdiDATA +install-data-am: install-dvi: install-dvi-am @@ -469,26 +545,24 @@ ps: ps-am ps-am: -uninstall-am: uninstall-halfdiDATA +uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ - distclean distclean-generic distclean-libtool distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-halfdiDATA install-html \ + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ install-html-am install-info install-info-am install-man \ install-pdf install-pdf-am install-ps install-ps-am \ install-strip installcheck installcheck-am installdirs \ maintainer-clean maintainer-clean-generic mostlyclean \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - uninstall uninstall-am uninstall-halfdiDATA + tags-am uninstall uninstall-am +.PRECIOUS: Makefile -# FIXME: should be able to use $< here. -20-ups-nut-device.fdi: ups-nut-device.fdi - cp ups-nut-device.fdi $@ # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/scripts/ufw/README b/scripts/ufw/README new file mode 100644 index 0000000..701f323 --- /dev/null +++ b/scripts/ufw/README @@ -0,0 +1,30 @@ +Uncomplicated Firewall (UFW) support +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +NUT can tightly integrate with +link:http://en.wikipedia.org/wiki/Uncomplicated_Firewall[Uncomplicated Firewall] +using the provided profile (nut.ufw.profile). + +You must first install the profile on your system: + + $ cp nut.ufw.profile /etc/ufw/applications.d/ + +To enable outside access to your local upsd, use: + + $ ufw allow NUT + +To restrict access to the network '192.168.X.Y', use: + + $ ufw allow from 192.168.0.0/16 to any app NUT + +You can also use graphical frontends, such as gui-ufw (gufw), ufw-kde +or ufw-frontends. + +For more information, refer to: + + - link:http://gufw.tuxfamily.org/[UFW homepage], + - link:https://launchpad.net/ufw[UFW project page], + - link:https://wiki.ubuntu.com/UncomplicatedFirewall[UFW wiki], + - UFW manual page, section APPLICATION INTEGRATION + + diff --git a/scripts/ufw/nut.ufw.profile.in b/scripts/ufw/nut.ufw.profile.in new file mode 100644 index 0000000..df9202d --- /dev/null +++ b/scripts/ufw/nut.ufw.profile.in @@ -0,0 +1,4 @@ +[NUT] +title=NUT - Network UPS Tools server +description=NUT is a client - server system with support for UPS, PDU and PSU. +ports=@PORT@/tcp diff --git a/scripts/upower/95-upower-hid.rules b/scripts/upower/95-upower-hid.rules index dd9d899..c6e2d06 100644 --- a/scripts/upower/95-upower-hid.rules +++ b/scripts/upower/95-upower-hid.rules @@ -1,115 +1,2 @@ -############################################################################################################## -# Uninterruptible Power Supplies with USB HID interfaces -# -# to keep up to date, monitor: http://svn.debian.org/wsvn/nut/trunk/scripts/upower/95-upower-hid.rules - -# only support USB, else ignore -SUBSYSTEM!="usb", GOTO="up_hid_end" - -# if usbraw device, ignore -KERNEL!="hiddev*", GOTO="up_hid_end" - -# if an interface, ignore -ENV{DEVTYPE}=="usb_interface", GOTO="up_hid_end" - -ATTRS{idVendor}=="03f0", ENV{UPOWER_VENDOR}="Hewlett Packard" -ATTRS{idVendor}=="0463", ENV{UPOWER_VENDOR}="Eaton" -ATTRS{idVendor}=="047c", ENV{UPOWER_VENDOR}="Dell" -ATTRS{idVendor}=="050d", ENV{UPOWER_VENDOR}="Belkin" -ATTRS{idVendor}=="051d", ENV{UPOWER_VENDOR}="APC" -ATTRS{idVendor}=="0592", ENV{UPOWER_VENDOR}="Powerware" -ATTRS{idVendor}=="06da", ENV{UPOWER_VENDOR}="Phoenixtec" -ATTRS{idVendor}=="075d", ENV{UPOWER_VENDOR}="iDowell" -ATTRS{idVendor}=="0764", ENV{UPOWER_VENDOR}="Cyber Power Systems" -ATTRS{idVendor}=="09ae", ENV{UPOWER_VENDOR}="TrippLite" -ATTRS{idVendor}=="0d9f", ENV{UPOWER_VENDOR}="PowerCOM" -ATTRS{idVendor}=="10af", ENV{UPOWER_VENDOR}="Liebert" - -# Hewlett Packard -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1f06", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1f08", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1f09", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1f0a", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe0", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="03f0", ATTRS{idProduct}=="1fe1", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Eaton -ATTRS{idVendor}=="0463", ATTRS{idProduct}=="0001", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0463", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Dell -ATTRS{idVendor}=="047c", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Belkin -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0375", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0551", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0750", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0751", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0900", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0910", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0912", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="0980", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="050d", ATTRS{idProduct}=="1100", ENV{UPOWER_BATTERY_TYPE}="ups" - -# APC -ATTRS{idVendor}=="051d", ATTRS{idProduct}=="0002", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="051d", ATTRS{idProduct}=="0003", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Powerware -ATTRS{idVendor}=="0592", ATTRS{idProduct}=="0004", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Phoenixtec -ATTRS{idVendor}=="06da", ATTRS{idProduct}=="ffff", ENV{UPOWER_BATTERY_TYPE}="ups" - -# iDowell -ATTRS{idVendor}=="075d", ATTRS{idProduct}=="0300", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Cyber Power Systems -ATTRS{idVendor}=="0764", ATTRS{idProduct}=="0005", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0764", ATTRS{idProduct}=="0501", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0764", ATTRS{idProduct}=="0601", ENV{UPOWER_BATTERY_TYPE}="ups" - -# TrippLite -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="1003", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="1007", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="1008", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="1009", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="1010", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2005", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2007", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2008", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2009", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2010", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2011", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2012", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2013", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="2014", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3008", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3009", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3010", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3011", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3012", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3013", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3014", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="3015", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4001", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4002", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4003", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4004", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4005", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4006", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4007", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="09ae", ATTRS{idProduct}=="4008", ENV{UPOWER_BATTERY_TYPE}="ups" - -# PowerCOM -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="0004", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="00a2", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="00a3", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="00a4", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="00a5", ENV{UPOWER_BATTERY_TYPE}="ups" -ATTRS{idVendor}=="0d9f", ATTRS{idProduct}=="00a6", ENV{UPOWER_BATTERY_TYPE}="ups" - -# Liebert -ATTRS{idVendor}=="10af", ATTRS{idProduct}=="0001", ENV{UPOWER_BATTERY_TYPE}="ups" - -LABEL="up_hid_end" +# Copy some attributes from the USB device to the hiddev device +SUBSYSTEM=="usbmisc", SUBSYSTEMS=="usb", KERNEL=="hiddev*", IMPORT{parent}="UPOWER_*", IMPORT{parent}="ID_VENDOR", IMPORT{parent}="ID_PRODUCT" diff --git a/scripts/upsdrvsvcctl/Makefile.am b/scripts/upsdrvsvcctl/Makefile.am new file mode 100644 index 0000000..77313ca --- /dev/null +++ b/scripts/upsdrvsvcctl/Makefile.am @@ -0,0 +1,13 @@ +EXTRA_DIST = README + +if HAVE_SYSTEMD +EXTRA_DIST += nut-driver-enumerator.sh upsdrvsvcctl +else +if WITH_SOLARIS_SMF +EXTRA_DIST += nut-driver-enumerator.sh upsdrvsvcctl +endif +endif + +EXTRA_DIST += nut-driver-enumerator.sh.in upsdrvsvcctl.in + +MAINTAINERCLEANFILES = Makefile.in .dirstamp diff --git a/scripts/java/Makefile.in b/scripts/upsdrvsvcctl/Makefile.in similarity index 61% rename from scripts/java/Makefile.in rename to scripts/upsdrvsvcctl/Makefile.in index a10e01e..860002a 100644 --- a/scripts/java/Makefile.in +++ b/scripts/upsdrvsvcctl/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -14,9 +13,62 @@ # PARTICULAR PURPOSE. @SET_MAKE@ - -# TODO: Java / Maven build integration VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -36,45 +88,82 @@ POST_UNINSTALL = : build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ -subdir = scripts/java -DIST_COMMON = README $(srcdir)/Makefile.am $(srcdir)/Makefile.in +@HAVE_SYSTEMD_TRUE@am__append_1 = nut-driver-enumerator.sh upsdrvsvcctl +@HAVE_SYSTEMD_FALSE@@WITH_SOLARIS_SMF_TRUE@am__append_2 = nut-driver-enumerator.sh upsdrvsvcctl +subdir = scripts/upsdrvsvcctl ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h -CONFIG_CLEAN_FILES = +CONFIG_CLEAN_FILES = nut-driver-enumerator.sh upsdrvsvcctl CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = SOURCES = DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(srcdir)/nut-driver-enumerator.sh.in \ + $(srcdir)/upsdrvsvcctl.in README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -85,16 +174,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -103,11 +201,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -117,14 +212,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -135,21 +231,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -163,35 +268,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -202,8 +318,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -222,18 +342,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -243,20 +366,9 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ -EXTRA_DIST = README \ -jNut/pom.xml \ -jNut/README \ -jNut/src/main/java/org/networkupstools/jnut/Client.java \ -jNut/src/main/java/org/networkupstools/jnut/Command.java \ -jNut/src/main/java/org/networkupstools/jnut/Device.java \ -jNut/src/main/java/org/networkupstools/jnut/NutException.java \ -jNut/src/main/java/org/networkupstools/jnut/StringLineSocket.java \ -jNut/src/main/java/org/networkupstools/jnut/Variable.java \ -jNut/src/test/java/org/networkupstools/jnut/ClientTest.java \ -jNutList/pom.xml \ -jNutList/README \ -jNutList/src/main/java/org/networkupstools/jnutlist/AppList.java - +EXTRA_DIST = README $(am__append_1) $(am__append_2) \ + nut-driver-enumerator.sh.in upsdrvsvcctl.in +MAINTAINERCLEANFILES = Makefile.in .dirstamp all: all-am .SUFFIXES: @@ -269,17 +381,16 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi exit 1;; \ esac; \ done; \ - echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/java/Makefile'; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu scripts/upsdrvsvcctl/Makefile'; \ $(am__cd) $(top_srcdir) && \ - $(AUTOMAKE) --gnu scripts/java/Makefile -.PRECIOUS: Makefile + $(AUTOMAKE) --gnu scripts/upsdrvsvcctl/Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -290,20 +401,27 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): +nut-driver-enumerator.sh: $(top_builddir)/config.status $(srcdir)/nut-driver-enumerator.sh.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +upsdrvsvcctl: $(top_builddir)/config.status $(srcdir)/upsdrvsvcctl.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo clean-libtool: -rm -rf .libs _libs -tags: TAGS -TAGS: +tags TAGS: -ctags: CTAGS -CTAGS: +ctags CTAGS: + +cscope cscopelist: -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -347,10 +465,15 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -362,6 +485,7 @@ distclean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool mostlyclean-am @@ -431,15 +555,18 @@ uninstall-am: .MAKE: install-am install-strip .PHONY: all all-am check check-am clean clean-generic clean-libtool \ - distclean distclean-generic distclean-libtool distdir dvi \ - dvi-am html html-am info info-am install install-am \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ - install-info install-info-am install-man install-pdf \ - install-pdf-am install-ps install-ps-am install-strip \ - installcheck installcheck-am installdirs maintainer-clean \ - maintainer-clean-generic mostlyclean mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am uninstall uninstall-am + cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + +.PRECIOUS: Makefile # Tell versions [3.59,3.63) of GNU make to not export all variables. diff --git a/scripts/upsdrvsvcctl/README b/scripts/upsdrvsvcctl/README new file mode 100644 index 0000000..b2a9db6 --- /dev/null +++ b/scripts/upsdrvsvcctl/README @@ -0,0 +1,11 @@ +This directory contains the shared NUT support files for Linux systemd (the +System and Service Manager) and Solaris SMF (Service Management Framework). +It includes the nut-driver-enumerator.sh (service and implementation method) +and upsdrvsvcctl (tool) to manage NUT drivers as service instances. + +These files are automatically installed into SBINDIR/upsdrvsvcctl and +LIBEXECDIR/nut-driver-enumerator.sh, upon detection (at configure time) +of a systemd or SMF enabled system, with Makefiles of the ../systemd/ and +../Solaris/ source directories respectively. + +Contributed 2016-2018 by Jim Klimov diff --git a/scripts/upsdrvsvcctl/nut-driver-enumerator.sh b/scripts/upsdrvsvcctl/nut-driver-enumerator.sh new file mode 100755 index 0000000..cce714c --- /dev/null +++ b/scripts/upsdrvsvcctl/nut-driver-enumerator.sh @@ -0,0 +1,1391 @@ +#!/bin/sh +# +# NOTE: This script is intentionally written with portable shell constructs +# with the aim and hope to work in different interpreters, so it is a +# bit dumber and less efficient than could be achieved with the more +# featured shells in the spectrum. +# NOTE ALSO: The configuration parser in this script is not meant to be a +# reference or 100% compliant with what the binary code uses; its aim +# is to just pick out some strings relevant for tracking config changes. +# +# Copyright (C) 2016-2020 Eaton +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +#! \file nut-driver-enumerator.sh(.in) +# \author Jim Klimov +# \brief Enumerate NUT devices for service-unit instance configuration +# \details This script allows to enumerate UPSes in order to produce the +# individual service unit instances for each defined configuration. +# It assumes the user has adequate permissions to inspect and create +# services (e.g. is a root or has proper RBAC profiles to do so). +# It helps service frameworks such as Linux systemd and Solaris SMF. +# When executed, this script looks for all configured ups.conf +# sections and registered service instances, and makes these two +# lists match up. It has also a mode to do this in a loop, to keep +# checking for differences and applying them, on systems where it's +# problematic to trigger it in response to FS event notifications. +# Returns exit codes: +# 0 Sections and services already match up +# 42 Sections and services differed, but now match up - +# now the caller should likely restart some services. +# Note that the drivers' service instances were started or +# stopped as required (by AUTO_START=yes) - but maybe the +# upsd or upsmon services should restart. If you pass envvar +# REPORT_RESTART_42=no then this codepath would return 0. +# In default mode, such non-null reconfiguration should cause +# the nut-driver-enumerator service to restart and this would +# propagate to other NUT services that depend on it. +# 13 Sections and services differed, and still do not match up +# 1 Bad inputs, e.g. unrecognized service management framework +# 2 Absent or unreadable ups.conf file +# + +# NOTE: Currently found caveats that might be solved later but require +# considerable effort: +# * Solaris SMF constrains the syntax of valid strings for instance names +# (e.g. not starting with a digit, no period chars) which blocks creation +# of some UPS driver instances. This might be worked around by hashing +# the device name e.g. to base64 (and un-hashing instance name when calling +# upsdrvctl), but is not quite user-friendly. Also can store device name +# in a service attribute while mangling the instance name to a valid subset. +# Comparisons (if devices are already wrapped) becomes more complicated in +# the script in either case, as well as in the service startup method. +# ** The `+` `/` `=` characters from base64 are also invalid for SMF instance +# name, but the first two can be sed'ed to `-` `_` and back, for example. +# Some prefix word is also needed (avoid starting with a digit). +# The trailing padding `=` can be dropped, and added until we get a +# non-empty decode. Conversions can be done by +# `echo "$string" | openssl base64 -e|-d` +# * Dummy-UPS services that "proxy" another locally defined section are +# essentially a circular dependency for upsd. While the system might +# start-up lacking a driver, there should be some timer to re-enable +# failed not-disabled drivers (would be useful in any case though). + +# Directory where NUT configs are located, e.g. /etc/nut or /etc/ups +# Set at package configuration, compiled into daemons and drivers +prefix="/usr/local" +[ -n "${NUT_CONFPATH-}" ] || NUT_CONFPATH="${prefix}/etc" +# Technically this should be a distribution-dependent value configured +# during package build. But everyone has it the same from systemd defaults: +[ -n "${SYSTEMD_CONFPATH-}" ] || SYSTEMD_CONFPATH="/etc/systemd/system" + +if [ -n "$ZSH_VERSION" ]; then + ### Problem: loops like `for UPS in $UPSLIST` do not separate + ### the UPSLIST into many tokens but use it as one string. + echo "FATAL: zsh is not supported in this script" >&2 + exit 1 +# setopt noglob +# setopt +F +# IFS="`printf ' \t\r\n'`" ; export IFS +fi + +if set | egrep '^(shell|version|t?csh)' | egrep 't?csh' >/dev/null ; then + echo "FATAL: csh or tcsh is not supported in this script" >&2 + exit 1 +fi + +# Third-party services to depend on (can be overridden by config file) +### Note that for systemd+udev integration, it may be better to set up +### triggers in udev, see e.g. +### http://stackoverflow.com/questions/18463755/linux-start-daemon-on-connected-usb-serial-dongle +### Also can tune whether a driver "Wants" another service (would consider +### ordering if that one is enabled, but live if it is disabled), or if it +### "Requires" that (would cause that to start). +DEPSVC_USB_SYSTEMD="systemd-udev.service systemd-udev-settle.service" +DEPREQ_USB_SYSTEMD="Wants" +DEPSVC_NET_FULL_SYSTEMD="network-online.target systemd-resolved.service ifplugd.service" +DEPREQ_NET_FULL_SYSTEMD="Wants" +DEPSVC_NET_LOCAL_SYSTEMD="network.target" +DEPREQ_NET_LOCAL_SYSTEMD="Wants" +SVCNAME_SYSTEMD="nut-driver" + +# Some or all of these FMRIs may be related to dynamically changing hardware +# require_all) ;; # All cited services are running (online or degraded) +# require_any) ;; # At least one of the cited services is running +# optional_all) ;; # (All) cited services are running or would not run +# # without administrative action (disabled, maintenance, +# # not present, or are waiting for dependencies which do +# # not start without administrative action). +DEPSVC_USB_SMF="svc:/system/hotplug:default svc:/system/dbus:default svc:/system/hal:default svc:/milestone/devices:default" +DEPREQ_USB_SMF="optional_all" +# By default there are several physical network FMRIs shipped and at most +# only one is enabled on a particular system (e.g. :default or :nwam) +DEPSVC_NET_FULL_SMF="svc:/network/physical svc:/milestone/name-services" +DEPREQ_NET_FULL_SMF="optional_all" +DEPSVC_NET_LOCAL_SMF="svc:/network/loopback:default" +DEPREQ_NET_LOCAL_SMF="optional_all" +SVCNAME_SMF="svc:/system/power/nut-driver" + +[ -z "${NUT_DRIVER_ENUMERATOR_CONF-}" ] && \ + NUT_DRIVER_ENUMERATOR_CONF="${NUT_CONFPATH}/nut-driver-enumerator.conf" + +[ -s "${NUT_DRIVER_ENUMERATOR_CONF}" ] && \ + echo "Sourcing config file: ${NUT_DRIVER_ENUMERATOR_CONF}" && \ + . "${NUT_DRIVER_ENUMERATOR_CONF}" + +[ -z "${UPSCONF-}" ] && \ + UPSCONF="${NUT_CONFPATH}/ups.conf" + +# Start a freshly-registered unit? +[ -z "${AUTO_START-}" ] && AUTO_START=yes + +# We avoid regex '\t' which gets misinterpreted by some tools +TABCHAR="`printf '\t'`" || TABCHAR=' ' + +if [ -z "${SERVICE_FRAMEWORK-}" ] ; then + [ -x /usr/sbin/svcadm ] && [ -x /usr/sbin/svccfg ] && [ -x /usr/bin/svcs ] && [ -x /usr/bin/svcprop ] && \ + SERVICE_FRAMEWORK="smf" + [ -z "${SERVICE_FRAMEWORK-}" ] && \ + [ -x /bin/systemctl ] && \ + SERVICE_FRAMEWORK="systemd" +fi + +# Optionally use Coreutils timeout to limit the +# (potentially hanging) calls to systemd tools... +# Should not hurt with SMF too, if it ever misbehaves. +if [ -z "${TIMEOUT_CMD+x}" ]; then + # Envvar not set at all (set but empty is okay, caller wants that then) + TIMEOUT_CMD="" + TIMEOUT_ARGS="" + if which timeout 2>/dev/null >/dev/null ; then + # Systemd default timeout for unit start/stop + TIMEOUT_CMD="timeout" + TIMEOUT_ARGS="90s" + fi +fi + +# Cache needed bits of ups.conf to speed up later parsing. Note that these +# data are needed for most operations, and populated by upslist_readFile() +UPSCONF_DATA="" +# Subset of normalized data above that only has sections, drivers and ports +UPSCONF_DATA_SDP="" + +# List of configured UPSes in the config-file +UPSLIST_FILE="" +# List of configured service instances for UPS drivers +UPSLIST_SVCS="" + +# Framework-specific implementations are generally hooked here: +hook_registerInstance="" +hook_unregisterInstance="" +hook_refreshSupervizor="" +hook_listInstances="" +hook_listInstances_raw="" +hook_validInstanceName="" +hook_validFullUnitName="" +hook_validInstanceSuffixName="" +hook_getSavedMD5="" +hook_setSavedMD5="" +hook_restart_upsd="" +hook_restart_drv="" + +case "${SERVICE_FRAMEWORK-}" in + smf) + hook_registerInstance="smf_registerInstance" + hook_unregisterInstance="smf_unregisterInstance" + hook_refreshSupervizor="smf_refreshSupervizor" + hook_listInstances="smf_listInstances" + hook_listInstances_raw="smf_listInstances_raw" + hook_validInstanceName="smf_validInstanceName" + hook_validFullUnitName="smf_validFullUnitName" + hook_validInstanceSuffixName="smf_validInstanceSuffixName" + hook_getSavedMD5="smf_getSavedMD5" + hook_setSavedMD5="smf_setSavedMD5" + hook_restart_upsd="smf_restart_upsd" + hook_restart_drv="smf_restart_drv" + ;; + systemd) + hook_registerInstance="systemd_registerInstance" + hook_unregisterInstance="systemd_unregisterInstance" + hook_refreshSupervizor="systemd_refreshSupervizor" + hook_listInstances="systemd_listInstances" + hook_listInstances_raw="systemd_listInstances_raw" + hook_validInstanceName="systemd_validInstanceName" + hook_validFullUnitName="systemd_validFullUnitName" + hook_validInstanceSuffixName="systemd_validInstanceSuffixName" + hook_getSavedMD5="systemd_getSavedMD5" + hook_setSavedMD5="systemd_setSavedMD5" + hook_restart_upsd="systemd_restart_upsd" + hook_restart_drv="systemd_restart_drv" + ;; + selftest) + hook_registerInstance="selftest_NOOP" + hook_unregisterInstance="selftest_NOOP" + hook_refreshSupervizor="selftest_NOOP" + hook_listInstances="selftest_NOOP" + hook_listInstances_raw="selftest_NOOP" + hook_validInstanceName="selftest_NOOP" + hook_validFullUnitName="selftest_NOOP" + hook_validInstanceSuffixName="selftest_NOOP" + hook_getSavedMD5="selftest_NOOP" + hook_setSavedMD5="selftest_NOOP" + hook_restart_upsd="selftest_NOOP" + hook_restart_drv="selftest_NOOP" + ;; + "") + echo "Error detecting the service-management framework on this OS" >&2 + exit 1 + ;; + *) + echo "Error: User provided an unknown service-management framework '$SERVICE_FRAMEWORK'" >&2 + exit 1 + ;; +esac + +selftest_NOOP() { + echo "NO-OP: Self-testing context does not do systems configuration" >&2 + return 0 +} + +common_isFiled() { + [ -n "$UPSLIST_FILE" ] && \ + for UPSF in $UPSLIST_FILE ; do + [ "$1" = "$UPSF" ] && return 0 + [ "`$hook_validInstanceName "$UPSF"`" = "$1" ] && return 0 + done + return 1 +} + +common_isRegistered() { + [ -n "$UPSLIST_SVCS" ] && \ + for UPSS in $UPSLIST_SVCS ; do + [ "$1" = "$UPSS" ] && return 0 + [ "`$hook_validInstanceName "$1"`" = "$UPSS" ] && return 0 + done + return 1 +} + +upslist_equals() { + # Compare pre-sorted list of DEVICES ($1) and SVCINSTs ($2) including + # the possible mangling for service names. Return 0 if lists describe + # exactly same set of devices and their services. + # Note: This logic only checks the names, not the contents of device + # sections, so re-definitions of an existing device configuration + # would not trigger a service restart by itself. Such deeper check + # belongs in a different routine, see upssvcconf_checksum_unchanged(). + + # Trivial case 0: one string is empty, another is not + # Note: `echo '' | wc -l` == "1" not "0"! + [ -n "$1" -a -z "$2" ] && return 1 + [ -z "$1" -a -n "$2" ] && return 1 + + # Trivial case 1: equal strings + [ "$1" = "$2" ] && return 0 + # Trivial case 2: different amount of entries + [ "`echo "$1" | wc -l`" = "`echo "$2" | wc -l`" ] || return $? + + _TMP_DEV_SVC="" + for _DEV in $1 ; do + DEVINST="`$hook_validInstanceName "$_DEV"`" + for _SVC in $2 ; do + [ "$_DEV" = "$_SVC" ] \ + || [ "$DEVINST" = "$_SVC" ] \ + && { [ -z "$_TMP_DEV_SVC" ] \ + && _TMP_DEV_SVC="$_DEV = $_SVC" \ + || _TMP_DEV_SVC="$_TMP_DEV_SVC +$_DEV = $_SVC" ; } + done + done + + # Input was not empty; did anything in output fit? + [ -z "$_TMP_DEV_SVC" ] && return 1 + + # Exit code : is the built mapping as long as the source list(s)? + [ "`echo "$1" | wc -l`" = "`echo "$_TMP_DEV_SVC" | wc -l`" ] +} + +upssvcconf_checksum_unchanged() { + # $1 = dev, $2 = svc + # compare checksums of the configuration section from the file and the + # stashed configuration in a service instance (if any). + # FIXME : optimize by caching, we likely have quite a few requests + [ "`upsconf_getSection_MD5 "$1"`" = "`$hook_getSavedMD5 "$2"`" ] +} + +upslist_checksums_unchanged() { + # For each device and its corresponding unit, compare checksums of the + # configuration section from the file and the stashed configuration in + # a service instance. Prints a list of mismatching service names that + # should get reconfigured. + [ -z "$1" -o -z "$2" ] && return 1 + + _TMP_SVC="" + for _DEV in $1 ; do + DEVINST="`$hook_validInstanceName "$_DEV"`" + for _SVC in $2 ; do + if [ "$_DEV" = "$_SVC" ] \ + || [ "$DEVINST" = "$_SVC" ] \ + ; then + upssvcconf_checksum_unchanged "$_DEV" "$_SVC" || \ + { [ -z "$_TMP_SVC" ] \ + && _TMP_SVC="$_SVC" \ + || _TMP_SVC="$_TMP_SVC +$_SVC" ; } + fi + done + done + [ -z "$_TMP_SVC" ] && return 0 + echo "$_TMP_SVC" + return 1 +} + +upsconf_getSection_content() { + # "$1" = name of ups.conf section to display in whole, from whatever + # comes on stdin (file or a pre-made normalized variable) + # empty "$1" means the global config (before any sections) + # + # NOTE (TODO?): This routine finds the one NUT device section, prints it + # and returns when the section is over. It currently does not cover (in + # a way allowing to do it efficiently) selection of several sections, + # or storing each section content in some array or dynamic variables + # (as would be better fit for portable shells) to later address them + # quickly without re-parsing the file or big envvar many times. + # + + CURR_SECTION="" + SECTION_CONTENT="" + RES=1 + [ -n "$1" ] || RES=0 + while read LINE ; do + case "$LINE" in + \["$1"\]) + if [ "$RES" = 0 ]; then + # We have already displayed a section, here is a new one, + # and this routine only displays one (TODO: toggle?) + break + fi + SECTION_CONTENT="$LINE" + CURR_SECTION="$1" + RES=0 + continue + ;; + \[*\ *\]|\[*"$TABCHAR"*\]) + # Note that section-name brackets should contain a single token + # Fall through to add the line to contents of existing section + ;; + \[*\]) + [ "$CURR_SECTION" = "$1" ] && break + # Use a value that can not be a section name here: + CURR_SECTION="[]" + continue + ;; + "") continue ;; + *) ;; # Fall through to add the line to contents of existing section + esac + if [ "$CURR_SECTION" = "$1" ]; then + if [ -n "$SECTION_CONTENT" ]; then + SECTION_CONTENT="$SECTION_CONTENT +$LINE" + else + SECTION_CONTENT="$LINE" + fi + fi + done + + if [ -n "$SECTION_CONTENT" ]; then + echo "$SECTION_CONTENT" + fi + + [ "$RES" = 0 ] || echo "ERROR: Section [$1] was not found in the '$UPSCONF' file" >&2 + return $RES +} + +upsconf_getSection() { + # Use the whole output of normalization parser + upslist_normalizeFile_once || return # Propagate errors upwards + upsconf_getSection_content "$@" << EOF +${UPSCONF_DATA} +EOF +} + +upsconf_getSection_MD5() { + calc_md5 "`upsconf_getSection "$@"`" +} + +upsconf_getSection_SDP() { + # Use the section-driver-port subset + upslist_normalizeFile_once || return # Propagate errors upwards + upsconf_getSection_content "$@" << EOF +${UPSCONF_DATA_SDP} +EOF +} + +upsconf_getValue() { + # "$1" = name of ups.conf section, may be empty for global config + # "$2..$N" = name of config key; we will echo its value +### [ -n "$1" ] || return $? + [ -n "$2" ] || return $? + [ -n "$GETSECTION" ] || GETSECTION="upsconf_getSection" + CURR_SECTION="" # Gets set by a GETSECTION implementation + RES=0 + + # Note: Primary aim of this egrep is to pick either assignments or flags + # As a by-product it can be used to test if a particular value is set ;) + SECTION_CONTENT="`$GETSECTION "$1"`" || return + shift + KEYS="$*" + + while [ "$#" -gt 0 ] ; do + RES_L=0 + VALUE="" + + LINE="`echo "$SECTION_CONTENT" | egrep '(^'"$1"'=|^'"$1"'$)'`" \ + && VALUE="$(echo "$LINE" | sed -e "s,^$1=,," -e 's,^\"\(.*\)\"$,\1,' -e "s,^'\(.*\)'$,\1,")" \ + || RES_L=$? + + [ "$RES_L" = 0 ] || { RES="$RES_L" ; echo "ERROR: Section [$CURR_SECTION] or key '$1' in it was not found in the '$UPSCONF' file" >&2 ; } + + echo "$VALUE" + shift + done + + [ "$RES" = 0 ] || echo "ERROR: Section [$CURR_SECTION] or key(s) '$KEYS' in it was not found in the '$UPSCONF' file" >&2 + return $RES +} + +upsconf_getDriver() { + # "$1" = name of ups.conf section; return (echo) the driver name used there + # In the context this function is used, UPSCONF exists and section is there + GETSECTION="upsconf_getSection_SDP" upsconf_getValue "$1" "driver" + return $? +} + +upsconf_getPort() { + # "$1" = name of ups.conf section; return (echo) the "port" name used there + # In the context this function is used, UPSCONF exists and section is there + GETSECTION="upsconf_getSection_SDP" upsconf_getValue "$1" "port" + return $? +} + +upsconf_getDriverMedia() { + # "$1" = name of ups.conf section; return (echo) name and type of driver as + # needed for dependency evaluation (what services we must depend on for this + # unit), newline-separated (drvnametype). Empty type for unclassified + # results, assuming no known special dependencies (note that depending on + # particular system's physics, both serial and network media may need USB). + CURR_DRV="`upsconf_getDriver "$1"`" || return $? + case "$CURR_DRV" in + *netxml*|*snmp*|*ipmi*|*powerman*|*-mib*|*avahi*|*apcupsd*) + printf '%s\n%s\n' "$CURR_DRV" "network" ; return ;; + *usb*) + printf '%s\n%s\n' "$CURR_DRV" "usb" ; return ;; + nutdrv_qx) # May be direct serial or USB + CURR_PORT="`upsconf_getPort "$1"`" || CURR_PORT="" + case "$CURR_PORT" in + auto|/dev/*usb*|/dev/*hid*) + printf '%s\n%s\n' "$CURR_DRV" "usb" ; return ;; + /dev/*) + # See drivers/nutdrv_qx.c :: upsdrv_initups() for a list + if [ -n "`upsconf_getValue "$1" 'subdriver' 'vendorid' 'productid' 'vendor' 'product' 'serial' 'bus' 'langid_fix'`" ] \ + ; then + printf '%s\n%s\n' "$CURR_DRV" "usb" ; return + else + printf '%s\n%s\n' "$CURR_DRV" "serial" ; return + fi + ;; + *) + printf '%s\n%s\n' "$CURR_DRV" "" ; return ;; + esac + ;; + *dummy*|*clone*) # May be networked (proxy to remote NUT) + CURR_PORT="`upsconf_getPort "$1"`" || CURR_PORT="" + case "$CURR_PORT" in + *@localhost|*@|*@127.0.0.1|*@::1) + printf '%s\n%s\n' "$CURR_DRV" "network-localhost" ; return ;; + *@*) + printf '%s\n%s\n' "$CURR_DRV" "network" ; return ;; + *) + printf '%s\n%s\n' "$CURR_DRV" "" ; return ;; + esac + ;; + *) printf '%s\n%s\n' "$CURR_DRV" "" ; return ;; + esac +} + +upsconf_getMedia() { + _DRVMED="`upsconf_getDriverMedia "$1"`" || return + echo "$_DRVMED" | tail -n +2 + return 0 +} + +upsconf_debug() { + _DRV="`upsconf_getDriver "$1"`" + _PRT="`upsconf_getPort "$1"`" + _MED="`upsconf_getMedia "$1"`" + _MD5="`upsconf_getSection_MD5 "$1"`" + NAME_MD5="`calc_md5 "$1"`" + echo "INST: ${NAME_MD5}~[$1]: DRV='$_DRV' PORT='$_PRT' MEDIA='$_MED' SECTIONMD5='$_MD5'" +} + +calc_md5() { + # Tries several ways to produce an MD5 of the "$1" argument + _MD5="`echo "$1" | md5sum 2>/dev/null | awk '{print $1}'`" && [ -n "$_MD5" ] || \ + { _MD5="`echo "$1" | openssl dgst -md5 2>/dev/null | awk '{print $NF}'`" && [ -n "$_MD5" ]; } || \ + return 1 + + echo "$_MD5" +} + +calc_md5_file() { + # Tries several ways to produce an MD5 of the file named by "$1" argument + [ -s "$1" ] || return 2 + + _MD5="`md5sum 2>/dev/null < "$1" | awk '{print $1}'`" && [ -n "$_MD5" ] || \ + { _MD5="`openssl dgst -md5 2>/dev/null < "$1" | awk '{print $NF}'`" && [ -n "$_MD5" ]; } || \ + return 1 + + echo "$_MD5" +} + +smf_validFullUnitName() { + case "$1" in + *:*) echo "$1" ;; + *) echo "$SVCNAME_SMF:$1" ;; + esac +} +smf_validInstanceName() { + echo "MD5_`calc_md5 "$1"`" +} +smf_validInstanceSuffixName() { + case "$1" in + *:*) echo "$1" | sed 's,^.*:\([^:]*\)$,\1,' ;; + *) echo "$1" ;; + esac +} +smf_registerInstance() { + DEVICE="$1" + SVCINST="$1" + /usr/sbin/svccfg -s nut-driver add "$DEVICE" || \ + { SVCINST="`smf_validInstanceName "$1"`" && \ + /usr/sbin/svccfg -s nut-driver add "$SVCINST" || return ; } + echo "Added instance: 'nut-driver:$SVCINST' for NUT configuration section '$DEVICE'" >&2 + + DEPSVC="" + DEPREQ="" + _MED="`upsconf_getMedia "$DEVICE"`" + case "$_MED" in + usb) + DEPSVC="$DEPSVC_USB_SMF" + DEPREQ="$DEPREQ_USB_SMF" ;; + network-localhost) + DEPSVC="$DEPSVC_NET_LOCAL_SMF" + DEPREQ="$DEPREQ_NET_LOCAL_SMF" ;; + network) + DEPSVC="$DEPSVC_NET_FULL_SMF" + DEPREQ="$DEPREQ_NET_FULL_SMF" ;; + serial) ;; + '') ;; + *) echo "WARNING: Unexpected NUT media type ignored: '$_MED'" >&2 ;; + esac + + TARGET_FMRI="nut-driver:$SVCINST" + if [ -n "$DEPSVC" ]; then + [ -n "$DEPREQ" ] || DEPREQ="optional_all" + echo "Adding '$DEPREQ' dependency for '$SVCINST' on '$DEPSVC'..." + + DEPPG="nut-driver-enumerator-generated" + RESTARTON="refresh" + /usr/sbin/svccfg -s "$TARGET_FMRI" addpg "$DEPPG" dependency && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$DEPPG"/grouping = astring: "$DEPREQ" && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$DEPPG"/restart_on = astring: "$RESTARTON" && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$DEPPG"/type = astring: service && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$DEPPG"/entities = fmri: "($DEPSVC)" && \ + echo "OK" || echo "FAILED to define the dependency" >&2 + fi + + smf_setSavedMD5 "$SVCINST" "`upsconf_getSection_MD5 "$DEVICE"`" + + /usr/sbin/svcadm refresh "${TARGET_FMRI}" || return + if [ "$AUTO_START" = yes ] ; then + /usr/sbin/svcadm clear "${TARGET_FMRI}" 2>/dev/null || true + /usr/sbin/svcadm enable "${TARGET_FMRI}" || return + echo "Started instance: 'nut-driver:$SVCINST' for NUT configuration section '$DEVICE'" >&2 + fi +} +smf_unregisterInstance() { + echo "Removing instance: 'nut-driver:$1' ..." >&2 + /usr/sbin/svcadm disable -ts 'nut-driver:'"$1" || false + /usr/sbin/svccfg -s nut-driver delete "$1" +} +smf_refreshSupervizor() { + : +} +smf_listInstances_raw() { + # Newer versions have pattern matching; older SMF might not have this luxury + /usr/bin/svcs -a -H -o fmri | egrep '/nut-driver:' +} +smf_listInstances() { + smf_listInstances_raw | sed 's/^.*://' | sort -n +} +smf_getSavedMD5() { + # Query service instance $1 + PG="nut-driver-enumerator-generated-checksum" + PROP="SECTION_CHECKSUM" + + if [ -n "$1" ]; then + TARGET_FMRI="nut-driver:$1" + else + # Global section + TARGET_FMRI="nut-driver" + PROP="SECTION_CHECKSUM_GLOBAL" + fi + + # Note: lookups for GLOBAL cause each service instance to show up + /usr/bin/svcprop -p "$PG/$PROP" "$TARGET_FMRI" | head -1 | awk '{print $NF}' +} +smf_setSavedMD5() { + # Save checksum value $2 into service instance $1 + PG="nut-driver-enumerator-generated-checksum" + PROP="SECTION_CHECKSUM" + + if [ -n "$1" ]; then + TARGET_FMRI="nut-driver:$1" + else + # Global section + TARGET_FMRI="nut-driver" + PROP="SECTION_CHECKSUM_GLOBAL" + fi + + /usr/sbin/svccfg -s "$TARGET_FMRI" delprop "$PG" || true + /usr/sbin/svccfg -s "$TARGET_FMRI" addpg "$PG" application && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$PG/$PROP" = astring: "$2" + [ $? = 0 ] && echo "OK" || { echo "FAILED to stash the checksum">&2 ; return 1 ; } + /usr/sbin/svcadm refresh "${TARGET_FMRI}" || return +} +smf_restart_upsd() { + echo "Restarting NUT data server to make sure it knows new configuration..." + /usr/sbin/svcadm enable "nut-server" 2>/dev/null + /usr/sbin/svcadm clear "nut-server" 2>/dev/null + /usr/sbin/svcadm refresh "nut-server" || \ + /usr/sbin/svcadm restart "nut-server" +} +smf_restart_drv() { + echo "Restarting NUT driver instance '$1' to make sure it knows new configuration..." + /usr/sbin/svcadm enable "nut-driver:$1" 2>/dev/null + /usr/sbin/svcadm clear "nut-driver:$1" 2>/dev/null + /usr/sbin/svcadm refresh "nut-driver:$1" || \ + /usr/sbin/svcadm restart "nut-driver:$1" +} + +systemd_validFullUnitName() { + case "$1" in + *@*.*) echo "$1" ;; + *@*) echo "$1.service" ;; + *) echo "$SVCNAME_SYSTEMD@$1.service" ;; + esac +} +systemd_validInstanceName() { + echo "MD5_`calc_md5 "$1"`" +} +systemd_validInstanceSuffixName() { + echo "$1" | sed -e 's,^.*@,,' -e 's,\.service$,,' +} +systemd_registerInstance() { + # Instance is registered by device section name; ultimate name in systemd may differ + DEVICE="$1" + SVCINST="$1" + /bin/systemctl enable 'nut-driver@'"$DEVICE".service || \ + { SVCINST="`systemd_validInstanceName "$1"`" && \ + /bin/systemctl enable 'nut-driver@'"$SVCINST".service || return ; } + echo "Enabled instance: 'nut-driver@$SVCINST' for NUT configuration section '$DEVICE'" >&2 + + DEPSVC="" + DEPREQ="" + _MED="`upsconf_getMedia "$DEVICE"`" + case "$_MED" in + usb) + DEPSVC="$DEPSVC_USB_SYSTEMD" + DEPREQ="$DEPREQ_USB_SYSTEMD" ;; + network-localhost) + DEPSVC="$DEPSVC_NET_LOCAL_SYSTEMD" + DEPREQ="$DEPREQ_NET_LOCAL_SYSTEMD" ;; + network) + DEPSVC="$DEPSVC_NET_FULL_SYSTEMD" + DEPREQ="$DEPREQ_NET_FULL_SYSTEMD" ;; + serial) ;; + '') ;; + *) echo "WARNING: Unexpected NUT media type ignored: '$_MED'" >&2 ;; + esac + if [ -n "$DEPSVC" ]; then + [ -n "$DEPREQ" ] || DEPREQ="#Wants" + echo "Adding '$DEPREQ'+After dependency for '$SVCINST' on '$DEPSVC'..." + mkdir -p "${SYSTEMD_CONFPATH}/nut-driver@$SVCINST.service.d" && \ + cat > "${SYSTEMD_CONFPATH}/nut-driver@$SVCINST.service.d/nut-driver-enumerator-generated.conf" <&2 + fi + + systemd_setSavedMD5 "$SVCINST" "`upsconf_getSection_MD5 "$DEVICE"`" + + if [ "$AUTO_START" = yes ] ; then + systemd_refreshSupervizor || echo "WARNING: Somehow managed to fail systemd_refreshSupervizor()" >&2 + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl start --no-block 'nut-driver@'"$SVCINST".service || return + echo "Started instance: 'nut-driver@$SVCINST' for NUT configuration section '$DEVICE'" >&2 + fi +} +systemd_unregisterInstance() { + echo "Removing instance: 'nut-driver@$1' ..." >&2 + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl stop 'nut-driver@'"$1".service || \ + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl stop 'nut-driver@'"$1".service || \ + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl stop 'nut-driver@'"$1".service || false + + /bin/systemctl disable 'nut-driver@'"$1".service + rm -rf "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d" + /bin/systemctl reset-failed 'nut-driver@'"$1".service +} +systemd_refreshSupervizor() { + /bin/systemctl daemon-reload +} +systemd_listInstances_raw() { + /bin/systemctl show 'nut-driver@*' -p Id | egrep '=nut-driver' | sed 's,^Id=,,' +} +systemd_listInstances() { + systemd_listInstances_raw | sed -e 's/^.*@//' -e 's/\.service$//' | sort -n +} +systemd_getSavedMD5() { + # Query service instance $1 or global section + PROP="SECTION_CHECKSUM" + [ -n "$1" ] || PROP="SECTION_CHECKSUM_GLOBAL" + [ -s "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" ] \ + && grep "Environment='$PROP=" "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" | sed -e "s,^Environment='$PROP=,," -e "s,'\$,," \ + || { echo "Did not find '${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf' with a $PROP" ; return 1; } +} +systemd_setSavedMD5() { + # Save checksum value $2 into service instance $1 + PROP="SECTION_CHECKSUM" + [ -n "$1" ] || PROP="SECTION_CHECKSUM_GLOBAL" + mkdir -p "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d" && \ + cat > "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" << EOF +[Service] +Environment='$PROP=$2' +EOF + [ $? = 0 ] && echo "OK" || { echo "FAILED to stash the checksum">&2 ; return 1 ; } +} +systemd_restart_upsd() { + # Do not restart/reload if not already running + case "`/bin/systemctl is-active "nut-server"`" in + active|unknown) ;; # unknown meant "starting" in our testing... + failed) echo "Note: nut-server unit was 'failed' - not disabled by user, so (re)starting it (probably had no config initially)" >&2 ;; + *) return 0 ;; + esac + + echo "Restarting NUT data server to make sure it knows new configuration..." + # Note: reload is a better way to go about this, so the + # data service is not interrupted by re-initialization + # of the daemon. But systemd/systemctl sometimes stalls... + + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl reload-or-restart "nut-server" || \ + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl restart "nut-server" +} + +systemd_restart_drv() { + # Do not restart/reload if not already running + case "`/bin/systemctl is-active "nut-driver@$1"`" in + active|unknown) ;; + *) return 0 ;; + esac + + echo "Restarting NUT driver instance '$1' to make sure it knows new configuration..." + + # Full restart, e.g. in case we changed the user= in configs + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl restart "nut-driver@$1" +} + +upslist_normalizeFile_filter() { + # See upslist_normalizeFile() detailed comments below; this routine + # is a pipe worker to prepare the text into a simpler expected form. + + # Pick the lines which contain a bracket or assignment character, + # or a single token (certain keywords come as just NUT "flags"), + # trim leading and trailing whitespace, comment-only lines, and in + # assignment lines trim the spaces around equality character and + # quoting characters around assignment of values without whitespaces. + # Any whitespace characters around a section name (single token that + # starts the line and is enclosed in brackets) and a trailing comment + # are dropped. Note that brackets with spaces inside, and brackets + # that do not start the non-whitespace payload of the line, are not + # sections. + egrep -v '(^$|^#)' | \ + sed -e 's,^['"$TABCHAR"'\ ]*,,' \ + -e 's,^\#.*$,,' \ + -e 's,['"$TABCHAR"'\ ]*$,,' \ + -e 's,^\([^=\ '"$TABCHAR"']*\)['"$TABCHAR"'\ ]*=['"$TABCHAR"'\ ]*,\1=,g' \ + -e 's,=\"\([^\ '"$TABCHAR"']*\)\"$,=\1,' \ + -e 's,^\(\[[^]'"$TABCHAR"'\ ]*\]\)['"$TABCHAR"'\ ]*\(#.*\)*$,\1,' \ + | egrep -v '^$' \ + | egrep '([\[\=]|^[^ '"$TABCHAR"']*$|^[^ '"$TABCHAR"']*[ '"$TABCHAR"']*\#.*$)' +} + +upslist_normalizeFile() { + # Read the ups.conf file and find all defined sections (names of + # configuration blocks for drivers that connect to a certain device + # using specified protocol and media); normalize section contents + # as detailed below, to simplify subsequent parsing and comparison. + + # File contents + UPSCONF_DATA="" + UPSCONF_DATA_SDP="" + if [ -n "$UPSCONF" ] && [ -f "$UPSCONF" ] && [ -r "$UPSCONF" ]; then + [ ! -s "$UPSCONF" ] \ + && echo "WARNING: The '$UPSCONF' file exists but is empty" >&2 \ + && return 0 + # Ok to continue - we may end up removing all instances + else + echo "FATAL: The '$UPSCONF' file does not exist or is not readable" >&2 + return 2 + fi + + # Store a normalized version of NUT configuration file contents. + # Also use a SDP subset with just section, driver and port info + # for faster parsing when determining driver-required media etc. + UPSCONF_DATA="$(upslist_normalizeFile_filter < "$UPSCONF")" \ + && [ -n "$UPSCONF_DATA" ] \ + && UPSCONF_DATA_SDP="`egrep '^(\[.*\]|driver=|port=)' << EOF +$UPSCONF_DATA +EOF`" \ + || { echo "Error reading the '$UPSCONF' file or it does not declare any device configurations: nothing left after normalization" >&2 + UPSCONF_DATA="" + UPSCONF_DATA_SDP="" + } +} + +upslist_normalizeFile_once() { + # Wrapper that ensures that the parsing is only done once + # (will re-parse if there were no devices listed on the + # first time, though) + [ -z "$UPSCONF_DATA" ] && [ -z "$UPSCONF_DATA_SDP" ] || return 0 + upslist_normalizeFile +} + +upslist_readFile() { + # Use the routine above (unconditionally) to get or update the + # listing of device sections known at this moment. + + # List of devices from the config file + UPSLIST_FILE="" + if [ "$DO_NORMALIZE_ONCE" = yes ]; then + upslist_normalizeFile_once || return # Propagate errors upwards + else + upslist_normalizeFile || return # Propagate errors upwards + fi + + if [ -n "$UPSCONF_DATA" ] ; then + # Note that section-name brackets should contain a single token + UPSLIST_FILE="$(echo "$UPSCONF_DATA_SDP" | egrep '^\[[^'"$TABCHAR"'\ ]*\]$' | sed 's,^\[\(.*\)\]$,\1,' | sort -n)" \ + || UPSLIST_FILE="" + if [ -z "$UPSLIST_FILE" ] ; then + echo "Error reading the '$UPSCONF' file or it does not declare any device configurations: no section declarations in parsed normalized contents" >&2 + fi + fi + # Ok to continue with empty results - we may end up removing all instances +} + +upslist_readFile_once() { + # Wrapper that ensures that the parsing is only done once + # (will re-parse if there were no devices listed on the + # first time, though) + [ -z "$UPSLIST_FILE" ] || return 0 + DO_NORMALIZE_ONCE=yes upslist_readFile +} + +upslist_readSvcs() { + UPSLIST_SVCS="`$hook_listInstances`" || UPSLIST_SVCS="" + if [ -z "$UPSLIST_SVCS" ] && [ "$1" != "-" ] ; then + EXPLAIN="" + [ -z "$1" ] || EXPLAIN=" - $1" + echo "Error reading the list of ${SERVICE_FRAMEWORK-} service instances for UPS drivers, or none are defined${EXPLAIN}" >&2 + # Ok to continue - we may end up defining new instances + fi +} + +upslist_debug() { + for UPSF in "" $UPSLIST_FILE ; do + upsconf_debug "$UPSF" + done +} + +upslist_addSvcs() { + # Note: This routine registers service instances for device config sections + # that are not wrapped currently. Support for redefined previously existing + # sections - is attained by removing the old service instance elsewhere and + # recreating it here, since any data could change including the dependency + # list, etc. + for UPSF in $UPSLIST_FILE ; do + if ! common_isRegistered "$UPSF" ; then + echo "Adding new ${SERVICE_FRAMEWORK} service instance for power device [${UPSF}]..." >&2 + $hook_registerInstance "$UPSF" + fi + done +} + +upslist_delSvcs() { + for UPSS in $UPSLIST_SVCS ; do + if ! common_isFiled "$UPSS" ; then + echo "Dropping old ${SERVICE_FRAMEWORK} service instance for power device [${UPSS}] which is no longer in config file..." >&2 + $hook_unregisterInstance "$UPSS" + fi + done +} + +upslist_restartSvcs() { + for UPSS in $UPSLIST_SVCS ; do + if common_isFiled "$UPSS" ; then + $hook_restart_drv "$UPSS" + fi + done +} + +nut_driver_enumerator_main() { + ################# MAIN PROGRAM by default + + # Note: do not use the read..._once() here, to ensure that the + # looped daemon sees the whole picture, which can be new every time + upslist_readFile || return $? + #upslist_debug + upslist_readSvcs "before manipulations" + + # Test if global config has changed since last run + RESTART_ALL=no + upssvcconf_checksum_unchanged "" || { echo "`date -u` : Detected changes in global section of '$UPSCONF', will restart all drivers"; RESTART_ALL=yes; } + + # Quickly exit if there's nothing to do; note the lists are pre-sorted + # Otherwise a non-zero exit will be done below + # Note: We implement testing in detail whether section definitions were + # changed since last run, as a first step before checking that name + # lists are still equivalent, because we need to always have the result + # of the "has it changed?" check as a hit-list of something to remove, + # while the check for no new device section definitions is just boolean. + # We can only exit quickly if both there are no changed sections and no + # new or removed sections since last run. + NEW_CHECKSUM="`upslist_checksums_unchanged "$UPSLIST_FILE" "$UPSLIST_SVCS"`" \ + && [ -z "$NEW_CHECKSUM" ] \ + && upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" \ + && if [ -z "$DAEMON_SLEEP" -o "${VERBOSE_LOOP}" = yes ] ; then \ + echo "`date -u` : OK: No changes to reconcile between ${SERVICE_FRAMEWORK} service instances and device configurations in '$UPSCONF'" ; \ + fi \ + && [ "$RESTART_ALL" = no ] && return 0 + + if [ -n "$NEW_CHECKSUM" ]; then + for UPSS in $NEW_CHECKSUM ; do + echo "Dropping old ${SERVICE_FRAMEWORK} service instance ${UPSS} whose section in config file has changed..." >&2 + $hook_unregisterInstance "$UPSS" + done + upslist_readSvcs "after updating for new config section checksums" + fi + + if [ -n "$UPSLIST_SVCS" ]; then + # Drop services that are not in config file (any more?) + upslist_delSvcs + + if [ "$RESTART_ALL" = yes ] && [ "$AUTO_START" = yes ] ; then + # Here restart only existing services; new ones will (try to) + # start soon after creation and upsd is handled below + upslist_restartSvcs + fi + fi + + if [ "$RESTART_ALL" = yes ] ; then + # Save new checksum of global config + $hook_setSavedMD5 "" "`upsconf_getSection_MD5 ""`" + fi + + if [ -n "$UPSLIST_FILE" ]; then + # Add services for sections that are in config file but not yet wrapped + upslist_addSvcs + $hook_refreshSupervizor + upslist_readSvcs "after checking for new config sections to define service instances" + fi + + upslist_readSvcs + if [ -n "$UPSLIST_SVCS" ] ; then + echo "=== The currently defined service instances are:" + echo "$UPSLIST_SVCS" + fi + + if [ -n "$UPSLIST_FILE" ] ; then + echo "=== The currently defined configurations in '$UPSCONF' are:" + echo "$UPSLIST_FILE" + fi + + # We had some changes to the config file; upsd must be made aware + if [ "$AUTO_START" = yes ] ; then + $hook_restart_upsd + fi + + # Return 42 if there was a change applied succesfully + # (but e.g. some services should restart - upsd, maybe upsmon) + UPSLIST_EQ_RES=0 + upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" || UPSLIST_EQ_RES=$? + + # File processing and service startups take a while; + # make sure upsconf did not change while we worked... + # NOTE: Check this at the last moment to minimize + # the chance of still not noticing the change made + # at just the wrong moment. + UPSCONF_CHECKSUM_END="`calc_md5_file "$UPSCONF"`" || true + if [ "$UPSCONF_CHECKSUM_END" != "$UPSCONF_CHECKSUM_START" ] ; then + # NOTE: even if daemonized, the sleep between iterations + # can be configured into an uncomfortably long lag, so + # we should re-sync the system config in any case. + echo "`date -u` : '$UPSCONF' changed while $0 $* was processing its older contents; re-running the script to pick up the late-coming changes" + # Make sure the cycle does not repeat itself due to diffs + # from an ages-old state of the file from when we started. + UPSCONF_CHECKSUM_START="$UPSCONF_CHECKSUM_END" + ( nut_driver_enumerator_main ) ; return $? + # The "main" routine at the end of recursions will + # do REPORT_RESTART_42 logic or the error exit-code + fi + + if [ "$UPSLIST_EQ_RES" = 0 ] ; then + echo "`date -u` : OK: No more changes to reconcile between ${SERVICE_FRAMEWORK} service instances and device configurations in '$UPSCONF'" + [ "${REPORT_RESTART_42-}" = no ] && return 0 || return 42 + fi + return 13 +} + +daemonize() ( + # Support (SIG)HUP == signal code 1 to quickly reconfigure, + # e.g. to request it while the sleep is happening or while + # "main" is processing an earlier change of the file. + RECONFIGURE_ASAP=false + trap 'RECONFIGURE_ASAP=true' 1 + + # Note: this loop would die on errors with config file or + # inability to ensure that it matches the list of services. + # If caller did not `export REPORT_RESTART_42=no` then the + # loop would exit with code 42, and probably trigger restart + # of the service which wraps this daemon do topple others that + # depend on it. + # Note: do not quickly skip the "main" based on full-file + # checksum refresh, to ensure that whatever is configured + # gets applied (e.g. if user disabled some services or they + # died, or some config was not applied due to coding error). + while nut_driver_enumerator_main ; do + if $RECONFIGURE_ASAP ; then + echo "`date -u` : Trapped a SIGHUP during last run of nut_driver_enumerator_main, repeating reconfiguration quickly" >&2 + else + sleep $DAEMON_SLEEP & + trap "kill $! ; echo 'Sleep interrupted, processing configs now!'>&2" 1 + wait $! + fi + RECONFIGURE_ASAP=false + trap 'RECONFIGURE_ASAP=true' 1 + done + exit $? +) + +# Save the checksum of ups.conf as early as possible, +# to test in the end that it is still the same file. +UPSCONF_CHECKSUM_START="`calc_md5_file "$UPSCONF"`" || true + +# By default, update wrapping of devices into services +if [ $# = 0 ]; then + nut_driver_enumerator_main ; exit $? +fi + +if [ $# = 1 ] ; then + [ -n "$DAEMON_SLEEP" ] || DAEMON_SLEEP=60 + # Note: Not all shells have 'case ... ;&' support + case "$1" in + --daemon=*) DAEMON_SLEEP="`echo "$1" | sed 's,^--daemon=,,'`" ;; + esac + case "$1" in + --daemon|--daemon=*) + daemonize & + exit $? + ;; + esac +fi +unset DAEMON_SLEEP + +usage() { + cat << EOF +$0 (no args) + Update wrapping of devices into services +$0 --daemon(=freq) + Update wrapping of devices into services in an infinite loop + Default freq is 60 sec +$0 --reconfigure + Stop and un-register all service instances and recreate them + (e.g. if new dependency template was defined in a new + version of this script and/or NUT package) +$0 --get-service-framework + Print the detected service + management framework in this OS +$0 --list-devices + Print list of devices in NUT config +$0 --list-services + Print list of service instances which wrap registered + NUT devices (full name of service unit) +$0 --list-instances + Print list of service instances which wrap registered + NUT devices (just instance suffix) +$0 --get-service-for-device DEV + Print the full name of service unit which wraps a NUT + device named "DEV" +$0 --get-device-for-service SVC + Print the NUT device name for full or instance-suffix name of + a service unit which wraps it +$0 --list-services-for-devices + Print a TAB-separated list of service units and corresponding + NUT device names which each such unit wraps +$0 --show-configs|--show-all-configs + Show the complete normalized list of device configuration blocks +$0 --show-config DEV +$0 --show-device-config DEV + Show configuration block of the specified NUT device +$0 --show-device-config-value DEV KEY [KEY...] + Show single configuration key value of the specified NUT device + For flags, shows the flag name if present in the section + If several keys or flags are requested, their values are reported + one per line in the same order (including empty lines for missing + values); any missing value yields a non-zero exit code. +EOF +} + +while [ $# -gt 0 ]; do + case "$1" in + --help|-h|-help) usage; exit 0 ;; + --get-service-framework) echo "${SERVICE_FRAMEWORK}" ; exit 0 ;; + --reconfigure) + upslist_readFile_once || exit $? + upslist_readSvcs "before manipulations" + + if [ -n "$UPSLIST_SVCS" ]; then + for UPSS in $UPSLIST_SVCS ; do + echo "Dropping old ${SERVICE_FRAMEWORK} service instance for power device [${UPSS}] to reconfigure the service unit..." >&2 + $hook_unregisterInstance "$UPSS" + done + upslist_readSvcs "after dropping" + fi + + if [ -n "$UPSLIST_FILE" ]; then + upslist_addSvcs + upslist_readSvcs "after checking for new config sections to define service instances" + fi + + # Save new checksum of global config + $hook_setSavedMD5 "" "`upsconf_getSection_MD5 ""`" + + # Service units were manipulated, including saving of checksums; + # refresh the service management daemon if needed + $hook_refreshSupervizor + + if [ -n "$UPSLIST_SVCS" ] ; then + echo "=== The currently defined service instances are:" + echo "$UPSLIST_SVCS" + fi + + if [ -n "$UPSLIST_FILE" ] ; then + echo "=== The currently defined configurations in '$UPSCONF' are:" + echo "$UPSLIST_FILE" + fi + + # We had some changes to the config file; upsd must be made aware + if [ "$AUTO_START" = yes ] ; then + $hook_restart_upsd + fi + + # Return 42 if there was a change applied succesfully + # (but e.g. some services should restart - upsd, maybe upsmon) + UPSLIST_EQ_RES=0 + upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" || UPSLIST_EQ_RES=$? + + # File processing and service startups take a while; + # make sure upsconf did not change while we worked... + # NOTE: Check this at the last moment to minimize + # the chance of still not noticing the change made + # at just the wrong moment. + UPSCONF_CHECKSUM_END="`calc_md5_file "$UPSCONF"`" || true + if [ "$UPSCONF_CHECKSUM_END" != "$UPSCONF_CHECKSUM_START" ] ; then + echo "`date -u` : '$UPSCONF' changed while $0 $* was processing its older contents; re-running the script to pick up the late-coming changes" + $0 ; exit $? + # The "main" routine will do REPORT_RESTART_42 logic too + fi + + if [ "$UPSLIST_EQ_RES" = 0 ] ; then + echo "`date -u` : OK: No more changes to reconcile between ${SERVICE_FRAMEWORK} service instances and device configurations in '$UPSCONF'" + [ "${REPORT_RESTART_42-}" = no ] && exit 0 || exit 42 + fi + + exit 13 + ;; + --list-devices) + upslist_readFile_once && \ + if [ -n "$UPSLIST_FILE" ] ; then + echo "=== The currently defined configurations in '$UPSCONF' are:" >&2 + echo "$UPSLIST_FILE" + exit 0 + fi + echo "No devices detected in '$UPSCONF'" >&2 + exit 1 + ;; + --list-services) + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && \ + if [ -n "$UPSLIST_SVCS_RAW" ] ; then + echo "=== The currently defined service units are:" >&2 + echo "$UPSLIST_SVCS_RAW" + exit 0 + fi + echo "No service units detected" >&2 + exit 1 + ;; + --list-instances) + upslist_readSvcs "by user request" && \ + if [ -n "$UPSLIST_SVCS" ] ; then + echo "=== The currently defined service instances are:" >&2 + echo "$UPSLIST_SVCS" + exit 0 + fi + echo "No service instances detected" >&2 + exit 1 + ;; + --get-service-for-device) [ -z "$2" ] && echo "Device name argument required" >&2 && exit 1 + DEV="$2" + upslist_readSvcs "by user request" && [ -n "$UPSLIST_SVCS" ] \ + || { echo "No service instances detected" >&2 ; exit 1; } + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ -n "$UPSLIST_SVCS_RAW" ] \ + || { echo "No service units detected" >&2 ; exit 1; } + vINST="`$hook_validInstanceName "$DEV"`" + vUNITD="`$hook_validFullUnitName "$DEV"`" + vUNITI="`$hook_validFullUnitName "$vINST"`" + # First pass over simple verbatim names + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$DEV" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITD" ] ; then + echo "$UNIT" + exit 0 + fi + done + fi + done + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$vINST" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITI" ] ; then + echo "$UNIT" + exit 0 + fi + done + fi + done + echo "No service instances detected that match device '$2'" >&2 + exit 1 + ;; + --get-device-for-service) [ -z "$2" ] && echo "Service (instance) name argument required" >&2 && exit 1 + # Instance name can be a hash or "native" NUT section name + SVC="`$hook_validInstanceSuffixName "$2"`" + case "$SVC" in + MD5_*) ;; # fall through to the bulk of code + *) upslist_readFile_once || exit $? + echo "$UPSLIST_FILE" | egrep "^$SVC\$" + exit $? + ;; + esac + FINAL_RES=0 + OUT="`"$0" --list-services-for-devices`" && [ -n "$OUT" ] || FINAL_RES=$? + if [ "$FINAL_RES" = 0 ]; then + echo "$OUT" | grep "$SVC" | ( \ + while read _SVC _DEV ; do + _SVC="`$hook_validInstanceSuffixName "${_SVC}"`" || exit + [ "${_SVC}" = "${SVC}" ] && echo "$_DEV" && exit 0 + done ; exit 1 ) && exit 0 + fi + echo "No service instance '$2' was detected that matches a NUT device" >&2 + exit 1 + ;; + --list-services-for-devices) + FINAL_RES=0 + upslist_readFile_once && [ -n "$UPSLIST_FILE" ] \ + || { echo "No devices detected in '$UPSCONF'" >&2 ; exit 1 ; } + upslist_readSvcs "by user request" && [ -n "$UPSLIST_SVCS" ] \ + || { echo "No service instances detected" >&2 ; exit 1; } + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ -n "$UPSLIST_SVCS_RAW" ] \ + || { echo "No service units detected" >&2 ; exit 1; } + for DEV in $UPSLIST_FILE ; do + vINST="`$hook_validInstanceName "$DEV"`" + vUNITD="`$hook_validFullUnitName "$DEV"`" + vUNITI="`$hook_validFullUnitName "$vINST"`" + # First pass over simple verbatim names + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$DEV" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITD" ] ; then + printf '%s\t%s\n' "$UNIT" "$DEV" + continue 3 + fi + done + fi + done + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$vINST" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITI" ] ; then + printf '%s\t%s\n' "$UNIT" "$DEV" + continue 3 + fi + done + fi + done + echo "WARNING: no service instances detected that match device '$DEV'" >&2 + FINAL_RES=1 + done + exit $FINAL_RES + ;; + --show-configs|--show-device-configs|--show-all-configs|--show-all-device-configs) + RES=0 + upslist_readFile_once || RES=$? + [ "$RES" != 0 ] && { echo "ERROR: upslist_readFile_once () failed with code $RES" >&2; exit $RES; } + [ -n "$UPSLIST_FILE" ] \ + || { echo "WARNING: No devices detected in '$UPSCONF'" >&2 ; RES=1 ; } + echo "$UPSCONF_DATA" + exit $RES + ;; + --show-config|--show-device-config) + [ -z "$2" ] && echo "WARNING: Device name argument empty, will show global config" >&2 + DEV="$2" + upsconf_getSection "$DEV" + exit $? + ;; + --show-config-value|--show-device-config-value) + [ -z "$3" ] && echo "At least one configuration key name argument is required" >&2 && exit 1 + [ -z "$2" ] && echo "WARNING: Device name argument empty, will show global config" >&2 + DEV="$2" + shift 2 + upsconf_getValue "$DEV" "$@" + exit $? + ;; + upsconf_debug) # Not public, not in usage() + [ -z "$2" ] && echo "Device name argument required" >&2 && exit 1 + upsconf_debug "$2" + exit $? + ;; + upslist_debug) # Not public, not in usage() + upslist_readFile_once || exit + upslist_debug + exit $? + ;; + *) echo "Unrecognized argument: $1" >&2 ; exit 1 ;; + esac + shift +done diff --git a/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in b/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in new file mode 100755 index 0000000..a91ce5e --- /dev/null +++ b/scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in @@ -0,0 +1,1391 @@ +#!/bin/sh +# +# NOTE: This script is intentionally written with portable shell constructs +# with the aim and hope to work in different interpreters, so it is a +# bit dumber and less efficient than could be achieved with the more +# featured shells in the spectrum. +# NOTE ALSO: The configuration parser in this script is not meant to be a +# reference or 100% compliant with what the binary code uses; its aim +# is to just pick out some strings relevant for tracking config changes. +# +# Copyright (C) 2016-2020 Eaton +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +#! \file nut-driver-enumerator.sh(.in) +# \author Jim Klimov +# \brief Enumerate NUT devices for service-unit instance configuration +# \details This script allows to enumerate UPSes in order to produce the +# individual service unit instances for each defined configuration. +# It assumes the user has adequate permissions to inspect and create +# services (e.g. is a root or has proper RBAC profiles to do so). +# It helps service frameworks such as Linux systemd and Solaris SMF. +# When executed, this script looks for all configured ups.conf +# sections and registered service instances, and makes these two +# lists match up. It has also a mode to do this in a loop, to keep +# checking for differences and applying them, on systems where it's +# problematic to trigger it in response to FS event notifications. +# Returns exit codes: +# 0 Sections and services already match up +# 42 Sections and services differed, but now match up - +# now the caller should likely restart some services. +# Note that the drivers' service instances were started or +# stopped as required (by AUTO_START=yes) - but maybe the +# upsd or upsmon services should restart. If you pass envvar +# REPORT_RESTART_42=no then this codepath would return 0. +# In default mode, such non-null reconfiguration should cause +# the nut-driver-enumerator service to restart and this would +# propagate to other NUT services that depend on it. +# 13 Sections and services differed, and still do not match up +# 1 Bad inputs, e.g. unrecognized service management framework +# 2 Absent or unreadable ups.conf file +# + +# NOTE: Currently found caveats that might be solved later but require +# considerable effort: +# * Solaris SMF constrains the syntax of valid strings for instance names +# (e.g. not starting with a digit, no period chars) which blocks creation +# of some UPS driver instances. This might be worked around by hashing +# the device name e.g. to base64 (and un-hashing instance name when calling +# upsdrvctl), but is not quite user-friendly. Also can store device name +# in a service attribute while mangling the instance name to a valid subset. +# Comparisons (if devices are already wrapped) becomes more complicated in +# the script in either case, as well as in the service startup method. +# ** The `+` `/` `=` characters from base64 are also invalid for SMF instance +# name, but the first two can be sed'ed to `-` `_` and back, for example. +# Some prefix word is also needed (avoid starting with a digit). +# The trailing padding `=` can be dropped, and added until we get a +# non-empty decode. Conversions can be done by +# `echo "$string" | openssl base64 -e|-d` +# * Dummy-UPS services that "proxy" another locally defined section are +# essentially a circular dependency for upsd. While the system might +# start-up lacking a driver, there should be some timer to re-enable +# failed not-disabled drivers (would be useful in any case though). + +# Directory where NUT configs are located, e.g. /etc/nut or /etc/ups +# Set at package configuration, compiled into daemons and drivers +prefix="@prefix@" +[ -n "${NUT_CONFPATH-}" ] || NUT_CONFPATH="@sysconfdir@" +# Technically this should be a distribution-dependent value configured +# during package build. But everyone has it the same from systemd defaults: +[ -n "${SYSTEMD_CONFPATH-}" ] || SYSTEMD_CONFPATH="/etc/systemd/system" + +if [ -n "$ZSH_VERSION" ]; then + ### Problem: loops like `for UPS in $UPSLIST` do not separate + ### the UPSLIST into many tokens but use it as one string. + echo "FATAL: zsh is not supported in this script" >&2 + exit 1 +# setopt noglob +# setopt +F +# IFS="`printf ' \t\r\n'`" ; export IFS +fi + +if set | egrep '^(shell|version|t?csh)' | egrep 't?csh' >/dev/null ; then + echo "FATAL: csh or tcsh is not supported in this script" >&2 + exit 1 +fi + +# Third-party services to depend on (can be overridden by config file) +### Note that for systemd+udev integration, it may be better to set up +### triggers in udev, see e.g. +### http://stackoverflow.com/questions/18463755/linux-start-daemon-on-connected-usb-serial-dongle +### Also can tune whether a driver "Wants" another service (would consider +### ordering if that one is enabled, but live if it is disabled), or if it +### "Requires" that (would cause that to start). +DEPSVC_USB_SYSTEMD="systemd-udev.service systemd-udev-settle.service" +DEPREQ_USB_SYSTEMD="Wants" +DEPSVC_NET_FULL_SYSTEMD="network-online.target systemd-resolved.service ifplugd.service" +DEPREQ_NET_FULL_SYSTEMD="Wants" +DEPSVC_NET_LOCAL_SYSTEMD="network.target" +DEPREQ_NET_LOCAL_SYSTEMD="Wants" +SVCNAME_SYSTEMD="nut-driver" + +# Some or all of these FMRIs may be related to dynamically changing hardware +# require_all) ;; # All cited services are running (online or degraded) +# require_any) ;; # At least one of the cited services is running +# optional_all) ;; # (All) cited services are running or would not run +# # without administrative action (disabled, maintenance, +# # not present, or are waiting for dependencies which do +# # not start without administrative action). +DEPSVC_USB_SMF="svc:/system/hotplug:default svc:/system/dbus:default svc:/system/hal:default svc:/milestone/devices:default" +DEPREQ_USB_SMF="optional_all" +# By default there are several physical network FMRIs shipped and at most +# only one is enabled on a particular system (e.g. :default or :nwam) +DEPSVC_NET_FULL_SMF="svc:/network/physical svc:/milestone/name-services" +DEPREQ_NET_FULL_SMF="optional_all" +DEPSVC_NET_LOCAL_SMF="svc:/network/loopback:default" +DEPREQ_NET_LOCAL_SMF="optional_all" +SVCNAME_SMF="svc:/system/power/nut-driver" + +[ -z "${NUT_DRIVER_ENUMERATOR_CONF-}" ] && \ + NUT_DRIVER_ENUMERATOR_CONF="${NUT_CONFPATH}/nut-driver-enumerator.conf" + +[ -s "${NUT_DRIVER_ENUMERATOR_CONF}" ] && \ + echo "Sourcing config file: ${NUT_DRIVER_ENUMERATOR_CONF}" && \ + . "${NUT_DRIVER_ENUMERATOR_CONF}" + +[ -z "${UPSCONF-}" ] && \ + UPSCONF="${NUT_CONFPATH}/ups.conf" + +# Start a freshly-registered unit? +[ -z "${AUTO_START-}" ] && AUTO_START=yes + +# We avoid regex '\t' which gets misinterpreted by some tools +TABCHAR="`printf '\t'`" || TABCHAR=' ' + +if [ -z "${SERVICE_FRAMEWORK-}" ] ; then + [ -x /usr/sbin/svcadm ] && [ -x /usr/sbin/svccfg ] && [ -x /usr/bin/svcs ] && [ -x /usr/bin/svcprop ] && \ + SERVICE_FRAMEWORK="smf" + [ -z "${SERVICE_FRAMEWORK-}" ] && \ + [ -x /bin/systemctl ] && \ + SERVICE_FRAMEWORK="systemd" +fi + +# Optionally use Coreutils timeout to limit the +# (potentially hanging) calls to systemd tools... +# Should not hurt with SMF too, if it ever misbehaves. +if [ -z "${TIMEOUT_CMD+x}" ]; then + # Envvar not set at all (set but empty is okay, caller wants that then) + TIMEOUT_CMD="" + TIMEOUT_ARGS="" + if which timeout 2>/dev/null >/dev/null ; then + # Systemd default timeout for unit start/stop + TIMEOUT_CMD="timeout" + TIMEOUT_ARGS="90s" + fi +fi + +# Cache needed bits of ups.conf to speed up later parsing. Note that these +# data are needed for most operations, and populated by upslist_readFile() +UPSCONF_DATA="" +# Subset of normalized data above that only has sections, drivers and ports +UPSCONF_DATA_SDP="" + +# List of configured UPSes in the config-file +UPSLIST_FILE="" +# List of configured service instances for UPS drivers +UPSLIST_SVCS="" + +# Framework-specific implementations are generally hooked here: +hook_registerInstance="" +hook_unregisterInstance="" +hook_refreshSupervizor="" +hook_listInstances="" +hook_listInstances_raw="" +hook_validInstanceName="" +hook_validFullUnitName="" +hook_validInstanceSuffixName="" +hook_getSavedMD5="" +hook_setSavedMD5="" +hook_restart_upsd="" +hook_restart_drv="" + +case "${SERVICE_FRAMEWORK-}" in + smf) + hook_registerInstance="smf_registerInstance" + hook_unregisterInstance="smf_unregisterInstance" + hook_refreshSupervizor="smf_refreshSupervizor" + hook_listInstances="smf_listInstances" + hook_listInstances_raw="smf_listInstances_raw" + hook_validInstanceName="smf_validInstanceName" + hook_validFullUnitName="smf_validFullUnitName" + hook_validInstanceSuffixName="smf_validInstanceSuffixName" + hook_getSavedMD5="smf_getSavedMD5" + hook_setSavedMD5="smf_setSavedMD5" + hook_restart_upsd="smf_restart_upsd" + hook_restart_drv="smf_restart_drv" + ;; + systemd) + hook_registerInstance="systemd_registerInstance" + hook_unregisterInstance="systemd_unregisterInstance" + hook_refreshSupervizor="systemd_refreshSupervizor" + hook_listInstances="systemd_listInstances" + hook_listInstances_raw="systemd_listInstances_raw" + hook_validInstanceName="systemd_validInstanceName" + hook_validFullUnitName="systemd_validFullUnitName" + hook_validInstanceSuffixName="systemd_validInstanceSuffixName" + hook_getSavedMD5="systemd_getSavedMD5" + hook_setSavedMD5="systemd_setSavedMD5" + hook_restart_upsd="systemd_restart_upsd" + hook_restart_drv="systemd_restart_drv" + ;; + selftest) + hook_registerInstance="selftest_NOOP" + hook_unregisterInstance="selftest_NOOP" + hook_refreshSupervizor="selftest_NOOP" + hook_listInstances="selftest_NOOP" + hook_listInstances_raw="selftest_NOOP" + hook_validInstanceName="selftest_NOOP" + hook_validFullUnitName="selftest_NOOP" + hook_validInstanceSuffixName="selftest_NOOP" + hook_getSavedMD5="selftest_NOOP" + hook_setSavedMD5="selftest_NOOP" + hook_restart_upsd="selftest_NOOP" + hook_restart_drv="selftest_NOOP" + ;; + "") + echo "Error detecting the service-management framework on this OS" >&2 + exit 1 + ;; + *) + echo "Error: User provided an unknown service-management framework '$SERVICE_FRAMEWORK'" >&2 + exit 1 + ;; +esac + +selftest_NOOP() { + echo "NO-OP: Self-testing context does not do systems configuration" >&2 + return 0 +} + +common_isFiled() { + [ -n "$UPSLIST_FILE" ] && \ + for UPSF in $UPSLIST_FILE ; do + [ "$1" = "$UPSF" ] && return 0 + [ "`$hook_validInstanceName "$UPSF"`" = "$1" ] && return 0 + done + return 1 +} + +common_isRegistered() { + [ -n "$UPSLIST_SVCS" ] && \ + for UPSS in $UPSLIST_SVCS ; do + [ "$1" = "$UPSS" ] && return 0 + [ "`$hook_validInstanceName "$1"`" = "$UPSS" ] && return 0 + done + return 1 +} + +upslist_equals() { + # Compare pre-sorted list of DEVICES ($1) and SVCINSTs ($2) including + # the possible mangling for service names. Return 0 if lists describe + # exactly same set of devices and their services. + # Note: This logic only checks the names, not the contents of device + # sections, so re-definitions of an existing device configuration + # would not trigger a service restart by itself. Such deeper check + # belongs in a different routine, see upssvcconf_checksum_unchanged(). + + # Trivial case 0: one string is empty, another is not + # Note: `echo '' | wc -l` == "1" not "0"! + [ -n "$1" -a -z "$2" ] && return 1 + [ -z "$1" -a -n "$2" ] && return 1 + + # Trivial case 1: equal strings + [ "$1" = "$2" ] && return 0 + # Trivial case 2: different amount of entries + [ "`echo "$1" | wc -l`" = "`echo "$2" | wc -l`" ] || return $? + + _TMP_DEV_SVC="" + for _DEV in $1 ; do + DEVINST="`$hook_validInstanceName "$_DEV"`" + for _SVC in $2 ; do + [ "$_DEV" = "$_SVC" ] \ + || [ "$DEVINST" = "$_SVC" ] \ + && { [ -z "$_TMP_DEV_SVC" ] \ + && _TMP_DEV_SVC="$_DEV = $_SVC" \ + || _TMP_DEV_SVC="$_TMP_DEV_SVC +$_DEV = $_SVC" ; } + done + done + + # Input was not empty; did anything in output fit? + [ -z "$_TMP_DEV_SVC" ] && return 1 + + # Exit code : is the built mapping as long as the source list(s)? + [ "`echo "$1" | wc -l`" = "`echo "$_TMP_DEV_SVC" | wc -l`" ] +} + +upssvcconf_checksum_unchanged() { + # $1 = dev, $2 = svc + # compare checksums of the configuration section from the file and the + # stashed configuration in a service instance (if any). + # FIXME : optimize by caching, we likely have quite a few requests + [ "`upsconf_getSection_MD5 "$1"`" = "`$hook_getSavedMD5 "$2"`" ] +} + +upslist_checksums_unchanged() { + # For each device and its corresponding unit, compare checksums of the + # configuration section from the file and the stashed configuration in + # a service instance. Prints a list of mismatching service names that + # should get reconfigured. + [ -z "$1" -o -z "$2" ] && return 1 + + _TMP_SVC="" + for _DEV in $1 ; do + DEVINST="`$hook_validInstanceName "$_DEV"`" + for _SVC in $2 ; do + if [ "$_DEV" = "$_SVC" ] \ + || [ "$DEVINST" = "$_SVC" ] \ + ; then + upssvcconf_checksum_unchanged "$_DEV" "$_SVC" || \ + { [ -z "$_TMP_SVC" ] \ + && _TMP_SVC="$_SVC" \ + || _TMP_SVC="$_TMP_SVC +$_SVC" ; } + fi + done + done + [ -z "$_TMP_SVC" ] && return 0 + echo "$_TMP_SVC" + return 1 +} + +upsconf_getSection_content() { + # "$1" = name of ups.conf section to display in whole, from whatever + # comes on stdin (file or a pre-made normalized variable) + # empty "$1" means the global config (before any sections) + # + # NOTE (TODO?): This routine finds the one NUT device section, prints it + # and returns when the section is over. It currently does not cover (in + # a way allowing to do it efficiently) selection of several sections, + # or storing each section content in some array or dynamic variables + # (as would be better fit for portable shells) to later address them + # quickly without re-parsing the file or big envvar many times. + # + + CURR_SECTION="" + SECTION_CONTENT="" + RES=1 + [ -n "$1" ] || RES=0 + while read LINE ; do + case "$LINE" in + \["$1"\]) + if [ "$RES" = 0 ]; then + # We have already displayed a section, here is a new one, + # and this routine only displays one (TODO: toggle?) + break + fi + SECTION_CONTENT="$LINE" + CURR_SECTION="$1" + RES=0 + continue + ;; + \[*\ *\]|\[*"$TABCHAR"*\]) + # Note that section-name brackets should contain a single token + # Fall through to add the line to contents of existing section + ;; + \[*\]) + [ "$CURR_SECTION" = "$1" ] && break + # Use a value that can not be a section name here: + CURR_SECTION="[]" + continue + ;; + "") continue ;; + *) ;; # Fall through to add the line to contents of existing section + esac + if [ "$CURR_SECTION" = "$1" ]; then + if [ -n "$SECTION_CONTENT" ]; then + SECTION_CONTENT="$SECTION_CONTENT +$LINE" + else + SECTION_CONTENT="$LINE" + fi + fi + done + + if [ -n "$SECTION_CONTENT" ]; then + echo "$SECTION_CONTENT" + fi + + [ "$RES" = 0 ] || echo "ERROR: Section [$1] was not found in the '$UPSCONF' file" >&2 + return $RES +} + +upsconf_getSection() { + # Use the whole output of normalization parser + upslist_normalizeFile_once || return # Propagate errors upwards + upsconf_getSection_content "$@" << EOF +${UPSCONF_DATA} +EOF +} + +upsconf_getSection_MD5() { + calc_md5 "`upsconf_getSection "$@"`" +} + +upsconf_getSection_SDP() { + # Use the section-driver-port subset + upslist_normalizeFile_once || return # Propagate errors upwards + upsconf_getSection_content "$@" << EOF +${UPSCONF_DATA_SDP} +EOF +} + +upsconf_getValue() { + # "$1" = name of ups.conf section, may be empty for global config + # "$2..$N" = name of config key; we will echo its value +### [ -n "$1" ] || return $? + [ -n "$2" ] || return $? + [ -n "$GETSECTION" ] || GETSECTION="upsconf_getSection" + CURR_SECTION="" # Gets set by a GETSECTION implementation + RES=0 + + # Note: Primary aim of this egrep is to pick either assignments or flags + # As a by-product it can be used to test if a particular value is set ;) + SECTION_CONTENT="`$GETSECTION "$1"`" || return + shift + KEYS="$*" + + while [ "$#" -gt 0 ] ; do + RES_L=0 + VALUE="" + + LINE="`echo "$SECTION_CONTENT" | egrep '(^'"$1"'=|^'"$1"'$)'`" \ + && VALUE="$(echo "$LINE" | sed -e "s,^$1=,," -e 's,^\"\(.*\)\"$,\1,' -e "s,^'\(.*\)'$,\1,")" \ + || RES_L=$? + + [ "$RES_L" = 0 ] || { RES="$RES_L" ; echo "ERROR: Section [$CURR_SECTION] or key '$1' in it was not found in the '$UPSCONF' file" >&2 ; } + + echo "$VALUE" + shift + done + + [ "$RES" = 0 ] || echo "ERROR: Section [$CURR_SECTION] or key(s) '$KEYS' in it was not found in the '$UPSCONF' file" >&2 + return $RES +} + +upsconf_getDriver() { + # "$1" = name of ups.conf section; return (echo) the driver name used there + # In the context this function is used, UPSCONF exists and section is there + GETSECTION="upsconf_getSection_SDP" upsconf_getValue "$1" "driver" + return $? +} + +upsconf_getPort() { + # "$1" = name of ups.conf section; return (echo) the "port" name used there + # In the context this function is used, UPSCONF exists and section is there + GETSECTION="upsconf_getSection_SDP" upsconf_getValue "$1" "port" + return $? +} + +upsconf_getDriverMedia() { + # "$1" = name of ups.conf section; return (echo) name and type of driver as + # needed for dependency evaluation (what services we must depend on for this + # unit), newline-separated (drvnametype). Empty type for unclassified + # results, assuming no known special dependencies (note that depending on + # particular system's physics, both serial and network media may need USB). + CURR_DRV="`upsconf_getDriver "$1"`" || return $? + case "$CURR_DRV" in + *netxml*|*snmp*|*ipmi*|*powerman*|*-mib*|*avahi*|*apcupsd*) + printf '%s\n%s\n' "$CURR_DRV" "network" ; return ;; + *usb*) + printf '%s\n%s\n' "$CURR_DRV" "usb" ; return ;; + nutdrv_qx) # May be direct serial or USB + CURR_PORT="`upsconf_getPort "$1"`" || CURR_PORT="" + case "$CURR_PORT" in + auto|/dev/*usb*|/dev/*hid*) + printf '%s\n%s\n' "$CURR_DRV" "usb" ; return ;; + /dev/*) + # See drivers/nutdrv_qx.c :: upsdrv_initups() for a list + if [ -n "`upsconf_getValue "$1" 'subdriver' 'vendorid' 'productid' 'vendor' 'product' 'serial' 'bus' 'langid_fix'`" ] \ + ; then + printf '%s\n%s\n' "$CURR_DRV" "usb" ; return + else + printf '%s\n%s\n' "$CURR_DRV" "serial" ; return + fi + ;; + *) + printf '%s\n%s\n' "$CURR_DRV" "" ; return ;; + esac + ;; + *dummy*|*clone*) # May be networked (proxy to remote NUT) + CURR_PORT="`upsconf_getPort "$1"`" || CURR_PORT="" + case "$CURR_PORT" in + *@localhost|*@|*@127.0.0.1|*@::1) + printf '%s\n%s\n' "$CURR_DRV" "network-localhost" ; return ;; + *@*) + printf '%s\n%s\n' "$CURR_DRV" "network" ; return ;; + *) + printf '%s\n%s\n' "$CURR_DRV" "" ; return ;; + esac + ;; + *) printf '%s\n%s\n' "$CURR_DRV" "" ; return ;; + esac +} + +upsconf_getMedia() { + _DRVMED="`upsconf_getDriverMedia "$1"`" || return + echo "$_DRVMED" | tail -n +2 + return 0 +} + +upsconf_debug() { + _DRV="`upsconf_getDriver "$1"`" + _PRT="`upsconf_getPort "$1"`" + _MED="`upsconf_getMedia "$1"`" + _MD5="`upsconf_getSection_MD5 "$1"`" + NAME_MD5="`calc_md5 "$1"`" + echo "INST: ${NAME_MD5}~[$1]: DRV='$_DRV' PORT='$_PRT' MEDIA='$_MED' SECTIONMD5='$_MD5'" +} + +calc_md5() { + # Tries several ways to produce an MD5 of the "$1" argument + _MD5="`echo "$1" | md5sum 2>/dev/null | awk '{print $1}'`" && [ -n "$_MD5" ] || \ + { _MD5="`echo "$1" | openssl dgst -md5 2>/dev/null | awk '{print $NF}'`" && [ -n "$_MD5" ]; } || \ + return 1 + + echo "$_MD5" +} + +calc_md5_file() { + # Tries several ways to produce an MD5 of the file named by "$1" argument + [ -s "$1" ] || return 2 + + _MD5="`md5sum 2>/dev/null < "$1" | awk '{print $1}'`" && [ -n "$_MD5" ] || \ + { _MD5="`openssl dgst -md5 2>/dev/null < "$1" | awk '{print $NF}'`" && [ -n "$_MD5" ]; } || \ + return 1 + + echo "$_MD5" +} + +smf_validFullUnitName() { + case "$1" in + *:*) echo "$1" ;; + *) echo "$SVCNAME_SMF:$1" ;; + esac +} +smf_validInstanceName() { + echo "MD5_`calc_md5 "$1"`" +} +smf_validInstanceSuffixName() { + case "$1" in + *:*) echo "$1" | sed 's,^.*:\([^:]*\)$,\1,' ;; + *) echo "$1" ;; + esac +} +smf_registerInstance() { + DEVICE="$1" + SVCINST="$1" + /usr/sbin/svccfg -s nut-driver add "$DEVICE" || \ + { SVCINST="`smf_validInstanceName "$1"`" && \ + /usr/sbin/svccfg -s nut-driver add "$SVCINST" || return ; } + echo "Added instance: 'nut-driver:$SVCINST' for NUT configuration section '$DEVICE'" >&2 + + DEPSVC="" + DEPREQ="" + _MED="`upsconf_getMedia "$DEVICE"`" + case "$_MED" in + usb) + DEPSVC="$DEPSVC_USB_SMF" + DEPREQ="$DEPREQ_USB_SMF" ;; + network-localhost) + DEPSVC="$DEPSVC_NET_LOCAL_SMF" + DEPREQ="$DEPREQ_NET_LOCAL_SMF" ;; + network) + DEPSVC="$DEPSVC_NET_FULL_SMF" + DEPREQ="$DEPREQ_NET_FULL_SMF" ;; + serial) ;; + '') ;; + *) echo "WARNING: Unexpected NUT media type ignored: '$_MED'" >&2 ;; + esac + + TARGET_FMRI="nut-driver:$SVCINST" + if [ -n "$DEPSVC" ]; then + [ -n "$DEPREQ" ] || DEPREQ="optional_all" + echo "Adding '$DEPREQ' dependency for '$SVCINST' on '$DEPSVC'..." + + DEPPG="nut-driver-enumerator-generated" + RESTARTON="refresh" + /usr/sbin/svccfg -s "$TARGET_FMRI" addpg "$DEPPG" dependency && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$DEPPG"/grouping = astring: "$DEPREQ" && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$DEPPG"/restart_on = astring: "$RESTARTON" && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$DEPPG"/type = astring: service && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$DEPPG"/entities = fmri: "($DEPSVC)" && \ + echo "OK" || echo "FAILED to define the dependency" >&2 + fi + + smf_setSavedMD5 "$SVCINST" "`upsconf_getSection_MD5 "$DEVICE"`" + + /usr/sbin/svcadm refresh "${TARGET_FMRI}" || return + if [ "$AUTO_START" = yes ] ; then + /usr/sbin/svcadm clear "${TARGET_FMRI}" 2>/dev/null || true + /usr/sbin/svcadm enable "${TARGET_FMRI}" || return + echo "Started instance: 'nut-driver:$SVCINST' for NUT configuration section '$DEVICE'" >&2 + fi +} +smf_unregisterInstance() { + echo "Removing instance: 'nut-driver:$1' ..." >&2 + /usr/sbin/svcadm disable -ts 'nut-driver:'"$1" || false + /usr/sbin/svccfg -s nut-driver delete "$1" +} +smf_refreshSupervizor() { + : +} +smf_listInstances_raw() { + # Newer versions have pattern matching; older SMF might not have this luxury + /usr/bin/svcs -a -H -o fmri | egrep '/nut-driver:' +} +smf_listInstances() { + smf_listInstances_raw | sed 's/^.*://' | sort -n +} +smf_getSavedMD5() { + # Query service instance $1 + PG="nut-driver-enumerator-generated-checksum" + PROP="SECTION_CHECKSUM" + + if [ -n "$1" ]; then + TARGET_FMRI="nut-driver:$1" + else + # Global section + TARGET_FMRI="nut-driver" + PROP="SECTION_CHECKSUM_GLOBAL" + fi + + # Note: lookups for GLOBAL cause each service instance to show up + /usr/bin/svcprop -p "$PG/$PROP" "$TARGET_FMRI" | head -1 | awk '{print $NF}' +} +smf_setSavedMD5() { + # Save checksum value $2 into service instance $1 + PG="nut-driver-enumerator-generated-checksum" + PROP="SECTION_CHECKSUM" + + if [ -n "$1" ]; then + TARGET_FMRI="nut-driver:$1" + else + # Global section + TARGET_FMRI="nut-driver" + PROP="SECTION_CHECKSUM_GLOBAL" + fi + + /usr/sbin/svccfg -s "$TARGET_FMRI" delprop "$PG" || true + /usr/sbin/svccfg -s "$TARGET_FMRI" addpg "$PG" application && \ + /usr/sbin/svccfg -s "$TARGET_FMRI" setprop "$PG/$PROP" = astring: "$2" + [ $? = 0 ] && echo "OK" || { echo "FAILED to stash the checksum">&2 ; return 1 ; } + /usr/sbin/svcadm refresh "${TARGET_FMRI}" || return +} +smf_restart_upsd() { + echo "Restarting NUT data server to make sure it knows new configuration..." + /usr/sbin/svcadm enable "nut-server" 2>/dev/null + /usr/sbin/svcadm clear "nut-server" 2>/dev/null + /usr/sbin/svcadm refresh "nut-server" || \ + /usr/sbin/svcadm restart "nut-server" +} +smf_restart_drv() { + echo "Restarting NUT driver instance '$1' to make sure it knows new configuration..." + /usr/sbin/svcadm enable "nut-driver:$1" 2>/dev/null + /usr/sbin/svcadm clear "nut-driver:$1" 2>/dev/null + /usr/sbin/svcadm refresh "nut-driver:$1" || \ + /usr/sbin/svcadm restart "nut-driver:$1" +} + +systemd_validFullUnitName() { + case "$1" in + *@*.*) echo "$1" ;; + *@*) echo "$1.service" ;; + *) echo "$SVCNAME_SYSTEMD@$1.service" ;; + esac +} +systemd_validInstanceName() { + echo "MD5_`calc_md5 "$1"`" +} +systemd_validInstanceSuffixName() { + echo "$1" | sed -e 's,^.*@,,' -e 's,\.service$,,' +} +systemd_registerInstance() { + # Instance is registered by device section name; ultimate name in systemd may differ + DEVICE="$1" + SVCINST="$1" + /bin/systemctl enable 'nut-driver@'"$DEVICE".service || \ + { SVCINST="`systemd_validInstanceName "$1"`" && \ + /bin/systemctl enable 'nut-driver@'"$SVCINST".service || return ; } + echo "Enabled instance: 'nut-driver@$SVCINST' for NUT configuration section '$DEVICE'" >&2 + + DEPSVC="" + DEPREQ="" + _MED="`upsconf_getMedia "$DEVICE"`" + case "$_MED" in + usb) + DEPSVC="$DEPSVC_USB_SYSTEMD" + DEPREQ="$DEPREQ_USB_SYSTEMD" ;; + network-localhost) + DEPSVC="$DEPSVC_NET_LOCAL_SYSTEMD" + DEPREQ="$DEPREQ_NET_LOCAL_SYSTEMD" ;; + network) + DEPSVC="$DEPSVC_NET_FULL_SYSTEMD" + DEPREQ="$DEPREQ_NET_FULL_SYSTEMD" ;; + serial) ;; + '') ;; + *) echo "WARNING: Unexpected NUT media type ignored: '$_MED'" >&2 ;; + esac + if [ -n "$DEPSVC" ]; then + [ -n "$DEPREQ" ] || DEPREQ="#Wants" + echo "Adding '$DEPREQ'+After dependency for '$SVCINST' on '$DEPSVC'..." + mkdir -p "${SYSTEMD_CONFPATH}/nut-driver@$SVCINST.service.d" && \ + cat > "${SYSTEMD_CONFPATH}/nut-driver@$SVCINST.service.d/nut-driver-enumerator-generated.conf" <&2 + fi + + systemd_setSavedMD5 "$SVCINST" "`upsconf_getSection_MD5 "$DEVICE"`" + + if [ "$AUTO_START" = yes ] ; then + systemd_refreshSupervizor || echo "WARNING: Somehow managed to fail systemd_refreshSupervizor()" >&2 + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl start --no-block 'nut-driver@'"$SVCINST".service || return + echo "Started instance: 'nut-driver@$SVCINST' for NUT configuration section '$DEVICE'" >&2 + fi +} +systemd_unregisterInstance() { + echo "Removing instance: 'nut-driver@$1' ..." >&2 + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl stop 'nut-driver@'"$1".service || \ + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl stop 'nut-driver@'"$1".service || \ + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl stop 'nut-driver@'"$1".service || false + + /bin/systemctl disable 'nut-driver@'"$1".service + rm -rf "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d" + /bin/systemctl reset-failed 'nut-driver@'"$1".service +} +systemd_refreshSupervizor() { + /bin/systemctl daemon-reload +} +systemd_listInstances_raw() { + /bin/systemctl show 'nut-driver@*' -p Id | egrep '=nut-driver' | sed 's,^Id=,,' +} +systemd_listInstances() { + systemd_listInstances_raw | sed -e 's/^.*@//' -e 's/\.service$//' | sort -n +} +systemd_getSavedMD5() { + # Query service instance $1 or global section + PROP="SECTION_CHECKSUM" + [ -n "$1" ] || PROP="SECTION_CHECKSUM_GLOBAL" + [ -s "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" ] \ + && grep "Environment='$PROP=" "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" | sed -e "s,^Environment='$PROP=,," -e "s,'\$,," \ + || { echo "Did not find '${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf' with a $PROP" ; return 1; } +} +systemd_setSavedMD5() { + # Save checksum value $2 into service instance $1 + PROP="SECTION_CHECKSUM" + [ -n "$1" ] || PROP="SECTION_CHECKSUM_GLOBAL" + mkdir -p "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d" && \ + cat > "${SYSTEMD_CONFPATH}/nut-driver@$1.service.d/nut-driver-enumerator-generated-checksum.conf" << EOF +[Service] +Environment='$PROP=$2' +EOF + [ $? = 0 ] && echo "OK" || { echo "FAILED to stash the checksum">&2 ; return 1 ; } +} +systemd_restart_upsd() { + # Do not restart/reload if not already running + case "`/bin/systemctl is-active "nut-server"`" in + active|unknown) ;; # unknown meant "starting" in our testing... + failed) echo "Note: nut-server unit was 'failed' - not disabled by user, so (re)starting it (probably had no config initially)" >&2 ;; + *) return 0 ;; + esac + + echo "Restarting NUT data server to make sure it knows new configuration..." + # Note: reload is a better way to go about this, so the + # data service is not interrupted by re-initialization + # of the daemon. But systemd/systemctl sometimes stalls... + + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl reload-or-restart "nut-server" || \ + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl restart "nut-server" +} + +systemd_restart_drv() { + # Do not restart/reload if not already running + case "`/bin/systemctl is-active "nut-driver@$1"`" in + active|unknown) ;; + *) return 0 ;; + esac + + echo "Restarting NUT driver instance '$1' to make sure it knows new configuration..." + + # Full restart, e.g. in case we changed the user= in configs + $TIMEOUT_CMD $TIMEOUT_ARGS /bin/systemctl restart "nut-driver@$1" +} + +upslist_normalizeFile_filter() { + # See upslist_normalizeFile() detailed comments below; this routine + # is a pipe worker to prepare the text into a simpler expected form. + + # Pick the lines which contain a bracket or assignment character, + # or a single token (certain keywords come as just NUT "flags"), + # trim leading and trailing whitespace, comment-only lines, and in + # assignment lines trim the spaces around equality character and + # quoting characters around assignment of values without whitespaces. + # Any whitespace characters around a section name (single token that + # starts the line and is enclosed in brackets) and a trailing comment + # are dropped. Note that brackets with spaces inside, and brackets + # that do not start the non-whitespace payload of the line, are not + # sections. + egrep -v '(^$|^#)' | \ + sed -e 's,^['"$TABCHAR"'\ ]*,,' \ + -e 's,^\#.*$,,' \ + -e 's,['"$TABCHAR"'\ ]*$,,' \ + -e 's,^\([^=\ '"$TABCHAR"']*\)['"$TABCHAR"'\ ]*=['"$TABCHAR"'\ ]*,\1=,g' \ + -e 's,=\"\([^\ '"$TABCHAR"']*\)\"$,=\1,' \ + -e 's,^\(\[[^]'"$TABCHAR"'\ ]*\]\)['"$TABCHAR"'\ ]*\(#.*\)*$,\1,' \ + | egrep -v '^$' \ + | egrep '([\[\=]|^[^ '"$TABCHAR"']*$|^[^ '"$TABCHAR"']*[ '"$TABCHAR"']*\#.*$)' +} + +upslist_normalizeFile() { + # Read the ups.conf file and find all defined sections (names of + # configuration blocks for drivers that connect to a certain device + # using specified protocol and media); normalize section contents + # as detailed below, to simplify subsequent parsing and comparison. + + # File contents + UPSCONF_DATA="" + UPSCONF_DATA_SDP="" + if [ -n "$UPSCONF" ] && [ -f "$UPSCONF" ] && [ -r "$UPSCONF" ]; then + [ ! -s "$UPSCONF" ] \ + && echo "WARNING: The '$UPSCONF' file exists but is empty" >&2 \ + && return 0 + # Ok to continue - we may end up removing all instances + else + echo "FATAL: The '$UPSCONF' file does not exist or is not readable" >&2 + return 2 + fi + + # Store a normalized version of NUT configuration file contents. + # Also use a SDP subset with just section, driver and port info + # for faster parsing when determining driver-required media etc. + UPSCONF_DATA="$(upslist_normalizeFile_filter < "$UPSCONF")" \ + && [ -n "$UPSCONF_DATA" ] \ + && UPSCONF_DATA_SDP="`egrep '^(\[.*\]|driver=|port=)' << EOF +$UPSCONF_DATA +EOF`" \ + || { echo "Error reading the '$UPSCONF' file or it does not declare any device configurations: nothing left after normalization" >&2 + UPSCONF_DATA="" + UPSCONF_DATA_SDP="" + } +} + +upslist_normalizeFile_once() { + # Wrapper that ensures that the parsing is only done once + # (will re-parse if there were no devices listed on the + # first time, though) + [ -z "$UPSCONF_DATA" ] && [ -z "$UPSCONF_DATA_SDP" ] || return 0 + upslist_normalizeFile +} + +upslist_readFile() { + # Use the routine above (unconditionally) to get or update the + # listing of device sections known at this moment. + + # List of devices from the config file + UPSLIST_FILE="" + if [ "$DO_NORMALIZE_ONCE" = yes ]; then + upslist_normalizeFile_once || return # Propagate errors upwards + else + upslist_normalizeFile || return # Propagate errors upwards + fi + + if [ -n "$UPSCONF_DATA" ] ; then + # Note that section-name brackets should contain a single token + UPSLIST_FILE="$(echo "$UPSCONF_DATA_SDP" | egrep '^\[[^'"$TABCHAR"'\ ]*\]$' | sed 's,^\[\(.*\)\]$,\1,' | sort -n)" \ + || UPSLIST_FILE="" + if [ -z "$UPSLIST_FILE" ] ; then + echo "Error reading the '$UPSCONF' file or it does not declare any device configurations: no section declarations in parsed normalized contents" >&2 + fi + fi + # Ok to continue with empty results - we may end up removing all instances +} + +upslist_readFile_once() { + # Wrapper that ensures that the parsing is only done once + # (will re-parse if there were no devices listed on the + # first time, though) + [ -z "$UPSLIST_FILE" ] || return 0 + DO_NORMALIZE_ONCE=yes upslist_readFile +} + +upslist_readSvcs() { + UPSLIST_SVCS="`$hook_listInstances`" || UPSLIST_SVCS="" + if [ -z "$UPSLIST_SVCS" ] && [ "$1" != "-" ] ; then + EXPLAIN="" + [ -z "$1" ] || EXPLAIN=" - $1" + echo "Error reading the list of ${SERVICE_FRAMEWORK-} service instances for UPS drivers, or none are defined${EXPLAIN}" >&2 + # Ok to continue - we may end up defining new instances + fi +} + +upslist_debug() { + for UPSF in "" $UPSLIST_FILE ; do + upsconf_debug "$UPSF" + done +} + +upslist_addSvcs() { + # Note: This routine registers service instances for device config sections + # that are not wrapped currently. Support for redefined previously existing + # sections - is attained by removing the old service instance elsewhere and + # recreating it here, since any data could change including the dependency + # list, etc. + for UPSF in $UPSLIST_FILE ; do + if ! common_isRegistered "$UPSF" ; then + echo "Adding new ${SERVICE_FRAMEWORK} service instance for power device [${UPSF}]..." >&2 + $hook_registerInstance "$UPSF" + fi + done +} + +upslist_delSvcs() { + for UPSS in $UPSLIST_SVCS ; do + if ! common_isFiled "$UPSS" ; then + echo "Dropping old ${SERVICE_FRAMEWORK} service instance for power device [${UPSS}] which is no longer in config file..." >&2 + $hook_unregisterInstance "$UPSS" + fi + done +} + +upslist_restartSvcs() { + for UPSS in $UPSLIST_SVCS ; do + if common_isFiled "$UPSS" ; then + $hook_restart_drv "$UPSS" + fi + done +} + +nut_driver_enumerator_main() { + ################# MAIN PROGRAM by default + + # Note: do not use the read..._once() here, to ensure that the + # looped daemon sees the whole picture, which can be new every time + upslist_readFile || return $? + #upslist_debug + upslist_readSvcs "before manipulations" + + # Test if global config has changed since last run + RESTART_ALL=no + upssvcconf_checksum_unchanged "" || { echo "`date -u` : Detected changes in global section of '$UPSCONF', will restart all drivers"; RESTART_ALL=yes; } + + # Quickly exit if there's nothing to do; note the lists are pre-sorted + # Otherwise a non-zero exit will be done below + # Note: We implement testing in detail whether section definitions were + # changed since last run, as a first step before checking that name + # lists are still equivalent, because we need to always have the result + # of the "has it changed?" check as a hit-list of something to remove, + # while the check for no new device section definitions is just boolean. + # We can only exit quickly if both there are no changed sections and no + # new or removed sections since last run. + NEW_CHECKSUM="`upslist_checksums_unchanged "$UPSLIST_FILE" "$UPSLIST_SVCS"`" \ + && [ -z "$NEW_CHECKSUM" ] \ + && upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" \ + && if [ -z "$DAEMON_SLEEP" -o "${VERBOSE_LOOP}" = yes ] ; then \ + echo "`date -u` : OK: No changes to reconcile between ${SERVICE_FRAMEWORK} service instances and device configurations in '$UPSCONF'" ; \ + fi \ + && [ "$RESTART_ALL" = no ] && return 0 + + if [ -n "$NEW_CHECKSUM" ]; then + for UPSS in $NEW_CHECKSUM ; do + echo "Dropping old ${SERVICE_FRAMEWORK} service instance ${UPSS} whose section in config file has changed..." >&2 + $hook_unregisterInstance "$UPSS" + done + upslist_readSvcs "after updating for new config section checksums" + fi + + if [ -n "$UPSLIST_SVCS" ]; then + # Drop services that are not in config file (any more?) + upslist_delSvcs + + if [ "$RESTART_ALL" = yes ] && [ "$AUTO_START" = yes ] ; then + # Here restart only existing services; new ones will (try to) + # start soon after creation and upsd is handled below + upslist_restartSvcs + fi + fi + + if [ "$RESTART_ALL" = yes ] ; then + # Save new checksum of global config + $hook_setSavedMD5 "" "`upsconf_getSection_MD5 ""`" + fi + + if [ -n "$UPSLIST_FILE" ]; then + # Add services for sections that are in config file but not yet wrapped + upslist_addSvcs + $hook_refreshSupervizor + upslist_readSvcs "after checking for new config sections to define service instances" + fi + + upslist_readSvcs + if [ -n "$UPSLIST_SVCS" ] ; then + echo "=== The currently defined service instances are:" + echo "$UPSLIST_SVCS" + fi + + if [ -n "$UPSLIST_FILE" ] ; then + echo "=== The currently defined configurations in '$UPSCONF' are:" + echo "$UPSLIST_FILE" + fi + + # We had some changes to the config file; upsd must be made aware + if [ "$AUTO_START" = yes ] ; then + $hook_restart_upsd + fi + + # Return 42 if there was a change applied succesfully + # (but e.g. some services should restart - upsd, maybe upsmon) + UPSLIST_EQ_RES=0 + upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" || UPSLIST_EQ_RES=$? + + # File processing and service startups take a while; + # make sure upsconf did not change while we worked... + # NOTE: Check this at the last moment to minimize + # the chance of still not noticing the change made + # at just the wrong moment. + UPSCONF_CHECKSUM_END="`calc_md5_file "$UPSCONF"`" || true + if [ "$UPSCONF_CHECKSUM_END" != "$UPSCONF_CHECKSUM_START" ] ; then + # NOTE: even if daemonized, the sleep between iterations + # can be configured into an uncomfortably long lag, so + # we should re-sync the system config in any case. + echo "`date -u` : '$UPSCONF' changed while $0 $* was processing its older contents; re-running the script to pick up the late-coming changes" + # Make sure the cycle does not repeat itself due to diffs + # from an ages-old state of the file from when we started. + UPSCONF_CHECKSUM_START="$UPSCONF_CHECKSUM_END" + ( nut_driver_enumerator_main ) ; return $? + # The "main" routine at the end of recursions will + # do REPORT_RESTART_42 logic or the error exit-code + fi + + if [ "$UPSLIST_EQ_RES" = 0 ] ; then + echo "`date -u` : OK: No more changes to reconcile between ${SERVICE_FRAMEWORK} service instances and device configurations in '$UPSCONF'" + [ "${REPORT_RESTART_42-}" = no ] && return 0 || return 42 + fi + return 13 +} + +daemonize() ( + # Support (SIG)HUP == signal code 1 to quickly reconfigure, + # e.g. to request it while the sleep is happening or while + # "main" is processing an earlier change of the file. + RECONFIGURE_ASAP=false + trap 'RECONFIGURE_ASAP=true' 1 + + # Note: this loop would die on errors with config file or + # inability to ensure that it matches the list of services. + # If caller did not `export REPORT_RESTART_42=no` then the + # loop would exit with code 42, and probably trigger restart + # of the service which wraps this daemon do topple others that + # depend on it. + # Note: do not quickly skip the "main" based on full-file + # checksum refresh, to ensure that whatever is configured + # gets applied (e.g. if user disabled some services or they + # died, or some config was not applied due to coding error). + while nut_driver_enumerator_main ; do + if $RECONFIGURE_ASAP ; then + echo "`date -u` : Trapped a SIGHUP during last run of nut_driver_enumerator_main, repeating reconfiguration quickly" >&2 + else + sleep $DAEMON_SLEEP & + trap "kill $! ; echo 'Sleep interrupted, processing configs now!'>&2" 1 + wait $! + fi + RECONFIGURE_ASAP=false + trap 'RECONFIGURE_ASAP=true' 1 + done + exit $? +) + +# Save the checksum of ups.conf as early as possible, +# to test in the end that it is still the same file. +UPSCONF_CHECKSUM_START="`calc_md5_file "$UPSCONF"`" || true + +# By default, update wrapping of devices into services +if [ $# = 0 ]; then + nut_driver_enumerator_main ; exit $? +fi + +if [ $# = 1 ] ; then + [ -n "$DAEMON_SLEEP" ] || DAEMON_SLEEP=60 + # Note: Not all shells have 'case ... ;&' support + case "$1" in + --daemon=*) DAEMON_SLEEP="`echo "$1" | sed 's,^--daemon=,,'`" ;; + esac + case "$1" in + --daemon|--daemon=*) + daemonize & + exit $? + ;; + esac +fi +unset DAEMON_SLEEP + +usage() { + cat << EOF +$0 (no args) + Update wrapping of devices into services +$0 --daemon(=freq) + Update wrapping of devices into services in an infinite loop + Default freq is 60 sec +$0 --reconfigure + Stop and un-register all service instances and recreate them + (e.g. if new dependency template was defined in a new + version of this script and/or NUT package) +$0 --get-service-framework + Print the detected service + management framework in this OS +$0 --list-devices + Print list of devices in NUT config +$0 --list-services + Print list of service instances which wrap registered + NUT devices (full name of service unit) +$0 --list-instances + Print list of service instances which wrap registered + NUT devices (just instance suffix) +$0 --get-service-for-device DEV + Print the full name of service unit which wraps a NUT + device named "DEV" +$0 --get-device-for-service SVC + Print the NUT device name for full or instance-suffix name of + a service unit which wraps it +$0 --list-services-for-devices + Print a TAB-separated list of service units and corresponding + NUT device names which each such unit wraps +$0 --show-configs|--show-all-configs + Show the complete normalized list of device configuration blocks +$0 --show-config DEV +$0 --show-device-config DEV + Show configuration block of the specified NUT device +$0 --show-device-config-value DEV KEY [KEY...] + Show single configuration key value of the specified NUT device + For flags, shows the flag name if present in the section + If several keys or flags are requested, their values are reported + one per line in the same order (including empty lines for missing + values); any missing value yields a non-zero exit code. +EOF +} + +while [ $# -gt 0 ]; do + case "$1" in + --help|-h|-help) usage; exit 0 ;; + --get-service-framework) echo "${SERVICE_FRAMEWORK}" ; exit 0 ;; + --reconfigure) + upslist_readFile_once || exit $? + upslist_readSvcs "before manipulations" + + if [ -n "$UPSLIST_SVCS" ]; then + for UPSS in $UPSLIST_SVCS ; do + echo "Dropping old ${SERVICE_FRAMEWORK} service instance for power device [${UPSS}] to reconfigure the service unit..." >&2 + $hook_unregisterInstance "$UPSS" + done + upslist_readSvcs "after dropping" + fi + + if [ -n "$UPSLIST_FILE" ]; then + upslist_addSvcs + upslist_readSvcs "after checking for new config sections to define service instances" + fi + + # Save new checksum of global config + $hook_setSavedMD5 "" "`upsconf_getSection_MD5 ""`" + + # Service units were manipulated, including saving of checksums; + # refresh the service management daemon if needed + $hook_refreshSupervizor + + if [ -n "$UPSLIST_SVCS" ] ; then + echo "=== The currently defined service instances are:" + echo "$UPSLIST_SVCS" + fi + + if [ -n "$UPSLIST_FILE" ] ; then + echo "=== The currently defined configurations in '$UPSCONF' are:" + echo "$UPSLIST_FILE" + fi + + # We had some changes to the config file; upsd must be made aware + if [ "$AUTO_START" = yes ] ; then + $hook_restart_upsd + fi + + # Return 42 if there was a change applied succesfully + # (but e.g. some services should restart - upsd, maybe upsmon) + UPSLIST_EQ_RES=0 + upslist_equals "$UPSLIST_FILE" "$UPSLIST_SVCS" || UPSLIST_EQ_RES=$? + + # File processing and service startups take a while; + # make sure upsconf did not change while we worked... + # NOTE: Check this at the last moment to minimize + # the chance of still not noticing the change made + # at just the wrong moment. + UPSCONF_CHECKSUM_END="`calc_md5_file "$UPSCONF"`" || true + if [ "$UPSCONF_CHECKSUM_END" != "$UPSCONF_CHECKSUM_START" ] ; then + echo "`date -u` : '$UPSCONF' changed while $0 $* was processing its older contents; re-running the script to pick up the late-coming changes" + $0 ; exit $? + # The "main" routine will do REPORT_RESTART_42 logic too + fi + + if [ "$UPSLIST_EQ_RES" = 0 ] ; then + echo "`date -u` : OK: No more changes to reconcile between ${SERVICE_FRAMEWORK} service instances and device configurations in '$UPSCONF'" + [ "${REPORT_RESTART_42-}" = no ] && exit 0 || exit 42 + fi + + exit 13 + ;; + --list-devices) + upslist_readFile_once && \ + if [ -n "$UPSLIST_FILE" ] ; then + echo "=== The currently defined configurations in '$UPSCONF' are:" >&2 + echo "$UPSLIST_FILE" + exit 0 + fi + echo "No devices detected in '$UPSCONF'" >&2 + exit 1 + ;; + --list-services) + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && \ + if [ -n "$UPSLIST_SVCS_RAW" ] ; then + echo "=== The currently defined service units are:" >&2 + echo "$UPSLIST_SVCS_RAW" + exit 0 + fi + echo "No service units detected" >&2 + exit 1 + ;; + --list-instances) + upslist_readSvcs "by user request" && \ + if [ -n "$UPSLIST_SVCS" ] ; then + echo "=== The currently defined service instances are:" >&2 + echo "$UPSLIST_SVCS" + exit 0 + fi + echo "No service instances detected" >&2 + exit 1 + ;; + --get-service-for-device) [ -z "$2" ] && echo "Device name argument required" >&2 && exit 1 + DEV="$2" + upslist_readSvcs "by user request" && [ -n "$UPSLIST_SVCS" ] \ + || { echo "No service instances detected" >&2 ; exit 1; } + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ -n "$UPSLIST_SVCS_RAW" ] \ + || { echo "No service units detected" >&2 ; exit 1; } + vINST="`$hook_validInstanceName "$DEV"`" + vUNITD="`$hook_validFullUnitName "$DEV"`" + vUNITI="`$hook_validFullUnitName "$vINST"`" + # First pass over simple verbatim names + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$DEV" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITD" ] ; then + echo "$UNIT" + exit 0 + fi + done + fi + done + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$vINST" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITI" ] ; then + echo "$UNIT" + exit 0 + fi + done + fi + done + echo "No service instances detected that match device '$2'" >&2 + exit 1 + ;; + --get-device-for-service) [ -z "$2" ] && echo "Service (instance) name argument required" >&2 && exit 1 + # Instance name can be a hash or "native" NUT section name + SVC="`$hook_validInstanceSuffixName "$2"`" + case "$SVC" in + MD5_*) ;; # fall through to the bulk of code + *) upslist_readFile_once || exit $? + echo "$UPSLIST_FILE" | egrep "^$SVC\$" + exit $? + ;; + esac + FINAL_RES=0 + OUT="`"$0" --list-services-for-devices`" && [ -n "$OUT" ] || FINAL_RES=$? + if [ "$FINAL_RES" = 0 ]; then + echo "$OUT" | grep "$SVC" | ( \ + while read _SVC _DEV ; do + _SVC="`$hook_validInstanceSuffixName "${_SVC}"`" || exit + [ "${_SVC}" = "${SVC}" ] && echo "$_DEV" && exit 0 + done ; exit 1 ) && exit 0 + fi + echo "No service instance '$2' was detected that matches a NUT device" >&2 + exit 1 + ;; + --list-services-for-devices) + FINAL_RES=0 + upslist_readFile_once && [ -n "$UPSLIST_FILE" ] \ + || { echo "No devices detected in '$UPSCONF'" >&2 ; exit 1 ; } + upslist_readSvcs "by user request" && [ -n "$UPSLIST_SVCS" ] \ + || { echo "No service instances detected" >&2 ; exit 1; } + UPSLIST_SVCS_RAW="`$hook_listInstances_raw`" && [ -n "$UPSLIST_SVCS_RAW" ] \ + || { echo "No service units detected" >&2 ; exit 1; } + for DEV in $UPSLIST_FILE ; do + vINST="`$hook_validInstanceName "$DEV"`" + vUNITD="`$hook_validFullUnitName "$DEV"`" + vUNITI="`$hook_validFullUnitName "$vINST"`" + # First pass over simple verbatim names + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$DEV" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITD" ] ; then + printf '%s\t%s\n' "$UNIT" "$DEV" + continue 3 + fi + done + fi + done + for INST in $UPSLIST_SVCS ; do + if [ "$INST" = "$vINST" ] ; then + for UNIT in $UPSLIST_SVCS_RAW ; do + if [ "$UNIT" = "$vUNITI" ] ; then + printf '%s\t%s\n' "$UNIT" "$DEV" + continue 3 + fi + done + fi + done + echo "WARNING: no service instances detected that match device '$DEV'" >&2 + FINAL_RES=1 + done + exit $FINAL_RES + ;; + --show-configs|--show-device-configs|--show-all-configs|--show-all-device-configs) + RES=0 + upslist_readFile_once || RES=$? + [ "$RES" != 0 ] && { echo "ERROR: upslist_readFile_once () failed with code $RES" >&2; exit $RES; } + [ -n "$UPSLIST_FILE" ] \ + || { echo "WARNING: No devices detected in '$UPSCONF'" >&2 ; RES=1 ; } + echo "$UPSCONF_DATA" + exit $RES + ;; + --show-config|--show-device-config) + [ -z "$2" ] && echo "WARNING: Device name argument empty, will show global config" >&2 + DEV="$2" + upsconf_getSection "$DEV" + exit $? + ;; + --show-config-value|--show-device-config-value) + [ -z "$3" ] && echo "At least one configuration key name argument is required" >&2 && exit 1 + [ -z "$2" ] && echo "WARNING: Device name argument empty, will show global config" >&2 + DEV="$2" + shift 2 + upsconf_getValue "$DEV" "$@" + exit $? + ;; + upsconf_debug) # Not public, not in usage() + [ -z "$2" ] && echo "Device name argument required" >&2 && exit 1 + upsconf_debug "$2" + exit $? + ;; + upslist_debug) # Not public, not in usage() + upslist_readFile_once || exit + upslist_debug + exit $? + ;; + *) echo "Unrecognized argument: $1" >&2 ; exit 1 ;; + esac + shift +done diff --git a/scripts/upsdrvsvcctl/upsdrvsvcctl b/scripts/upsdrvsvcctl/upsdrvsvcctl new file mode 100755 index 0000000..001ac42 --- /dev/null +++ b/scripts/upsdrvsvcctl/upsdrvsvcctl @@ -0,0 +1,196 @@ +#!/bin/sh +# +# Copyright (C) 2016-2018 Eaton +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +#! \file upsdrvsvcctl(.in) +# \author Jim Klimov +# \brief Manage NUT devices registered as service-unit instances +# + +if [ -z "${SERVICE_FRAMEWORK-}" ] ; then + [ -x /usr/sbin/svcadm ] && [ -x /usr/sbin/svccfg ] && [ -x /usr/bin/svcs ] && \ + SERVICE_FRAMEWORK="smf" + [ -z "${SERVICE_FRAMEWORK-}" ] && \ + [ -x /bin/systemctl ] && \ + SERVICE_FRAMEWORK="systemd" +fi + +VERB="" +CMD="" +CMDARG="" +ENUMERATOR="" +case "$SERVICE_FRAMEWORK" in + smf) CMD="/usr/sbin/svcadm" + ENUMERATOR="/usr/local/libexec/nut-driver-enumerator.sh" + ;; + systemd) CMD="/bin/systemctl" + ENUMERATOR="/usr/local/libexec/nut-driver-enumerator.sh" + ;; + *) echo "Unrecognized SERVICE_FRAMEWORK: $SERVICE_FRAMEWORK" >&2 ; exit ;; +esac + + +usage() { + # Note: version header differs from UPS_VERSION in binaries that + # might also have the git-version suffixed during build time + cat << EOF +Network UPS Tools - UPS driver controller ${PACKAGE_VERSION} +Starts and stops UPS drivers via system service instances, see +the $ENUMERATOR +script for more details. + +usage: $0 [OPTIONS] (start | stop | shutdown) [] + +Options: + -h display this help + -t testing mode - prints actions without doing them + -D raise debugging level + start start all UPS drivers in ups.conf + start only start driver for UPS + stop stop all UPS drivers in ups.conf + stop only stop driver for UPS + +Note: the "shutdown" options from original upsdrvctl are not currently +directly supported by this service management framework wrapper; instead +they are passed to the native upsdrvctl binary (your current user account +should have sufficient permissions to do that all): + shutdown shutdown all UPS drivers in ups.conf + shutdown only shutdown UPS + +usage: $0 [OPTIONS] resync + resync call $ENUMERATOR + to update the mapping of service instances for + NUT drivers to device sections in 'ups.conf' + +usage: $0 [OPTIONS] reconfigure + reconfigure call $ENUMERATOR + to remove and re-create the mapping of all service + instances for NUT drivers to device sections in + 'ups.conf' e.g. after a NUT package upgrade + +usage: $0 [OPTIONS] list [] + list call $ENUMERATOR + to list the mapping of service instances to device sections + list (optionally return the service instance name for one device) + +usage: $0 [OPTIONS] show-config [] + show-config output config section from ups.conf for device + show-config ...or all devices if no argument was passed +EOF +} + +ACTION="" +SVCINST="" +DRYRUN="" +DEBUG=0 +# Note: DEBUG is UNUSED_PARAM so far +while [ $# -gt 0 ]; do + case "$1" in + resync) eval $DRYRUN $ENUMERATOR ; exit $? ;; + reconf|reconfigure) eval $DRYRUN $ENUMERATOR --reconfigure ; exit $? ;; + list) + if [ -n "$2" ] ; then + eval $ENUMERATOR --get-service-for-device "$2" ; exit $? + else + eval $ENUMERATOR --list-services-for-devices ; exit $? + fi + ;; + show-config) + if [ -n "$2" ] ; then + eval $ENUMERATOR --show-device-config "$2" ; exit $? + else + eval $ENUMERATOR --show-all-configs ; exit $? + fi + ;; + start|stop) + ACTION="$1" + if [ -n "$2" ] ; then + SVCINST="`$ENUMERATOR --get-service-for-device "$2"`" || exit + shift + fi + ;; + shutdown) + echo "NOTE: Action '$1' is not implemented via services currently, will call upsdrvctl" >&2 + echo "Stopping the driver service instance(s) to release exclusive resources, if any..." >&2 + RES=0 + $0 stop $2 + /usr/local/sbin/upsdrvctl shutdown $2 || RES=$? + echo "Starting the driver service instance(s) so they can reconnect when the UPS returns..." >&2 + $0 start $2 + exit $RES + ;; + -t) DRYRUN="echo" ;; + -h) usage; exit 0 ;; + -D) DEBUG="`expr $DEBUG + 1`" ;; + -r|-u) echo "Option '$1 $2' is not implemented via services currently" >&2 ; shift;; + *) echo "Unrecognized argument: $1" >&2 ; exit ;; + esac + shift +done + +if [ -z "$ENUMERATOR" ] || [ ! -s "$ENUMERATOR" ] || [ ! -x "$ENUMERATOR" ] ; then + echo "ENUMERATOR script (nut-driver-enumerator.sh) not found!" >&2 + exit 1 +fi + +if [ -z "$ACTION" ]; then + echo "No action was requested!" >&2 + exit 1 +fi + +if [ -z "$SVCINST" ]; then + SVCINST="`$ENUMERATOR --list-services`" || exit +fi + +# TODO: Support shutdown of one or all UPSes by stopping its service +# and then calling the original upsdrvctl on it? +case "$ACTION" in + start) + VERB="Starting" + case "$SERVICE_FRAMEWORK" in + smf) CMDARG="enable -ts" ;; + systemd) CMDARG="start" ;; + esac + ;; + stop) + VERB="Stopping" + case "$SERVICE_FRAMEWORK" in + smf) CMDARG="disable -ts" ;; + systemd) CMDARG="stop" ;; + esac + ;; + *) echo "Unrecognized ACTION: $ACTION" >&2 ; exit ;; +esac + +for INST in $SVCINST ; do + echo "$VERB $INST ..." >&2 + $DRYRUN $CMD $CMDARG "$INST" & +done +wait + +case "$SERVICE_FRAMEWORK" in + smf) + sleep 1 + echo "Post-process clearing services that failed early..." >&2 + for INST in $SVCINST ; do + echo "Clearing $INST (if it got broken) ..." >&2 + $DRYRUN $CMD clear "$INST" & + done + ;; +esac + +wait diff --git a/scripts/upsdrvsvcctl/upsdrvsvcctl.in b/scripts/upsdrvsvcctl/upsdrvsvcctl.in new file mode 100755 index 0000000..d9e127c --- /dev/null +++ b/scripts/upsdrvsvcctl/upsdrvsvcctl.in @@ -0,0 +1,196 @@ +#!/bin/sh +# +# Copyright (C) 2016-2018 Eaton +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +#! \file upsdrvsvcctl(.in) +# \author Jim Klimov +# \brief Manage NUT devices registered as service-unit instances +# + +if [ -z "${SERVICE_FRAMEWORK-}" ] ; then + [ -x /usr/sbin/svcadm ] && [ -x /usr/sbin/svccfg ] && [ -x /usr/bin/svcs ] && \ + SERVICE_FRAMEWORK="smf" + [ -z "${SERVICE_FRAMEWORK-}" ] && \ + [ -x /bin/systemctl ] && \ + SERVICE_FRAMEWORK="systemd" +fi + +VERB="" +CMD="" +CMDARG="" +ENUMERATOR="" +case "$SERVICE_FRAMEWORK" in + smf) CMD="/usr/sbin/svcadm" + ENUMERATOR="@NUT_LIBEXECDIR@/nut-driver-enumerator.sh" + ;; + systemd) CMD="/bin/systemctl" + ENUMERATOR="@NUT_LIBEXECDIR@/nut-driver-enumerator.sh" + ;; + *) echo "Unrecognized SERVICE_FRAMEWORK: $SERVICE_FRAMEWORK" >&2 ; exit ;; +esac + + +usage() { + # Note: version header differs from UPS_VERSION in binaries that + # might also have the git-version suffixed during build time + cat << EOF +Network UPS Tools - UPS driver controller ${PACKAGE_VERSION} +Starts and stops UPS drivers via system service instances, see +the $ENUMERATOR +script for more details. + +usage: $0 [OPTIONS] (start | stop | shutdown) [] + +Options: + -h display this help + -t testing mode - prints actions without doing them + -D raise debugging level + start start all UPS drivers in ups.conf + start only start driver for UPS + stop stop all UPS drivers in ups.conf + stop only stop driver for UPS + +Note: the "shutdown" options from original upsdrvctl are not currently +directly supported by this service management framework wrapper; instead +they are passed to the native upsdrvctl binary (your current user account +should have sufficient permissions to do that all): + shutdown shutdown all UPS drivers in ups.conf + shutdown only shutdown UPS + +usage: $0 [OPTIONS] resync + resync call $ENUMERATOR + to update the mapping of service instances for + NUT drivers to device sections in 'ups.conf' + +usage: $0 [OPTIONS] reconfigure + reconfigure call $ENUMERATOR + to remove and re-create the mapping of all service + instances for NUT drivers to device sections in + 'ups.conf' e.g. after a NUT package upgrade + +usage: $0 [OPTIONS] list [] + list call $ENUMERATOR + to list the mapping of service instances to device sections + list (optionally return the service instance name for one device) + +usage: $0 [OPTIONS] show-config [] + show-config output config section from ups.conf for device + show-config ...or all devices if no argument was passed +EOF +} + +ACTION="" +SVCINST="" +DRYRUN="" +DEBUG=0 +# Note: DEBUG is UNUSED_PARAM so far +while [ $# -gt 0 ]; do + case "$1" in + resync) eval $DRYRUN $ENUMERATOR ; exit $? ;; + reconf|reconfigure) eval $DRYRUN $ENUMERATOR --reconfigure ; exit $? ;; + list) + if [ -n "$2" ] ; then + eval $ENUMERATOR --get-service-for-device "$2" ; exit $? + else + eval $ENUMERATOR --list-services-for-devices ; exit $? + fi + ;; + show-config) + if [ -n "$2" ] ; then + eval $ENUMERATOR --show-device-config "$2" ; exit $? + else + eval $ENUMERATOR --show-all-configs ; exit $? + fi + ;; + start|stop) + ACTION="$1" + if [ -n "$2" ] ; then + SVCINST="`$ENUMERATOR --get-service-for-device "$2"`" || exit + shift + fi + ;; + shutdown) + echo "NOTE: Action '$1' is not implemented via services currently, will call upsdrvctl" >&2 + echo "Stopping the driver service instance(s) to release exclusive resources, if any..." >&2 + RES=0 + $0 stop $2 + @SBINDIR@/upsdrvctl shutdown $2 || RES=$? + echo "Starting the driver service instance(s) so they can reconnect when the UPS returns..." >&2 + $0 start $2 + exit $RES + ;; + -t) DRYRUN="echo" ;; + -h) usage; exit 0 ;; + -D) DEBUG="`expr $DEBUG + 1`" ;; + -r|-u) echo "Option '$1 $2' is not implemented via services currently" >&2 ; shift;; + *) echo "Unrecognized argument: $1" >&2 ; exit ;; + esac + shift +done + +if [ -z "$ENUMERATOR" ] || [ ! -s "$ENUMERATOR" ] || [ ! -x "$ENUMERATOR" ] ; then + echo "ENUMERATOR script (nut-driver-enumerator.sh) not found!" >&2 + exit 1 +fi + +if [ -z "$ACTION" ]; then + echo "No action was requested!" >&2 + exit 1 +fi + +if [ -z "$SVCINST" ]; then + SVCINST="`$ENUMERATOR --list-services`" || exit +fi + +# TODO: Support shutdown of one or all UPSes by stopping its service +# and then calling the original upsdrvctl on it? +case "$ACTION" in + start) + VERB="Starting" + case "$SERVICE_FRAMEWORK" in + smf) CMDARG="enable -ts" ;; + systemd) CMDARG="start" ;; + esac + ;; + stop) + VERB="Stopping" + case "$SERVICE_FRAMEWORK" in + smf) CMDARG="disable -ts" ;; + systemd) CMDARG="stop" ;; + esac + ;; + *) echo "Unrecognized ACTION: $ACTION" >&2 ; exit ;; +esac + +for INST in $SVCINST ; do + echo "$VERB $INST ..." >&2 + $DRYRUN $CMD $CMDARG "$INST" & +done +wait + +case "$SERVICE_FRAMEWORK" in + smf) + sleep 1 + echo "Post-process clearing services that failed early..." >&2 + for INST in $SVCINST ; do + echo "Clearing $INST (if it got broken) ..." >&2 + $DRYRUN $CMD clear "$INST" & + done + ;; +esac + +wait diff --git a/server/Makefile.am b/server/Makefile.am index 5f2f679..1ab6393 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -1,5 +1,10 @@ # Network UPS Tools: server +# Make sure out-of-dir dependencies exist (especially when dev-building parts): +$(top_builddir)/common/libcommon.la \ +$(top_builddir)/common/libparseconf.la: dummy + @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + # Avoid per-target CFLAGS, because this will prevent re-use of object # files. In any case, CFLAGS are only -I options, so there is no harm, # but only add them if we really use the target. @@ -10,7 +15,7 @@ endif if WITH_SSL AM_CFLAGS += $(LIBSSL_CFLAGS) endif -LDADD = ../common/libcommon.la ../common/libparseconf.la $(NETLIBS) +LDADD = $(top_builddir)/common/libcommon.la $(top_builddir)/common/libparseconf.la $(NETLIBS) if WITH_WRAP LDADD += $(LIBWRAP_LIBS) endif @@ -21,10 +26,19 @@ endif sbin_PROGRAMS = upsd EXTRA_PROGRAMS = sockdebug -upsd_SOURCES = upsd.c user.c conf.c ssl.c sstate.c desc.c \ +upsd_SOURCES = upsd.c user.c conf.c netssl.c sstate.c desc.c \ netget.c netmisc.c netlist.c netuser.c netset.c netinstcmd.c \ conf.h nut_ctype.h desc.h netcmds.h neterr.h netget.h netinstcmd.h \ - netlist.h netmisc.h netset.h netuser.h ssl.h sstate.h stype.h upsd.h \ + netlist.h netmisc.h netset.h netuser.h netssl.h sstate.h stype.h upsd.h \ upstype.h user-data.h user.h sockdebug_SOURCES = sockdebug.c + +dummy: + +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +# NOTE: Do not clean ".deps" in SUBDIRS of the main project, +# the root Makefile.am takes care of that! +#clean-local: +# rm -rf $(builddir)/.deps diff --git a/server/Makefile.in b/server/Makefile.in index 0c32dea..bfb217c 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -18,6 +17,61 @@ # Network UPS Tools: server VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -44,32 +98,43 @@ target_triplet = @target@ sbin_PROGRAMS = upsd$(EXEEXT) EXTRA_PROGRAMS = sockdebug$(EXEEXT) subdir = server -DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = @@ -82,41 +147,98 @@ sockdebug_LDADD = $(LDADD) am__DEPENDENCIES_1 = @WITH_WRAP_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) @WITH_SSL_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) -sockdebug_DEPENDENCIES = ../common/libcommon.la \ - ../common/libparseconf.la $(am__DEPENDENCIES_1) \ +sockdebug_DEPENDENCIES = $(top_builddir)/common/libcommon.la \ + $(top_builddir)/common/libparseconf.la $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = am_upsd_OBJECTS = upsd.$(OBJEXT) user.$(OBJEXT) conf.$(OBJEXT) \ - ssl.$(OBJEXT) sstate.$(OBJEXT) desc.$(OBJEXT) netget.$(OBJEXT) \ - netmisc.$(OBJEXT) netlist.$(OBJEXT) netuser.$(OBJEXT) \ - netset.$(OBJEXT) netinstcmd.$(OBJEXT) + netssl.$(OBJEXT) sstate.$(OBJEXT) desc.$(OBJEXT) \ + netget.$(OBJEXT) netmisc.$(OBJEXT) netlist.$(OBJEXT) \ + netuser.$(OBJEXT) netset.$(OBJEXT) netinstcmd.$(OBJEXT) upsd_OBJECTS = $(am_upsd_OBJECTS) upsd_LDADD = $(LDADD) -upsd_DEPENDENCIES = ../common/libcommon.la ../common/libparseconf.la \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2) \ - $(am__DEPENDENCIES_3) +upsd_DEPENDENCIES = $(top_builddir)/common/libcommon.la \ + $(top_builddir)/common/libparseconf.la $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_3) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/conf.Po ./$(DEPDIR)/desc.Po \ + ./$(DEPDIR)/netget.Po ./$(DEPDIR)/netinstcmd.Po \ + ./$(DEPDIR)/netlist.Po ./$(DEPDIR)/netmisc.Po \ + ./$(DEPDIR)/netset.Po ./$(DEPDIR)/netssl.Po \ + ./$(DEPDIR)/netuser.Po ./$(DEPDIR)/sockdebug.Po \ + ./$(DEPDIR)/sstate.Po ./$(DEPDIR)/upsd.Po ./$(DEPDIR)/user.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = CCLD = $(CC) -LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ - $(LDFLAGS) -o $@ +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = SOURCES = $(sockdebug_SOURCES) $(upsd_SOURCES) DIST_SOURCES = $(sockdebug_SOURCES) $(upsd_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -127,16 +249,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -145,11 +276,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -159,14 +287,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -177,21 +306,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -205,35 +343,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -244,8 +393,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -264,18 +417,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -290,15 +446,17 @@ udevdir = @udevdir@ # files. In any case, CFLAGS are only -I options, so there is no harm, # but only add them if we really use the target. AM_CFLAGS = -I$(top_srcdir)/include $(am__append_1) $(am__append_2) -LDADD = ../common/libcommon.la ../common/libparseconf.la $(NETLIBS) \ +LDADD = $(top_builddir)/common/libcommon.la \ + $(top_builddir)/common/libparseconf.la $(NETLIBS) \ $(am__append_3) $(am__append_4) -upsd_SOURCES = upsd.c user.c conf.c ssl.c sstate.c desc.c \ +upsd_SOURCES = upsd.c user.c conf.c netssl.c sstate.c desc.c \ netget.c netmisc.c netlist.c netuser.c netset.c netinstcmd.c \ conf.h nut_ctype.h desc.h netcmds.h neterr.h netget.h netinstcmd.h \ - netlist.h netmisc.h netset.h netuser.h ssl.h sstate.h stype.h upsd.h \ + netlist.h netmisc.h netset.h netuser.h netssl.h sstate.h stype.h upsd.h \ upstype.h user-data.h user.h sockdebug_SOURCES = sockdebug.c +MAINTAINERCLEANFILES = Makefile.in .dirstamp all: all-am .SUFFIXES: @@ -315,14 +473,13 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu server/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu server/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -335,14 +492,19 @@ $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) $(am__aclocal_m4_deps): install-sbinPROGRAMS: $(sbin_PROGRAMS) @$(NORMAL_INSTALL) - test -z "$(sbindir)" || $(MKDIR_P) "$(DESTDIR)$(sbindir)" @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \ + fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ - while read p p1; do if test -f $$p || test -f $$p1; \ - then echo "$$p"; echo "$$p"; else :; fi; \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ - sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ @@ -363,7 +525,8 @@ uninstall-sbinPROGRAMS: @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ - -e 's/$$/$(EXEEXT)/' `; \ + -e 's/$$/$(EXEEXT)/' \ + `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(sbindir)" && rm -f $$files @@ -376,12 +539,14 @@ clean-sbinPROGRAMS: list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list -sockdebug$(EXEEXT): $(sockdebug_OBJECTS) $(sockdebug_DEPENDENCIES) + +sockdebug$(EXEEXT): $(sockdebug_OBJECTS) $(sockdebug_DEPENDENCIES) $(EXTRA_sockdebug_DEPENDENCIES) @rm -f sockdebug$(EXEEXT) - $(LINK) $(sockdebug_OBJECTS) $(sockdebug_LDADD) $(LIBS) -upsd$(EXEEXT): $(upsd_OBJECTS) $(upsd_DEPENDENCIES) + $(AM_V_CCLD)$(LINK) $(sockdebug_OBJECTS) $(sockdebug_LDADD) $(LIBS) + +upsd$(EXEEXT): $(upsd_OBJECTS) $(upsd_DEPENDENCIES) $(EXTRA_upsd_DEPENDENCIES) @rm -f upsd$(EXEEXT) - $(LINK) $(upsd_OBJECTS) $(upsd_LDADD) $(LIBS) + $(AM_V_CCLD)$(LINK) $(upsd_OBJECTS) $(upsd_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -389,40 +554,49 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/desc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netget.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netinstcmd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netlist.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netmisc.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netset.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netuser.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sockdebug.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ssl.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sstate.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsd.Po@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/conf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/desc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netget.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netinstcmd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netlist.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netmisc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netset.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netssl.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/netuser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sockdebug.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sstate.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upsd.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/user.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) .c.o: -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< mostlyclean-libtool: -rm -f *.lo @@ -430,26 +604,15 @@ mostlyclean-libtool: clean-libtool: -rm -rf .libs _libs -ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ - mkid -fID $$unique -tags: TAGS +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags -TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ + $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ @@ -461,15 +624,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $$unique; \ fi; \ fi -ctags: CTAGS -CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique @@ -478,11 +637,29 @@ GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -529,10 +706,15 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -544,13 +726,26 @@ distclean-generic: maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \ mostlyclean-am distclean: distclean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/conf.Po + -rm -f ./$(DEPDIR)/desc.Po + -rm -f ./$(DEPDIR)/netget.Po + -rm -f ./$(DEPDIR)/netinstcmd.Po + -rm -f ./$(DEPDIR)/netlist.Po + -rm -f ./$(DEPDIR)/netmisc.Po + -rm -f ./$(DEPDIR)/netset.Po + -rm -f ./$(DEPDIR)/netssl.Po + -rm -f ./$(DEPDIR)/netuser.Po + -rm -f ./$(DEPDIR)/sockdebug.Po + -rm -f ./$(DEPDIR)/sstate.Po + -rm -f ./$(DEPDIR)/upsd.Po + -rm -f ./$(DEPDIR)/user.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -596,7 +791,19 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/conf.Po + -rm -f ./$(DEPDIR)/desc.Po + -rm -f ./$(DEPDIR)/netget.Po + -rm -f ./$(DEPDIR)/netinstcmd.Po + -rm -f ./$(DEPDIR)/netlist.Po + -rm -f ./$(DEPDIR)/netmisc.Po + -rm -f ./$(DEPDIR)/netset.Po + -rm -f ./$(DEPDIR)/netssl.Po + -rm -f ./$(DEPDIR)/netuser.Po + -rm -f ./$(DEPDIR)/sockdebug.Po + -rm -f ./$(DEPDIR)/sstate.Po + -rm -f ./$(DEPDIR)/upsd.Po + -rm -f ./$(DEPDIR)/user.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -617,20 +824,34 @@ uninstall-am: uninstall-sbinPROGRAMS .MAKE: install-am install-strip -.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \ - clean-libtool clean-sbinPROGRAMS ctags distclean \ - distclean-compile distclean-generic distclean-libtool \ - distclean-tags distdir dvi dvi-am html html-am info info-am \ - install install-am install-data install-data-am install-dvi \ - install-dvi-am install-exec install-exec-am install-html \ - install-html-am install-info install-info-am install-man \ - install-pdf install-pdf-am install-ps install-ps-am \ - install-sbinPROGRAMS install-strip installcheck \ - installcheck-am installdirs maintainer-clean \ +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-generic clean-libtool clean-sbinPROGRAMS cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-sbinPROGRAMS install-strip \ + installcheck installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags uninstall uninstall-am uninstall-sbinPROGRAMS + tags tags-am uninstall uninstall-am uninstall-sbinPROGRAMS +.PRECIOUS: Makefile + + +# Make sure out-of-dir dependencies exist (especially when dev-building parts): +$(top_builddir)/common/libcommon.la \ +$(top_builddir)/common/libparseconf.la: dummy + @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +dummy: + +# NOTE: Do not clean ".deps" in SUBDIRS of the main project, +# the root Makefile.am takes care of that! +#clean-local: +# rm -rf $(builddir)/.deps # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/server/conf.c b/server/conf.c index 3e348e2..3dd2696 100644 --- a/server/conf.c +++ b/server/conf.c @@ -22,10 +22,20 @@ #include "upsconf.h" #include "sstate.h" #include "user.h" -#include "ssl.h" +#include "netssl.h" +#include "nut_stdint.h" +#include - ups_t *upstable = NULL; - int num_ups = 0; +static ups_t *upstable = NULL; +int num_ups = 0; + +/* Users can pass a -D[...] option to enable debugging. + * For the service tracing purposes, also the upsd.conf + * can define a debug_min value in the global section, + * to set the minimal debug level (CLI provided value less + * than that would not have effect, can only have more). + */ +int nut_debug_level_global = -1; /* add another UPS for monitoring from ups.conf */ static void ups_create(const char *fn, const char *name, const char *desc) @@ -110,25 +120,91 @@ static void ups_update(const char *fn, const char *name, const char *desc) /* always set this on reload */ temp->retain = 1; -} +} + +/* returns 1 if "arg" was usable as a boolean value, 0 if not + * saves converted meaning of "arg" into referenced "result" + */ +static int parse_boolean(char *arg, int *result) +{ + if ( (!strcasecmp(arg, "true")) || (!strcasecmp(arg, "on")) || (!strcasecmp(arg, "yes")) || (!strcasecmp(arg, "1"))) { + *result = 1; + return 1; + } + if ( (!strcasecmp(arg, "false")) || (!strcasecmp(arg, "off")) || (!strcasecmp(arg, "no")) || (!strcasecmp(arg, "0"))) { + *result = 0; + return 1; + } + return 0; +} /* return 1 if usable, 0 if not */ -static int parse_upsd_conf_args(int numargs, char **arg) +static int parse_upsd_conf_args(size_t numargs, char **arg) { /* everything below here uses up through arg[1] */ if (numargs < 2) return 0; + /* DEBUG_MIN (NUM) */ + /* debug_min (NUM) also acceptable, to be on par with ups.conf */ + if (!strcasecmp(arg[0], "DEBUG_MIN")) { + int lvl = -1; // typeof common/common.c: int nut_debug_level + if ( str_to_int (arg[1], &lvl, 10) && lvl >= 0 ) { + nut_debug_level_global = lvl; + } else { + upslogx(LOG_INFO, "DEBUG_MIN has non numeric or negative value in upsd.conf"); + } + return 1; + } + /* MAXAGE */ if (!strcmp(arg[0], "MAXAGE")) { - maxage = atoi(arg[1]); - return 1; + if (isdigit((size_t)arg[1][0])) { + maxage = atoi(arg[1]); + return 1; + } + else { + upslogx(LOG_ERR, "MAXAGE has non numeric value (%s)!", arg[1]); + return 0; + } + } + + /* TRACKINGDELAY */ + if (!strcmp(arg[0], "TRACKINGDELAY")) { + if (isdigit((size_t)arg[1][0])) { + tracking_delay = atoi(arg[1]); + return 1; + } + else { + upslogx(LOG_ERR, "TRACKINGDELAY has non numeric value (%s)!", arg[1]); + return 0; + } + } + + /* ALLOW_NO_DEVICE */ + if (!strcmp(arg[0], "ALLOW_NO_DEVICE")) { + if (isdigit((size_t)arg[1][0])) { + allow_no_device = (atoi(arg[1]) != 0); /* non-zero arg is true here */ + return 1; + } + if (parse_boolean(arg[1], &allow_no_device)) + return 1; + + upslogx(LOG_ERR, "ALLOW_NO_DEVICE has non numeric and non boolean value (%s)!", arg[1]); + return 0; } /* MAXCONN */ if (!strcmp(arg[0], "MAXCONN")) { - maxconn = atoi(arg[1]); - return 1; + if (isdigit((size_t)arg[1][0])) { + /* FIXME: Check for overflows (and int size of nfds_t vs. long) - see get_max_pid_t() for example */ + maxconn = (nfds_t)atol(arg[1]); + return 1; + } + else { + upslogx(LOG_ERR, "MAXCONN has non numeric value (%s)!", arg[1]); + return 0; + } } /* STATEPATH */ @@ -145,12 +221,45 @@ static int parse_upsd_conf_args(int numargs, char **arg) return 1; } +#ifdef WITH_OPENSSL /* CERTFILE */ if (!strcmp(arg[0], "CERTFILE")) { free(certfile); certfile = xstrdup(arg[1]); return 1; } +#elif (defined WITH_NSS) /* WITH_OPENSSL */ + /* CERTPATH */ + if (!strcmp(arg[0], "CERTPATH")) { + free(certfile); + certfile = xstrdup(arg[1]); + return 1; + } +#ifdef WITH_CLIENT_CERTIFICATE_VALIDATION + /* CERTREQUEST (0 | 1 | 2) */ + if (!strcmp(arg[0], "CERTREQUEST")) { + if (isdigit((size_t)arg[1][0])) { + certrequest = atoi(arg[1]); + return 1; + } + else { + upslogx(LOG_ERR, "CERTREQUEST has non numeric value (%s)!", arg[1]); + return 0; + } + } +#endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */ +#endif /* WITH_OPENSSL | WITH_NSS */ + +#if defined(WITH_OPENSSL) || defined(WITH_NSS) + /* DISABLE_WEAK_SSL */ + if (!strcmp(arg[0], "DISABLE_WEAK_SSL")) { + if (parse_boolean(arg[1], &disable_weak_ssl)) + return 1; + + upslogx(LOG_ERR, "DISABLE_WEAK_SSL has non boolean value (%s)!", arg[1]); + return 0; + } +#endif /* WITH_OPENSSL | WITH_NSS */ /* ACCEPT [...] */ if (!strcmp(arg[0], "ACCEPT")) { @@ -183,6 +292,17 @@ static int parse_upsd_conf_args(int numargs, char **arg) return 1; } +#ifdef WITH_NSS + /* CERTIDENT */ + if (!strcmp(arg[0], "CERTIDENT")) { + free(certname); + certname = xstrdup(arg[1]); + free(certpasswd); + certpasswd = xstrdup(arg[2]); + return 1; + } +#endif /* WITH_NSS */ + /* not recognized */ return 0; } @@ -214,6 +334,13 @@ void load_upsdconf(int reloading) return; } + if (reloading) { + /* if upsd.conf added or changed + * (or commented away) the debug_min + * setting, detect that */ + nut_debug_level_global = -1; + } + while (pconf_file_next(&ctx)) { if (pconf_parse_error(&ctx)) { upslogx(LOG_ERR, "Parse error: %s:%d: %s", @@ -228,11 +355,11 @@ void load_upsdconf(int reloading) unsigned int i; char errmsg[SMALLBUF]; - snprintf(errmsg, sizeof(errmsg), + snprintf(errmsg, sizeof(errmsg), "upsd.conf: invalid directive"); for (i = 0; i < ctx.numargs; i++) - snprintfcat(errmsg, sizeof(errmsg), " %s", + snprintfcat(errmsg, sizeof(errmsg), " %s", ctx.arglist[i]); upslogx(LOG_WARNING, "%s", errmsg); @@ -240,7 +367,16 @@ void load_upsdconf(int reloading) } - pconf_finish(&ctx); + if (reloading) { + if (nut_debug_level_global > -1) { + upslogx(LOG_INFO, + "Applying debug_min=%d from upsd.conf", + nut_debug_level_global); + nut_debug_level = nut_debug_level_global; + } + } + + pconf_finish(&ctx); } /* callback during parsing of ups.conf */ @@ -304,7 +440,7 @@ void upsconf_add(int reloading) /* don't accept an entry that's missing items */ if ((!tmp->driver) || (!tmp->port)) { - upslogx(LOG_WARNING, "Warning: ignoring incomplete configuration for UPS [%s]\n", + upslogx(LOG_WARNING, "Warning: ignoring incomplete configuration for UPS [%s]\n", tmp->upsname); } else { snprintf(statefn, sizeof(statefn), "%s-%s", @@ -377,7 +513,7 @@ static void delete_ups(upstype_t *target) /* shouldn't happen */ upslogx(LOG_ERR, "delete_ups: UPS not found"); -} +} /* see if we can open a file */ static int check_file(const char *fn) diff --git a/server/conf.h b/server/conf.h index 4663368..432b7f9 100644 --- a/server/conf.h +++ b/server/conf.h @@ -3,6 +3,7 @@ Copyright (C) 2001 Russell Kroll 2008 Arjen de Korte + 2020 Jim Klimov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,6 +20,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef NUT_CONF_H_SEEN +#define NUT_CONF_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + /* read upsd.conf */ void load_upsdconf(int reloading); @@ -40,4 +50,13 @@ typedef struct ups_s { void delete_acls(void); void delete_access(void); - extern int num_ups; +extern int num_ups; +extern int nut_debug_level_global; + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_CONF_H_SEEN */ diff --git a/server/desc.c b/server/desc.c index b74df72..e6b08f0 100644 --- a/server/desc.c +++ b/server/desc.c @@ -17,6 +17,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" /* must be the first header */ + #include #include "common.h" diff --git a/server/desc.h b/server/desc.h index 5343056..a574831 100644 --- a/server/desc.h +++ b/server/desc.h @@ -1,4 +1,44 @@ +/* desc.h - variable/command description handling for upsd + + Copyright (C) + 2003 Russell Kroll + 2005 Arnaud Quette + 2013 Emilien Kia + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_DESC_H_SEEN +#define NUT_DESC_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + void desc_load(void); void desc_free(void); const char *desc_get_cmd(const char *name); const char *desc_get_var(const char *name); + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_DESC_H_SEEN */ diff --git a/server/netcmds.h b/server/netcmds.h index 30f5124..6a5a632 100644 --- a/server/netcmds.h +++ b/server/netcmds.h @@ -1,6 +1,11 @@ /* netcmds.h - upsd support structure details Copyright (C) 2001 Russell Kroll + 2005 Arnaud Quette + 2007 Peter Selinger + 2010 Arjen de Korte + 2012 Emilien Kia + 2020 Jim Klimov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,9 +22,12 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef NUT_NETCMDS_H_SEEN +#define NUT_NETCMDS_H_SEEN 1 + #include "nut_ctype.h" -#include "ssl.h" +#include "netssl.h" #include "netget.h" #include "netset.h" #include "netlist.h" @@ -29,12 +37,20 @@ #define FLAG_USER 0x0001 /* username and password must be set */ -struct { +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +static struct { const char *name; - void (*func)(nut_ctype_t *client, int numargs, const char **arg); + void (*func)(nut_ctype_t *client, size_t numargs, const char **arg); int flags; } netcmds[] = { { "VER", net_ver, 0 }, + { "NETVER", net_netver, 0 }, + { "PROTVER", net_netver, 0 }, /* aliased since NUT 2.8.0 */ { "HELP", net_help, 0 }, { "STARTTLS", net_starttls, 0 }, @@ -46,6 +62,10 @@ struct { { "LOGIN", net_login, FLAG_USER }, { "LOGOUT", net_logout, 0 }, + /* NOTE: Protocol in NUT 2.8.0 allows to handle + * master/primary to rename/alias the routine. + */ + { "PRIMARY", net_primary, FLAG_USER }, { "MASTER", net_master, FLAG_USER }, { "FSD", net_fsd, FLAG_USER }, @@ -53,5 +73,13 @@ struct { { "SET", net_set, FLAG_USER }, { "INSTCMD", net_instcmd, FLAG_USER }, - { NULL, (void(*)())(NULL), 0 } + { NULL, (void(*)(struct nut_ctype_s *, size_t, const char **))(NULL), 0 } }; + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_NETCMDS_H_SEEN */ diff --git a/server/neterr.h b/server/neterr.h index 0be8b83..3891452 100644 --- a/server/neterr.h +++ b/server/neterr.h @@ -1,3 +1,35 @@ +/* neterr.h - network error definitions for NUT + + Copyright (C) + 2003 Russell Kroll + 2005 Arnaud Quette + 2013 Emilien Kia + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_NETERR_H_SEEN +#define NUT_NETERR_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + /* network error definitions for consistency */ #define NUT_ERR_ACCESS_DENIED "ACCESS-DENIED" @@ -33,3 +65,11 @@ #define NUT_ERR_UNKNOWN_INSTCMD "UNKNOWN-INSTCMD" #define NUT_ERR_MISSING_ARGUMENT "MISSING-ARGUMENT" #define NUT_ERR_INVALID_VALUE "INVALID-VALUE" + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_NETERR_H_SEEN */ diff --git a/server/netget.c b/server/netget.c index 43e7370..a5f822a 100644 --- a/server/netget.c +++ b/server/netget.c @@ -56,9 +56,6 @@ static void get_upsdesc(nut_ctype_t *client, const char *upsname) return; } - if (!ups_available(ups, client)) - return; - if (ups->desc) { pconf_encode(ups->desc, esc, sizeof(esc)); sendback(client, "UPSDESC %s \"%s\"\n", upsname, esc); @@ -112,7 +109,7 @@ static void get_cmddesc(nut_ctype_t *client, const char *upsname, const char *cm if (desc) sendback(client, "CMDDESC %s %s \"%s\"\n", upsname, cmd, desc); else - sendback(client, "CMDDESC %s %s \"Description unavailable\"\n", + sendback(client, "CMDDESC %s %s \"Description unavailable\"\n", upsname, cmd); } @@ -145,32 +142,36 @@ static void get_type(nut_ctype_t *client, const char *upsname, const char *var) snprintfcat(buf, sizeof(buf), " RW"); if (node->enum_list) { - sendback(client, "%s ENUM\n", buf); - return; + snprintfcat(buf, sizeof(buf), " ENUM"); + } + + if (node->range_list) { + snprintfcat(buf, sizeof(buf), " RANGE"); } if (node->flags & ST_FLAG_STRING) { - sendback(client, "%s STRING:%d\n", buf, node->aux); + sendback(client, "%s STRING:%ld\n", buf, node->aux); return; } - /* hmm... */ + /* Any variable that is not string | range | enum is just a simple + * numeric value */ - sendback(client, "TYPE %s %s UNKNOWN\n", upsname, var); -} + sendback(client, "%s NUMBER\n", buf); +} static void get_var_server(nut_ctype_t *client, const char *upsname, const char *var) { if (!strcasecmp(var, "server.info")) { sendback(client, "VAR %s server.info " "\"Network UPS Tools upsd %s - " - "http://www.networkupstools.org/\"\n", + "http://www.networkupstools.org/\"\n", upsname, UPS_VERSION); return; } if (!strcasecmp(var, "server.version")) { - sendback(client, "VAR %s server.version \"%s\"\n", + sendback(client, "VAR %s server.version \"%s\"\n", upsname, UPS_VERSION); return; } @@ -213,8 +214,27 @@ static void get_var(nut_ctype_t *client, const char *upsname, const char *var) sendback(client, "VAR %s %s \"%s\"\n", upsname, var, val); } -void net_get(nut_ctype_t *client, int numarg, const char **arg) +void net_get(nut_ctype_t *client, size_t numarg, const char **arg) { + if (numarg < 1) { + send_err(client, NUT_ERR_INVALID_ARGUMENT); + return; + } + + /* GET TRACKING [ID] */ + if (!strcasecmp(arg[0], "TRACKING")) { + if (numarg < 2) { + sendback(client, "%s\n", (client->tracking) ? "ON" : "OFF"); + } + else { + if (client->tracking) + sendback(client, "%s\n", tracking_get(arg[1])); + else + send_err(client, NUT_ERR_FEATURE_NOT_CONFIGURED); + } + return; + } + if (numarg < 2) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; diff --git a/server/netget.h b/server/netget.h index f1209d2..4ccfd7b 100644 --- a/server/netget.h +++ b/server/netget.h @@ -1 +1,42 @@ -void net_get(nut_ctype_t *client, int numarg, const char **arg); +/* netget.h - GET handlers for upsd + + Copyright (C) + 2003 Russell Kroll + 2005 Arnaud Quette + 2007 Peter Selinger + 2013 Emilien Kia + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_NETGET_H_SEEN +#define NUT_NETGET_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +void net_get(nut_ctype_t *client, size_t numarg, const char **arg); + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_NETGET_H_SEEN */ diff --git a/server/netinstcmd.c b/server/netinstcmd.c index c21d7ec..00923c4 100644 --- a/server/netinstcmd.c +++ b/server/netinstcmd.c @@ -28,10 +28,10 @@ #include "netinstcmd.h" -static void send_instcmd(nut_ctype_t *client, const char *upsname, - const char *cmdname, const char *value) +static void send_instcmd(nut_ctype_t *client, const char *upsname, + const char *cmdname, const char *value, const char *tracking_id) { - int found; + int found, have_tracking_id = 0; upstype_t *ups; const cmdlist_t *ctmp; char sockcmd[SMALLBUF], esc[SMALLBUF]; @@ -70,20 +70,30 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, return; } + /* Format the base command */ + snprintf(sockcmd, sizeof(sockcmd), "INSTCMD %s", cmdname); + /* see if the user has also passed a value for this command */ - if (value != NULL) { - upslogx(LOG_INFO, "Instant command: %s@%s did %s with value \"%s\" on %s", - client->username, client->addr, cmdname, value, ups->name); + if (value != NULL) + snprintfcat(sockcmd, sizeof(sockcmd), " %s", pconf_encode(value, esc, sizeof(esc))); - snprintf(sockcmd, sizeof(sockcmd), "INSTCMD %s %s\n", - cmdname, pconf_encode(value, esc, sizeof(esc))); + /* see if the user want execution tracking for this command */ + if (tracking_id && *tracking_id) { + snprintfcat(sockcmd, sizeof(sockcmd), " TRACKING %s", tracking_id); + /* Add an entry in the tracking structure */ + tracking_add(tracking_id); + have_tracking_id = 1; } - else { - upslogx(LOG_INFO, "Instant command: %s@%s did %s on %s", - client->username, client->addr, cmdname, ups->name); - snprintf(sockcmd, sizeof(sockcmd), "INSTCMD %s\n", cmdname); - } + /* add EOL */ + snprintfcat(sockcmd, sizeof(sockcmd), "\n"); + + upslogx(LOG_INFO, "Instant command: %s@%s did %s%s%s on %s (tracking ID: %s)", + client->username, client->addr, cmdname, + (value != NULL)?" with value ":"", + (value != NULL)?value:"", + ups->name, + (have_tracking_id) ? tracking_id : "disabled"); if (!sstate_sendline(ups, sockcmd)) { upslogx(LOG_INFO, "Set command send failed"); @@ -91,18 +101,38 @@ static void send_instcmd(nut_ctype_t *client, const char *upsname, return; } - /* FIXME: need to retrieve the cookie number */ - sendback(client, "OK\n"); + /* return the result, possibly including tracking_id */ + if (have_tracking_id) + sendback(client, "OK TRACKING %s\n", tracking_id); + else + sendback(client, "OK\n"); } -void net_instcmd(nut_ctype_t *client, int numarg, const char **arg) +void net_instcmd(nut_ctype_t *client, size_t numarg, const char **arg) { + const char *devname = NULL; + const char *cmdname = NULL; + const char *cmdparam = NULL; + char tracking_id[UUID4_LEN] = ""; + if (numarg < 2) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } - - /* INSTCMD []*/ - send_instcmd(client, arg[0], arg[1], (numarg == 3)?arg[2]:NULL); + + /* INSTCMD [cmdparam] */ + /* Check arguments */ + devname = arg[0]; + cmdname = arg[1]; + if (numarg == 3) + cmdparam = arg[2]; + + if (client->tracking) { + /* Generate a tracking ID, if client requested status tracking */ + nut_uuid_v4(tracking_id); + } + + send_instcmd(client, devname, cmdname, cmdparam, tracking_id); + return; } diff --git a/server/netinstcmd.h b/server/netinstcmd.h index 1015931..2814ecb 100644 --- a/server/netinstcmd.h +++ b/server/netinstcmd.h @@ -1 +1,42 @@ -void net_instcmd(nut_ctype_t *client, int numarg, const char **arg); +/* netinstcmd.h - network instand command definitions for NUT + + Copyright (C) + 2003 Russell Kroll + 2005 Arnaud Quette + 2007 Peter Selinger + 2013 Emilien Kia + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_NETINSTCMD_H_SEEN +#define NUT_NETINSTCMD_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +void net_instcmd(nut_ctype_t *client, size_t numarg, const char **arg); + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_NETINSTCMD_H_SEEN */ diff --git a/server/netlist.c b/server/netlist.c index ea6ee4c..d480eb1 100644 --- a/server/netlist.c +++ b/server/netlist.c @@ -1,6 +1,8 @@ /* netlist.c - LIST handlers for upsd - Copyright (C) 2003 Russell Kroll + Copyright (C) + 2003 Russell Kroll + 2012 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,7 +28,8 @@ #include "netlist.h" - extern upstype_t *firstups; /* for list_ups */ +extern upstype_t *firstups; /* for list_ups */ +extern nut_ctype_t *firstclient; /* for list_clients */ static int tree_dump(st_tree_t *node, nut_ctype_t *client, const char *ups, int rw, int fsd) @@ -49,7 +52,7 @@ static int tree_dump(st_tree_t *node, nut_ctype_t *client, const char *ups, if (node->flags & ST_FLAG_RW) { ret = sendback(client, "RW %s %s \"%s\"\n", ups, node->var, node->val); - + } else { ret = 1; /* dummy */ } @@ -185,6 +188,41 @@ static void list_enum(nut_ctype_t *client, const char *upsname, const char *var) sendback(client, "END LIST ENUM %s %s\n", upsname, var); } +static void list_range(nut_ctype_t *client, const char *upsname, const char *var) +{ + const upstype_t *ups; + const st_tree_t *node; + const range_t *rtmp; + + ups = get_ups_ptr(upsname); + + if (!ups) { + send_err(client, NUT_ERR_UNKNOWN_UPS); + return; + } + + if (!ups_available(ups, client)) + return; + + node = sstate_getnode(ups, var); + + if (!node) { + send_err(client, NUT_ERR_VAR_NOT_SUPPORTED); + return; + } + + if (!sendback(client, "BEGIN LIST RANGE %s %s\n", upsname, var)) + return; + + for (rtmp = node->range_list; rtmp != NULL; rtmp = rtmp->next) { + if (!sendback(client, "RANGE %s %s \"%i\" \"%i\"\n", + upsname, var, rtmp->min, rtmp->max)) + return; + } + + sendback(client, "END LIST RANGE %s %s\n", upsname, var); +} + static void list_ups(nut_ctype_t *client) { upstype_t *utmp; @@ -202,7 +240,7 @@ static void list_ups(nut_ctype_t *client) pconf_encode(utmp->desc, esc, sizeof(esc)); ret = sendback(client, "UPS %s \"%s\"\n", utmp->name, esc); - + } else { ret = sendback(client, "UPS %s \"Description unavailable\"\n", utmp->name); @@ -215,9 +253,39 @@ static void list_ups(nut_ctype_t *client) } sendback(client, "END LIST UPS\n"); -} +} -void net_list(nut_ctype_t *client, int numarg, const char **arg) +static void list_clients(nut_ctype_t *client, const char *upsname) +{ + const upstype_t *ups; + nut_ctype_t *c, *cnext; + + ups = get_ups_ptr(upsname); + + if (!ups) { + send_err(client, NUT_ERR_UNKNOWN_UPS); + return; + } + + if (!sendback(client, "BEGIN LIST CLIENT %s\n", upsname)) + return; + + if (firstclient) { + int ret; + /* show connected clients */ + for (c = firstclient; c; c = cnext) { + if (c->loginups && (!ups || !strcasecmp(c->loginups, ups->name))) { + ret = sendback(client, "CLIENT %s %s\n", c->loginups, c->addr); + if (!ret) + return; + } + cnext = c->next; + } + } + sendback(client, "END LIST CLIENT %s\n", upsname); +} + +void net_list(nut_ctype_t *client, size_t numarg, const char **arg) { if (numarg < 1) { send_err(client, NUT_ERR_INVALID_ARGUMENT); @@ -253,6 +321,12 @@ void net_list(nut_ctype_t *client, int numarg, const char **arg) return; } + /* LIST CLIENT UPS */ + if (!strcasecmp(arg[0], "CLIENT")) { + list_clients(client, arg[1]); + return; + } + if (numarg < 3) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; @@ -264,5 +338,11 @@ void net_list(nut_ctype_t *client, int numarg, const char **arg) return; } + /* LIST RANGE UPS VARNAME */ + if (!strcasecmp(arg[0], "RANGE")) { + list_range(client, arg[1], arg[2]); + return; + } + send_err(client, NUT_ERR_INVALID_ARGUMENT); } diff --git a/server/netlist.h b/server/netlist.h index d3b7776..072dc94 100644 --- a/server/netlist.h +++ b/server/netlist.h @@ -1 +1,42 @@ -void net_list(nut_ctype_t *client, int numarg, const char **arg); +/* netlist.h - LIST handlers for upsd + + Copyright (C) + 2003 Russell Kroll + 2005 Arnaud Quette + 2007 Peter Selinger + 2013 Emilien Kia + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_NETLIST_H_SEEN +#define NUT_NETLIST_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +void net_list(nut_ctype_t *client, size_t numarg, const char **arg); + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_NETLIST_H_SEEN */ diff --git a/server/netmisc.c b/server/netmisc.c index cbfb1ca..106d828 100644 --- a/server/netmisc.c +++ b/server/netmisc.c @@ -1,6 +1,8 @@ /* netmisc.c - miscellaneous network handlers for upsd (VER, HELP, FSD) - Copyright (C) 2003 Russell Kroll + Copyright (C) + 2003 Russell Kroll + 2012 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,15 +22,16 @@ #include "common.h" #include "upsd.h" -#include "sstate.h" +#include "sstate.h" #include "state.h" #include "user.h" /* for user_checkaction */ #include "neterr.h" #include "netmisc.h" -void net_ver(nut_ctype_t *client, int numarg, const char **arg) +void net_ver(nut_ctype_t *client, size_t numarg, const char **arg) { + NUT_UNUSED_VARIABLE(arg); if (numarg != 0) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; @@ -38,8 +41,20 @@ void net_ver(nut_ctype_t *client, int numarg, const char **arg) UPS_VERSION); } -void net_help(nut_ctype_t *client, int numarg, const char **arg) +void net_netver(nut_ctype_t *client, size_t numarg, const char **arg) { + NUT_UNUSED_VARIABLE(arg); + if (numarg != 0) { + send_err(client, NUT_ERR_INVALID_ARGUMENT); + return; + } + + sendback(client, "%s\n", NUT_NETVERSION); +} + +void net_help(nut_ctype_t *client, size_t numarg, const char **arg) +{ + NUT_UNUSED_VARIABLE(arg); if (numarg != 0) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; @@ -49,7 +64,7 @@ void net_help(nut_ctype_t *client, int numarg, const char **arg) " USERNAME PASSWORD STARTTLS\n"); } -void net_fsd(nut_ctype_t *client, int numarg, const char **arg) +void net_fsd(nut_ctype_t *client, size_t numarg, const char **arg) { upstype_t *ups; @@ -65,13 +80,13 @@ void net_fsd(nut_ctype_t *client, int numarg, const char **arg) return; } - /* make sure this user is allowed to do FSD */ + /* make sure this user is allowed to do FSD */ if (!user_checkaction(client->username, client->password, "FSD")) { send_err(client, NUT_ERR_ACCESS_DENIED); return; } - upslogx(LOG_INFO, "Client %s@%s set FSD on UPS [%s]", + upslogx(LOG_INFO, "Client %s@%s set FSD on UPS [%s]", client->username, client->addr, ups->name); ups->fsd = 1; diff --git a/server/netmisc.h b/server/netmisc.h index e1899df..8513190 100644 --- a/server/netmisc.h +++ b/server/netmisc.h @@ -1,3 +1,45 @@ -void net_ver(nut_ctype_t *client, int numarg, const char **arg); -void net_help(nut_ctype_t *client, int numarg, const char **arg); -void net_fsd(nut_ctype_t *client, int numarg, const char **arg); +/* netmisc.h - miscellaneous network handlers for upsd (VER, HELP, FSD) + + Copyright (C) + 2003 Russell Kroll + 2007 Peter Selinger + 2012 Arnaud Quette + 2013 Emilien Kia + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_NETMISC_H_SEEN +#define NUT_NETMISC_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +void net_ver(nut_ctype_t *client, size_t numarg, const char **arg); +void net_netver(nut_ctype_t *client, size_t numarg, const char **arg); +void net_help(nut_ctype_t *client, size_t numarg, const char **arg); +void net_fsd(nut_ctype_t *client, size_t numarg, const char **arg); + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_NETMISC_H_SEEN */ diff --git a/server/netset.c b/server/netset.c index 145bd2f..1e185b8 100644 --- a/server/netset.c +++ b/server/netset.c @@ -28,12 +28,14 @@ #include "netset.h" static void set_var(nut_ctype_t *client, const char *upsname, const char *var, - const char *newval) + const char *newval, const char *tracking_id) { upstype_t *ups; const char *val; const enum_t *etmp; + const range_t *rtmp; char cmd[SMALLBUF], esc[SMALLBUF]; + int have_tracking_id = 0; ups = get_ups_ptr(upsname); @@ -67,7 +69,7 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, /* see if the new value is allowed for this variable */ if (sstate_getflags(ups, var) & ST_FLAG_STRING) { - int aux; + long aux; aux = sstate_getaux(ups, var); @@ -80,6 +82,10 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, return; } + /* FIXME? Should this cast to "long"? + * An int-size string is quite a lot already, + * even on architectures with a moderate INTMAX + */ if (aux < (int) strlen(newval)) { send_err(client, NUT_ERR_TOO_LONG); return; @@ -108,33 +114,117 @@ static void set_var(nut_ctype_t *client, const char *upsname, const char *var, } } + /* or if it's within a range */ + + rtmp = sstate_getrangelist(ups, var); + + if (rtmp) { + int found = 0; + int inewval = atoi(newval); + + while (rtmp) { + if ((inewval >= rtmp->min) && (inewval <= rtmp->max)) { + found = 1; + break; + } + + rtmp = rtmp->next; + } + + if (!found) { + send_err(client, NUT_ERR_INVALID_VALUE); + return; + } + } + /* must be OK now */ - upslogx(LOG_INFO, "Set variable: %s@%s set %s on %s to %s", - client->username, client->addr, var, ups->name, newval); - - snprintf(cmd, sizeof(cmd), "SET %s \"%s\"\n", + snprintf(cmd, sizeof(cmd), "SET %s \"%s\"", var, pconf_encode(newval, esc, sizeof(esc))); + /* see if the user want execution tracking for this command */ + if (tracking_id && *tracking_id) { + snprintfcat(cmd, sizeof(cmd), " TRACKING %s", tracking_id); + /* Add an entry in the tracking structure */ + tracking_add(tracking_id); + have_tracking_id = 1; + } + + /* add EOL */ + snprintfcat(cmd, sizeof(cmd), "\n"); + + upslogx(LOG_INFO, "Set variable: %s@%s set %s on %s to %s (tracking ID: %s)", + client->username, client->addr, var, ups->name, newval, + (have_tracking_id) ? tracking_id : "disabled"); + if (!sstate_sendline(ups, cmd)) { upslogx(LOG_INFO, "Set command send failed"); send_err(client, NUT_ERR_SET_FAILED); return; } - sendback(client, "OK\n"); + /* return the result, possibly including tracking_id */ + if (have_tracking_id) + sendback(client, "OK TRACKING %s\n", tracking_id); + else + sendback(client, "OK\n"); } -void net_set(nut_ctype_t *client, int numarg, const char **arg) +void net_set(nut_ctype_t *client, size_t numarg, const char **arg) { - if (numarg < 4) { + char tracking_id[UUID4_LEN] = ""; + + /* Base verification, to ensure that we have at least the SET parameter */ + if (numarg < 2) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; } /* SET VAR UPS VARNAME VALUE */ if (!strcasecmp(arg[0], "VAR")) { - set_var(client, arg[1], arg[2], arg[3]); + if (numarg < 4) { + send_err(client, NUT_ERR_INVALID_ARGUMENT); + return; + } + + if (client->tracking) { + /* Generate a tracking ID, if client requested status tracking */ + nut_uuid_v4(tracking_id); + } + + set_var(client, arg[1], arg[2], arg[3], tracking_id); + + return; + } + + /* SET TRACKING VALUE */ + if (!strcasecmp(arg[0], "TRACKING")) { + if (!strcasecmp(arg[1], "ON")) { + /* general enablement along with for this client */ + client->tracking = tracking_enable(); + } + else if (!strcasecmp(arg[1], "OFF")) { + /* disable status tracking for this client first */ + client->tracking = 0; + /* then only disable the general one if no other clients use it! + * Note: don't call tracking_free() since we want info to + * persist, and tracking_cleanup() takes care of cleaning */ + if (tracking_disable()) { + upsdebugx(2, "%s: TRACKING disabled for one client, more remain.", __func__); + } else { + upsdebugx(2, "%s: TRACKING disabled for last client.", __func__); + } + } + else { + send_err(client, NUT_ERR_INVALID_ARGUMENT); + return; + } + upsdebugx(1, "%s: TRACKING general %s, client %s.", __func__, + tracking_is_enabled() ? "enabled" : "disabled", + client->tracking ? "enabled" : "disabled"); + + sendback(client, "OK\n"); + return; } diff --git a/server/netset.h b/server/netset.h index 81068dd..1306126 100644 --- a/server/netset.h +++ b/server/netset.h @@ -1 +1,42 @@ -void net_set(nut_ctype_t *client, int numarg, const char **arg); +/* netset.h - SET handler for upsd + + Copyright (C) + 2003 Russell Kroll + 2005 Arnaud Quette + 2007 Peter Selinger + 2013 Emilien Kia + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_NETSET_H_SEEN +#define NUT_NETSET_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +void net_set(nut_ctype_t *client, size_t numarg, const char **arg); + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_NETSET_H_SEEN */ diff --git a/server/netssl.c b/server/netssl.c new file mode 100644 index 0000000..6360117 --- /dev/null +++ b/server/netssl.c @@ -0,0 +1,735 @@ +/* netssl.c - Interface to OpenSSL for upsd + + Copyright (C) + 2002 Russell Kroll + 2008 Arjen de Korte + + based on the original implementation: + + Copyright (C) 2002 Technorama Ltd. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include + +#include "upsd.h" +#include "neterr.h" +#include "netssl.h" +#include "nut_stdint.h" + +#ifdef WITH_NSS + #include + #include + #include +#if defined(NSS_VMAJOR) && (NSS_VMAJOR > 3 || (NSS_VMAJOR == 3 && defined(NSS_VMINOR) && NSS_VMINOR >= 39)) + #include + #include +#else + #include + #include +#endif /* NSS before 3.39 */ + #include + #include + #include +#endif /* WITH_NSS */ + +char *certfile = NULL; +char *certname = NULL; +char *certpasswd = NULL; + +/* Warning: in this release of NUT, this feature is disabled by default + * in order to retain compatibility with "least surprise" for earlier + * existing deployments. Over time it can become enabled by default. + * See upsd.conf option DISABLE_WEAK_SSL to toggle this in-vivo. + */ +int disable_weak_ssl = 0; + +#ifdef WITH_CLIENT_CERTIFICATE_VALIDATION +int certrequest = 0; +#endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */ + +static int ssl_initialized = 0; + +#ifndef WITH_SSL + +/* stubs for non-ssl compiles */ +void net_starttls(nut_ctype_t *client, size_t numarg, const char **arg) +{ + NUT_UNUSED_VARIABLE(client); + NUT_UNUSED_VARIABLE(numarg); + NUT_UNUSED_VARIABLE(arg); + + send_err(client, NUT_ERR_FEATURE_NOT_SUPPORTED); + return; +} + +ssize_t ssl_write(nut_ctype_t *client, const char *buf, size_t buflen) +{ + NUT_UNUSED_VARIABLE(client); + NUT_UNUSED_VARIABLE(buf); + NUT_UNUSED_VARIABLE(buflen); + + upslogx(LOG_ERR, "ssl_write called but SSL wasn't compiled in"); + return -1; +} + +ssize_t ssl_read(nut_ctype_t *client, char *buf, size_t buflen) +{ + NUT_UNUSED_VARIABLE(client); + NUT_UNUSED_VARIABLE(buf); + NUT_UNUSED_VARIABLE(buflen); + + upslogx(LOG_ERR, "ssl_read called but SSL wasn't compiled in"); + return -1; +} + +void ssl_init(void) +{ + ssl_initialized = 0; /* keep gcc quiet */ +} + +void ssl_finish(nut_ctype_t *client) +{ + if (client->ssl) { + upslogx(LOG_ERR, "ssl_finish found active SSL connection but SSL wasn't compiled in"); + } +} + +void ssl_cleanup(void) +{ +} + +#else + +#ifdef WITH_OPENSSL + +static SSL_CTX *ssl_ctx = NULL; + +static void ssl_debug(void) +{ + unsigned long e; + char errmsg[SMALLBUF]; + + while ((e = ERR_get_error()) != 0) { + ERR_error_string_n(e, errmsg, sizeof(errmsg)); + upsdebugx(1, "ssl_debug: %s", errmsg); + } +} + +static int ssl_error(SSL *ssl, ssize_t ret) +{ + int e; + + if (ret >= INT_MAX) { + upslogx(LOG_ERR, "ssl_error() ret=%zd would not fit in an int", ret); + return -1; + } + e = SSL_get_error(ssl, (int)ret); + + switch (e) + { + case SSL_ERROR_WANT_READ: + upsdebugx(1, "ssl_error() ret=%zd SSL_ERROR_WANT_READ", ret); + break; + + case SSL_ERROR_WANT_WRITE: + upsdebugx(1, "ssl_error() ret=%zd SSL_ERROR_WANT_WRITE", ret); + break; + + case SSL_ERROR_SYSCALL: + if (ret == 0 && ERR_peek_error() == 0) { + upsdebugx(1, "ssl_error() EOF from client"); + } else { + upsdebugx(1, "ssl_error() ret=%zd SSL_ERROR_SYSCALL", ret); + } + break; + + default: + upsdebugx(1, "ssl_error() ret=%zd SSL_ERROR %d", ret, e); + ssl_debug(); + } + + return -1; +} + +#elif defined(WITH_NSS) /* WITH_OPENSSL */ + +static CERTCertificate *cert; +static SECKEYPrivateKey *privKey; + +static char *nss_password_callback(PK11SlotInfo *slot, PRBool retry, + void *arg) +{ + NUT_UNUSED_VARIABLE(arg); + + if (retry) { + /* Force not inted to retrieve password many times. */ + return NULL; + } + upslogx(LOG_INFO, "Intend to retrieve password for %s / %s: password %sconfigured", + PK11_GetSlotName(slot), PK11_GetTokenName(slot), certpasswd?"":"not "); + return certpasswd ? PL_strdup(certpasswd) : NULL; +} + +static void nss_error(const char* text) +{ + char buffer[SMALLBUF]; + PRInt32 length = PR_GetErrorText(buffer); + if (length > 0 && length < SMALLBUF) { + upsdebugx(1, "nss_error %ld in %s : %s", (long)PR_GetError(), text, buffer); + }else{ + upsdebugx(1, "nss_error %ld in %s", (long)PR_GetError(), text); + } +} + +static int ssl_error(PRFileDesc *ssl, ssize_t ret) +{ + char buffer[256]; + PRInt32 length; + PRErrorCode e; + NUT_UNUSED_VARIABLE(ssl); + NUT_UNUSED_VARIABLE(ret); + + e = PR_GetError(); + length = PR_GetErrorText(buffer); + if (length > 0 && length < 256) { + upsdebugx(1, "ssl_error() ret=%d %*s", e, length, buffer); + } else { + upsdebugx(1, "ssl_error() ret=%d", e); + } + + return -1; +} + +static SECStatus AuthCertificate(CERTCertDBHandle *arg, PRFileDesc *fd, + PRBool checksig, PRBool isServer) +{ + nut_ctype_t *client = (nut_ctype_t *)SSL_RevealPinArg(fd); + SECStatus status = SSL_AuthCertificate(arg, fd, checksig, isServer); + upslogx(LOG_INFO, "Intend to authenticate client %s : %s.", + client?client->addr:"(unnamed)", + status==SECSuccess?"SUCCESS":"FAILED"); + return status; +} + +static SECStatus BadCertHandler(nut_ctype_t *arg, PRFileDesc *fd) +{ + NUT_UNUSED_VARIABLE(fd); + + upslogx(LOG_WARNING, "Certificate validation failed for %s", + (arg&&arg->addr)?arg->addr:""); +#ifdef WITH_CLIENT_CERTIFICATE_VALIDATION + /* BadCertHandler is called when the NSS certificate validation is failed. + * If the certificate verification (user conf) is mandatory, reject authentication + * else accept it. + */ + return certrequest==NETSSL_CERTREQ_REQUIRE?SECFailure:SECSuccess; +#else /* WITH_CLIENT_CERTIFICATE_VALIDATION */ + /* Always accept clients. */ + return SECSuccess; +#endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */ +} + +static void HandshakeCallback(PRFileDesc *fd, nut_ctype_t *client_data) +{ + NUT_UNUSED_VARIABLE(fd); + + upslogx(LOG_INFO, "SSL handshake done successfully with client %s", + client_data->addr); +} + + +#endif /* WITH_OPENSSL | WITH_NSS */ + +void net_starttls(nut_ctype_t *client, size_t numarg, const char **arg) +{ +#ifdef WITH_OPENSSL + int ret; +#elif defined(WITH_NSS) /* WITH_OPENSSL */ + SECStatus status; + PRFileDesc *socket; +#endif /* WITH_OPENSSL | WITH_NSS */ + + NUT_UNUSED_VARIABLE(numarg); + NUT_UNUSED_VARIABLE(arg); + + if (client->ssl) { + send_err(client, NUT_ERR_ALREADY_SSL_MODE); + return; + } + + client->ssl_connected = 0; + + if ((!certfile) || (!ssl_initialized)) { + send_err(client, NUT_ERR_FEATURE_NOT_CONFIGURED); + return; + } + +#ifdef WITH_OPENSSL + if (!ssl_ctx) +#elif defined(WITH_NSS) /* WITH_OPENSSL */ + if (!NSS_IsInitialized()) +#endif /* WITH_OPENSSL | WITH_NSS */ + { + send_err(client, NUT_ERR_FEATURE_NOT_CONFIGURED); + ssl_initialized = 0; + return; + } + + if (!sendback(client, "OK STARTTLS\n")) { + return; + } + +#ifdef WITH_OPENSSL + + client->ssl = SSL_new(ssl_ctx); + + if (!client->ssl) { + upslog_with_errno(LOG_ERR, "SSL_new failed\n"); + ssl_debug(); + return; + } + + if (SSL_set_fd(client->ssl, client->sock_fd) != 1) { + upslog_with_errno(LOG_ERR, "SSL_set_fd failed\n"); + ssl_debug(); + return; + } + + ret = SSL_accept(client->ssl); + switch (ret) + { + case 1: + client->ssl_connected = 1; + upsdebugx(3, "SSL connected (%s)", SSL_get_version(client->ssl)); + break; + + case 0: + upslog_with_errno(LOG_ERR, "SSL_accept do not accept handshake."); + ssl_error(client->ssl, ret); + break; + case -1: + upslog_with_errno(LOG_ERR, "Unknown return value from SSL_accept"); + ssl_error(client->ssl, ret); + break; + } + +#elif defined(WITH_NSS) /* WITH_OPENSSL */ + + socket = PR_ImportTCPSocket(client->sock_fd); + if (socket == NULL){ + upslogx(LOG_ERR, "Can not inialize SSL connection"); + nss_error("net_starttls / PR_ImportTCPSocket"); + return; + } + + client->ssl = SSL_ImportFD(NULL, socket); + if (client->ssl == NULL){ + upslogx(LOG_ERR, "Can not inialize SSL connection"); + nss_error("net_starttls / SSL_ImportFD"); + return; + } + + if (SSL_SetPKCS11PinArg(client->ssl, client) == -1){ + upslogx(LOG_ERR, "Can not inialize SSL connection"); + nss_error("net_starttls / SSL_SetPKCS11PinArg"); + return; + } + + /* Note cast to SSLAuthCertificate to prevent warning due to + * bad function prototype in NSS. + */ + status = SSL_AuthCertificateHook(client->ssl, (SSLAuthCertificate)AuthCertificate, CERT_GetDefaultCertDB()); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not inialize SSL connection"); + nss_error("net_starttls / SSL_AuthCertificateHook"); + return; + } + + status = SSL_BadCertHook(client->ssl, (SSLBadCertHandler)BadCertHandler, client); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not inialize SSL connection"); + nss_error("net_starttls / SSL_BadCertHook"); + return; + } + + status = SSL_HandshakeCallback(client->ssl, (SSLHandshakeCallback)HandshakeCallback, client); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not inialize SSL connection"); + nss_error("net_starttls / SSL_HandshakeCallback"); + return; + } + + status = SSL_ConfigSecureServer(client->ssl, cert, privKey, NSS_FindCertKEAType(cert)); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not inialize SSL connection"); + nss_error("net_starttls / SSL_ConfigSecureServer"); + return; + } + + status = SSL_ResetHandshake(client->ssl, PR_TRUE); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not inialize SSL connection"); + nss_error("net_starttls / SSL_ResetHandshake"); + return; + } + + /* Note: this call can generate memory leaks not resolvable + * by any release function. + * Probably SSL session key object allocation. */ + status = SSL_ForceHandshake(client->ssl); + if (status != SECSuccess) { + PRErrorCode code = PR_GetError(); + if (code==SSL_ERROR_NO_CERTIFICATE) { + upslogx(LOG_WARNING, "Client %s do not provide certificate.", + client->addr); + } else { + nss_error("net_starttls / SSL_ForceHandshake"); + /* TODO : Close the connection. */ + return; + } + } + client->ssl_connected = 1; +#endif /* WITH_OPENSSL | WITH_NSS */ +} + +void ssl_init(void) +{ +#ifdef WITH_NSS + SECStatus status; +#if defined(NSS_VMAJOR) && (NSS_VMAJOR > 3 || (NSS_VMAJOR == 3 && defined(NSS_VMINOR) && NSS_VMINOR >= 14)) + SSLVersionRange range; +#endif +#endif /* WITH_NSS */ + + if (!certfile) { + return; + } + + check_perms(certfile); + if (!disable_weak_ssl) + upslogx(LOG_WARNING, "Warning: DISABLE_WEAK_SSL is not enabled. Please consider enabling to improve network security."); + +#ifdef WITH_OPENSSL + +#if OPENSSL_VERSION_NUMBER < 0x10100000L + SSL_load_error_strings(); + SSL_library_init(); + + ssl_ctx = SSL_CTX_new(SSLv23_server_method()); +#else + ssl_ctx = SSL_CTX_new(TLS_server_method()); +#endif + + if (!ssl_ctx) { + ssl_debug(); + fatalx(EXIT_FAILURE, "SSL_CTX_new failed"); + } + + SSL_CTX_set_options(ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); +#if OPENSSL_VERSION_NUMBER < 0x10100000L + /* set minimum protocol TLSv1 */ + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); + if (disable_weak_ssl) { +#if defined(SSL_OP_NO_TLSv1_2) + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); +#elif defined(SSL_OP_NO_TLSv1_1) + SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TLSv1); +#endif + } +#else + if (SSL_CTX_set_min_proto_version(ssl_ctx, disable_weak_ssl ? TLS1_2_VERSION : TLS1_VERSION) != 1) { + ssl_debug(); + fatalx(EXIT_FAILURE, "SSL_CTX_set_min_proto_version(TLS1_VERSION)"); + } +#endif + + if (SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile) != 1) { + ssl_debug(); + fatalx(EXIT_FAILURE, "SSL_CTX_use_certificate_chain_file(%s) failed", certfile); + } + + if (SSL_CTX_use_PrivateKey_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) != 1) { + ssl_debug(); + fatalx(EXIT_FAILURE, "SSL_CTX_use_PrivateKey_file(%s) failed", certfile); + } + + if (SSL_CTX_check_private_key(ssl_ctx) != 1) { + ssl_debug(); + fatalx(EXIT_FAILURE, "SSL_CTX_check_private_key(%s) failed", certfile); + } + + if (SSL_CTX_set_cipher_list(ssl_ctx, "HIGH:@STRENGTH") != 1) { + ssl_debug(); + fatalx(EXIT_FAILURE, "SSL_CTX_set_cipher_list failed"); + } + + SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL); + + ssl_initialized = 1; + +#elif defined(WITH_NSS) /* WITH_OPENSSL */ + + if (!certname || certname[0]==0 ) { + upslogx(LOG_ERR, "The SSL certificate name is not specified."); + return; + } + + PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + PK11_SetPasswordFunc(nss_password_callback); + + if (certfile) + /* Note: this call can generate memory leaks not resolvable + * by any release function. + * Probably NSS key module object allocation and + * probably NSS key db object allocation too. */ + status = NSS_Init(certfile); + else + status = NSS_NoDB_Init(NULL); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not initialize SSL context"); + nss_error("upscli_init / NSS_[NoDB]_Init"); + return; + } + + status = NSS_SetDomesticPolicy(); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not initialize SSL policy"); + nss_error("upscli_init / NSS_SetDomesticPolicy"); + return; + } + + /* Default server cache config */ + status = SSL_ConfigServerSessionIDCache(0, 0, 0, NULL); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not initialize SSL server cache"); + nss_error("upscli_init / SSL_ConfigServerSessionIDCache"); + return; + } + + if (!disable_weak_ssl) { + status = SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_TRUE); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not enable SSLv3"); + nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_SSL3)"); + return; + } + status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not enable TLSv1"); + nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_TLS)"); + return; + } + } else { +#if defined(NSS_VMAJOR) && (NSS_VMAJOR > 3 || (NSS_VMAJOR == 3 && defined(NSS_VMINOR) && NSS_VMINOR >= 14)) + status = SSL_VersionRangeGetSupported(ssl_variant_stream, &range); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not get versions supported"); + nss_error("upscli_init / SSL_VersionRangeGetSupported"); + return; + } + range.min = SSL_LIBRARY_VERSION_TLS_1_1; +#ifdef SSL_LIBRARY_VERSION_TLS_1_2 + range.min = SSL_LIBRARY_VERSION_TLS_1_2; +#endif + status = SSL_VersionRangeSetDefault(ssl_variant_stream, &range); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not set versions supported"); + nss_error("upscli_init / SSL_VersionRangeSetDefault"); + return; + } + /* Disable old/weak ciphers */ + SSL_CipherPrefSetDefault(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, PR_FALSE); + SSL_CipherPrefSetDefault(TLS_RSA_WITH_3DES_EDE_CBC_SHA, PR_FALSE); + SSL_CipherPrefSetDefault(TLS_RSA_WITH_RC4_128_SHA, PR_FALSE); + SSL_CipherPrefSetDefault(TLS_RSA_WITH_RC4_128_MD5, PR_FALSE); +#else + status = SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_FALSE); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not disable SSLv3"); + nss_error("upscli_init / SSL_OptionSetDefault(SSL_DISABLE_SSL3)"); + return; + } + status = SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not enable TLSv1"); + nss_error("upscli_init / SSL_OptionSetDefault(SSL_ENABLE_TLS)"); + return; + } +#endif + } + +#ifdef WITH_CLIENT_CERTIFICATE_VALIDATION + if (certrequest < NETSSL_CERTREQ_NO && + certrequest > NETSSL_CERTREQ_REQUEST) { + upslogx(LOG_ERR, "Invalid certificate requirement"); + return; + } + + if (certrequest == NETSSL_CERTREQ_REQUEST || + certrequest == NETSSL_CERTREQ_REQUIRE ) { + status = SSL_OptionSetDefault(SSL_REQUEST_CERTIFICATE, PR_TRUE); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not enable certificate request"); + nss_error("upscli_init / SSL_OptionSetDefault(SSL_REQUEST_CERTIFICATE)"); + return; + } + } + + if (certrequest == NETSSL_CERTREQ_REQUIRE ) { + status = SSL_OptionSetDefault(SSL_REQUIRE_CERTIFICATE, PR_TRUE); + if (status != SECSuccess) { + upslogx(LOG_ERR, "Can not enable certificate requirement"); + nss_error("upscli_init / SSL_OptionSetDefault(SSL_REQUIRE_CERTIFICATE)"); + return; + } + } +#endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */ + + cert = PK11_FindCertFromNickname(certname, NULL); + if(cert==NULL) { + upslogx(LOG_ERR, "Can not find server certificate"); + nss_error("upscli_init / PK11_FindCertFromNickname"); + return; + } + + privKey = PK11_FindKeyByAnyCert(cert, NULL); + if(privKey==NULL){ + upslogx(LOG_ERR, "Can not find private key associate to server certificate"); + nss_error("upscli_init / PK11_FindKeyByAnyCert"); + return; + } + + ssl_initialized = 1; +#else /* WITH_OPENSSL | WITH_NSS */ + upslogx(LOG_ERR, "ssl_init called but SSL wasn't compiled in"); +#endif /* WITH_OPENSSL | WITH_NSS */ +} + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif +ssize_t ssl_read(nut_ctype_t *client, char *buf, size_t buflen) +{ + ssize_t ret = -1; + + if (!client->ssl_connected) { + return -1; + } + +#ifdef WITH_OPENSSL + /* SSL_* routines deal with int type for return and buflen + * We might need to window our I/O if we exceed 2GB (in + * 32-bit builds)... Not likely to exceed in 64-bit builds, + * but smaller systems with 16-bits might be endangered :) + */ + assert(buflen <= INT_MAX); + int iret = SSL_read(client->ssl, buf, (int)buflen); + assert(iret <= SSIZE_MAX); + ret = (ssize_t)iret; +#elif defined(WITH_NSS) /* WITH_OPENSSL */ + /* PR_* routines deal in PRInt32 type + * We might need to window our I/O if we exceed 2GB :) */ + assert(buflen <= PR_INT32_MAX); + ret = PR_Read(client->ssl, buf, (PRInt32)buflen); +#endif /* WITH_OPENSSL | WITH_NSS */ + + if (ret < 1) { + ssl_error(client->ssl, ret); + return -1; + } + + return ret; +} + +ssize_t ssl_write(nut_ctype_t *client, const char *buf, size_t buflen) +{ + ssize_t ret = -1; + + if (!client->ssl_connected) { + return -1; + } + +#ifdef WITH_OPENSSL + /* SSL_* routines deal with int type for return and buflen + * We might need to window our I/O if we exceed 2GB (in + * 32-bit builds)... Not likely to exceed in 64-bit builds, + * but smaller systems with 16-bits might be endangered :) + */ + assert(buflen <= INT_MAX); + int iret = SSL_write(client->ssl, buf, (int)buflen); + assert(iret <= SSIZE_MAX); + ret = (ssize_t)iret; +#elif defined(WITH_NSS) /* WITH_OPENSSL */ + /* PR_* routines deal in PRInt32 type + * We might need to window our I/O if we exceed 2GB :) */ + assert(buflen <= PR_INT32_MAX); + ret = PR_Write(client->ssl, buf, (PRInt32)buflen); +#endif /* WITH_OPENSSL | WITH_NSS */ + + upsdebugx(5, "ssl_write ret=%zd", ret); + + return ret; +} +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) ) +# pragma GCC diagnostic pop +#endif + +void ssl_finish(nut_ctype_t *client) +{ + if (client->ssl) { +#ifdef WITH_OPENSSL + SSL_free(client->ssl); +#elif defined(WITH_NSS) + PR_Shutdown(client->ssl, PR_SHUTDOWN_BOTH); + PR_Close(client->ssl); +#endif /* WITH_OPENSSL | WITH_NSS */ + client->ssl_connected = 0; + client->ssl = NULL; + } +} + +void ssl_cleanup(void) +{ +#ifdef WITH_OPENSSL + if (ssl_ctx) { + SSL_CTX_free(ssl_ctx); + ssl_ctx = NULL; + } +#elif defined(WITH_NSS) /* WITH_OPENSSL */ + CERT_DestroyCertificate(cert); + SECKEY_DestroyPrivateKey(privKey); + NSS_Shutdown(); + PR_Cleanup(); + /* Called to release memory arena used by NSS/NSPR. + * Prevent to show all PL_ArenaAllocate mem alloc as leaks. + * https://developer.mozilla.org/en/NSS_Memory_allocation + */ + PL_ArenaFinish(); +#endif /* WITH_OPENSSL | WITH_NSS */ + ssl_initialized = 0; +} + +#endif /* WITH_SSL */ diff --git a/server/netssl.h b/server/netssl.h new file mode 100644 index 0000000..1cff564 --- /dev/null +++ b/server/netssl.h @@ -0,0 +1,63 @@ +/* netssl.h - ssl support prototypes for upsd + + Copyright (C) 2002 Russell Kroll + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_NETSSL_H_SEEN +#define NUT_NETSSL_H_SEEN 1 + +#include "nut_ctype.h" + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +extern char *certfile; +extern char *certname; +extern char *certpasswd; +extern int disable_weak_ssl; +#ifdef WITH_CLIENT_CERTIFICATE_VALIDATION +extern int certrequest; +#endif /* WITH_CLIENT_CERTIFICATE_VALIDATION */ + +/* List possible values for certrequested */ +/* No request */ +#define NETSSL_CERTREQ_NO 0 +/* Requested (cnx failed if no certificate) */ +#define NETSSL_CERTREQ_REQUEST 1 +/* Required (cnx failed if no certificate or invalid CA chain) */ +#define NETSSL_CERTREQ_REQUIRE 2 + + +void ssl_init(void); +void ssl_finish(nut_ctype_t *client); +void ssl_cleanup(void); + +ssize_t ssl_read(nut_ctype_t *client, char *buf, size_t buflen); +ssize_t ssl_write(nut_ctype_t *client, const char *buf, size_t buflen); + +void net_starttls(nut_ctype_t *client, size_t numarg, const char **arg); + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_NETSSL_H_SEEN */ diff --git a/server/netuser.c b/server/netuser.c index 29c8596..6f4c677 100644 --- a/server/netuser.c +++ b/server/netuser.c @@ -1,4 +1,4 @@ -/* netuser.c - LOGIN/LOGOUT/USERNAME/PASSWORD/MASTER handlers for upsd +/* netuser.c - LOGIN/LOGOUT/USERNAME/PASSWORD/MASTER[PRIMARY] handlers for upsd Copyright (C) 2003 Russell Kroll @@ -28,7 +28,7 @@ #include "netuser.h" /* LOGIN */ -void net_login(nut_ctype_t *client, int numarg, const char **arg) +void net_login(nut_ctype_t *client, size_t numarg, const char **arg) { upstype_t *ups; @@ -53,6 +53,8 @@ void net_login(nut_ctype_t *client, int numarg, const char **arg) /* make sure this is a valid user */ if (!user_checkaction(client->username, client->password, "LOGIN")) { + upsdebugx(3, "%s: not a valid user: %s", + __func__, client->username); send_err(client, NUT_ERR_ACCESS_DENIED); return; } @@ -65,8 +67,9 @@ void net_login(nut_ctype_t *client, int numarg, const char **arg) sendback(client, "OK\n"); } -void net_logout(nut_ctype_t *client, int numarg, const char **arg) +void net_logout(nut_ctype_t *client, size_t numarg, const char **arg) { + NUT_UNUSED_VARIABLE(arg); if (numarg != 0) { send_err(client, NUT_ERR_INVALID_ARGUMENT); return; @@ -82,35 +85,64 @@ void net_logout(nut_ctype_t *client, int numarg, const char **arg) client->last_heard = 0; } -/* MASTER */ -void net_master(nut_ctype_t *client, int numarg, const char **arg) +/* NOTE: Protocol updated since NUT 2.8.0 to handle master/primary + * and API bumped, to rename/alias the routine. + */ +static int do_net_primary(nut_ctype_t *client, size_t numarg, const char **arg) { upstype_t *ups; if (numarg != 1) { send_err(client, NUT_ERR_INVALID_ARGUMENT); - return; + return -1; } ups = get_ups_ptr(arg[0]); if (!ups) { send_err(client, NUT_ERR_UNKNOWN_UPS); - return; + return -1; } - /* make sure this user is allowed to do MASTER */ - if (!user_checkaction(client->username, client->password, "MASTER")) { + /* make sure this user is allowed to do PRIMARY or MASTER */ + if (!user_checkaction(client->username, client->password, "PRIMARY") + && !user_checkaction(client->username, client->password, "MASTER") + ) { send_err(client, NUT_ERR_ACCESS_DENIED); - return; + return -1; } /* this is just an access level check */ - sendback(client, "OK MASTER-GRANTED\n"); + /* sendback() will be worded by caller below */ + return 0; +} + +/* MASTER (deprecated) */ +void net_master(nut_ctype_t *client, size_t numarg, const char **arg) { + /* Allow existing binaries linked against this file to still work */ + upsdebugx(1, + "WARNING: Client %s@%s " + "requested MASTER level for device %s - " + "which is deprecated in favor of PRIMARY " + "since NUT 2.8.0", + client->username, client->addr, + (numarg > 0) ? arg[0] : ""); + + if (0 == do_net_primary(client, numarg, arg)) { + sendback(client, "OK MASTER-GRANTED\n"); + } +} + +/* PRIMARY (since NUT 2.8.0) */ +void net_primary(nut_ctype_t *client, size_t numarg, const char **arg) +{ + if (0 == do_net_primary(client, numarg, arg)) { + sendback(client, "OK PRIMARY-GRANTED\n"); + } } /* USERNAME */ -void net_username(nut_ctype_t *client, int numarg, const char **arg) +void net_username(nut_ctype_t *client, size_t numarg, const char **arg) { if (numarg != 1) { send_err(client, NUT_ERR_INVALID_ARGUMENT); @@ -130,7 +162,7 @@ void net_username(nut_ctype_t *client, int numarg, const char **arg) } /* PASSWORD */ -void net_password(nut_ctype_t *client, int numarg, const char **arg) +void net_password(nut_ctype_t *client, size_t numarg, const char **arg) { if (numarg != 1) { send_err(client, NUT_ERR_INVALID_ARGUMENT); diff --git a/server/netuser.h b/server/netuser.h index 5f7e047..7532b3c 100644 --- a/server/netuser.h +++ b/server/netuser.h @@ -1,5 +1,53 @@ -void net_login(nut_ctype_t *client, int numarg, const char **arg); -void net_logout(nut_ctype_t *client, int numarg, const char **arg); -void net_master(nut_ctype_t *client, int numarg, const char **arg); -void net_username(nut_ctype_t *client, int numarg, const char **arg); -void net_password(nut_ctype_t *client, int numarg, const char **arg); +/* netuser.c - LOGIN/LOGOUT/USERNAME/PASSWORD/MASTER[PRIMARY] handlers for upsd + + Copyright (C) + 2003 Russell Kroll + 2005 Arnaud Quette + 2007 Peter Selinger + 2013 Emilien Kia + 2020-2021 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef NUT_NETUSER_H_SEEN +#define NUT_NETUSER_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +void net_login(nut_ctype_t *client, size_t numarg, const char **arg); +void net_logout(nut_ctype_t *client, size_t numarg, const char **arg); + +/* NOTE: Since NUT 2.8.0 we handle master as alias for primary + * Header keyword kept for building older consumers, but + * the implementation will warn that it is deprecated. + */ +void net_master(nut_ctype_t *client, size_t numarg, const char **arg); +void net_primary(nut_ctype_t *client, size_t numarg, const char **arg); + +void net_username(nut_ctype_t *client, size_t numarg, const char **arg); +void net_password(nut_ctype_t *client, size_t numarg, const char **arg); + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_NETUSER_H_SEEN */ diff --git a/server/nut_ctype.h b/server/nut_ctype.h index 498298c..db9f4df 100644 --- a/server/nut_ctype.h +++ b/server/nut_ctype.h @@ -4,6 +4,8 @@ 2002 Russell Kroll 2008 Arjen de Korte 2011 Arnaud Quette + 2013 Emilien Kia + 2020 Jim Klimov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -23,13 +25,26 @@ #ifndef NUT_CTYPE_H_SEEN #define NUT_CTYPE_H_SEEN 1 -#ifdef HAVE_SSL -#include -#include +/* Mozilla NSS */ +#ifdef WITH_NSS + #include + #include +#endif + +/* OpenSSL */ +#ifdef WITH_OPENSSL + #include + #include #endif #include "parseconf.h" +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + /* client structure */ typedef struct nut_ctype_s { char *addr; @@ -38,11 +53,16 @@ typedef struct nut_ctype_s { char *loginups; char *password; char *username; + /* per client status info for commands and settings + * (disabled by default) */ + int tracking; -#ifdef HAVE_SSL +#ifdef WITH_OPENSSL SSL *ssl; +#elif defined(WITH_NSS) + PRFileDesc *ssl; #else - void *ssl; + void *ssl; #endif int ssl_connected; @@ -53,4 +73,10 @@ typedef struct nut_ctype_s { struct nut_ctype_s *next; } nut_ctype_t; +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + #endif /* NUT_CTYPE_H_SEEN */ diff --git a/server/sockdebug.c b/server/sockdebug.c index 7829292..f9fe476 100644 --- a/server/sockdebug.c +++ b/server/sockdebug.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -31,11 +31,11 @@ PCONF_CTX_t sock_ctx; -static void sock_arg(int numarg, char **arg) +static void sock_arg(size_t numarg, char **arg) { - int i; + size_t i; - printf("numarg=%d : ", numarg); + printf("numarg=%zu : ", numarg); for (i = 0; i < numarg; i++) printf("[%s] ", arg[i]); @@ -48,6 +48,8 @@ static int socket_connect(const char *sockfn) int ret, fd; struct sockaddr_un sa; + check_unix_socket_filename(sockfn); + memset(&sa, '\0', sizeof(sa)); sa.sun_family = AF_UNIX; snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", sockfn); @@ -98,7 +100,7 @@ static void read_sock(int fd) } if (ret < 0) { - perror("read sockfd"); + perror("read sockfd"); exit(EXIT_FAILURE); } diff --git a/server/ssl.c b/server/ssl.c deleted file mode 100644 index bb95dca..0000000 --- a/server/ssl.c +++ /dev/null @@ -1,258 +0,0 @@ -/* ssl.c - Interface to OpenSSL for upsd - - Copyright (C) - 2002 Russell Kroll - 2008 Arjen de Korte - - based on the original implementation: - - Copyright (C) 2002 Technorama Ltd. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include -#include -#include - -#include "upsd.h" -#include "neterr.h" -#include "ssl.h" - -char *certfile = NULL; - -static int ssl_initialized = 0; - -#ifndef HAVE_SSL - -/* stubs for non-ssl compiles */ -void net_starttls(nut_ctype_t *client, int numarg, const char **arg) -{ - send_err(client, NUT_ERR_FEATURE_NOT_SUPPORTED); - return; -} - -int ssl_write(nut_ctype_t *client, const char *buf, size_t buflen) -{ - upslogx(LOG_ERR, "ssl_write called but SSL wasn't compiled in"); - return -1; -} - -int ssl_read(nut_ctype_t *client, char *buf, size_t buflen) -{ - upslogx(LOG_ERR, "ssl_read called but SSL wasn't compiled in"); - return -1; -} - -void ssl_init(void) -{ - ssl_initialized = 0; /* keep gcc quiet */ -} - -void ssl_finish(nut_ctype_t *client) -{ - if (client->ssl) { - upslogx(LOG_ERR, "ssl_finish found active SSL connection but SSL wasn't compiled in"); - } -} - -#else - -static SSL_CTX *ssl_ctx = NULL; - -static void ssl_debug(void) -{ - int e; - char errmsg[SMALLBUF]; - - while ((e = ERR_get_error()) != 0) { - ERR_error_string_n(e, errmsg, sizeof(errmsg)); - upsdebugx(1, "ssl_debug: %s", errmsg); - } -} - -void net_starttls(nut_ctype_t *client, int numarg, const char **arg) -{ - if (client->ssl) { - send_err(client, NUT_ERR_ALREADY_SSL_MODE); - return; - } - - if ((!ssl_ctx) || (!certfile) || (!ssl_initialized)) { - send_err(client, NUT_ERR_FEATURE_NOT_CONFIGURED); - return; - } - - if (!sendback(client, "OK STARTTLS\n")) { - return; - } - - client->ssl = SSL_new(ssl_ctx); - - if (!client->ssl) { - upslog_with_errno(LOG_ERR, "SSL_new failed\n"); - ssl_debug(); - return; - } - - if (SSL_set_fd(client->ssl, client->sock_fd) != 1) { - upslog_with_errno(LOG_ERR, "SSL_set_fd failed\n"); - ssl_debug(); - } -} - -void ssl_init(void) -{ -#if OPENSSL_VERSION_NUMBER >= 0x10000000L - const SSL_METHOD *ssl_method; -#else - SSL_METHOD *ssl_method; -#endif - if (!certfile) { - return; - } - - check_perms(certfile); - - SSL_load_error_strings(); - SSL_library_init(); - - if ((ssl_method = TLSv1_server_method()) == NULL) { - ssl_debug(); - fatalx(EXIT_FAILURE, "TLSv1_server_method failed"); - } - - if ((ssl_ctx = SSL_CTX_new(ssl_method)) == NULL) { - ssl_debug(); - fatalx(EXIT_FAILURE, "SSL_CTX_new failed"); - } - - if (SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile) != 1) { - ssl_debug(); - fatalx(EXIT_FAILURE, "SSL_CTX_use_certificate_chain_file(%s) failed", certfile); - } - - if (SSL_CTX_use_PrivateKey_file(ssl_ctx, certfile, SSL_FILETYPE_PEM) != 1) { - ssl_debug(); - fatalx(EXIT_FAILURE, "SSL_CTX_use_PrivateKey_file(%s) failed", certfile); - } - - if (SSL_CTX_check_private_key(ssl_ctx) != 1) { - ssl_debug(); - fatalx(EXIT_FAILURE, "SSL_CTX_check_private_key(%s) failed", certfile); - } - - if (SSL_CTX_set_cipher_list(ssl_ctx, "HIGH:@STRENGTH") != 1) { - ssl_debug(); - fatalx(EXIT_FAILURE, "SSL_CTX_set_cipher_list failed"); - } - - SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL); - - ssl_initialized = 1; -} - -static int ssl_error(SSL *ssl, int ret) -{ - int e; - - e = SSL_get_error(ssl, ret); - - switch (e) - { - case SSL_ERROR_WANT_READ: - upsdebugx(1, "ssl_error() ret=%d SSL_ERROR_WANT_READ", ret); - break; - - case SSL_ERROR_WANT_WRITE: - upsdebugx(1, "ssl_error() ret=%d SSL_ERROR_WANT_WRITE", ret); - break; - - case SSL_ERROR_SYSCALL: - if (ret == 0 && ERR_peek_error() == 0) { - upsdebugx(1, "ssl_error() EOF from client"); - } else { - upsdebugx(1, "ssl_error() ret=%d SSL_ERROR_SYSCALL", ret); - } - break; - - default: - upsdebugx(1, "ssl_error() ret=%d SSL_ERROR %d", ret, e); - ssl_debug(); - } - - return -1; -} - -static int ssl_accept(nut_ctype_t *client) -{ - int ret; - - ret = SSL_accept(client->ssl); - - switch (ret) - { - case 1: - client->ssl_connected = 1; - upsdebugx(3, "SSL connected"); - return 0; - - case 0: - case -1: - return ssl_error(client->ssl, ret); - } - - upslog_with_errno(LOG_ERR, "Unknown return value from SSL_accept"); - return -1; -} - -int ssl_read(nut_ctype_t *client, char *buf, size_t buflen) -{ - int ret; - - if (!client->ssl_connected) { - if (ssl_accept(client) != 0) - return -1; - } - - ret = SSL_read(client->ssl, buf, buflen); - - if (ret < 1) { - ssl_error(client->ssl, ret); - return -1; - } - - return ret; -} - -int ssl_write(nut_ctype_t *client, const char *buf, size_t buflen) -{ - int ret; - - ret = SSL_write(client->ssl, buf, buflen); - - upsdebugx(5, "ssl_write ret=%d", ret); - - return ret; -} - -void ssl_finish(nut_ctype_t *client) -{ - if (client->ssl) { - SSL_free(client->ssl); - } -} - -#endif /* HAVE_SSL */ diff --git a/server/ssl.h b/server/ssl.h deleted file mode 100644 index 7f4009c..0000000 --- a/server/ssl.h +++ /dev/null @@ -1,40 +0,0 @@ -/* ssl.h - ssl support prototypes for upsd - - Copyright (C) 2002 Russell Kroll - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#ifndef SSL_H_SEEN -#define SSL_H_SEEN 1 - -#ifdef HAVE_SSL -#include -#include -#endif - -#include "nut_ctype.h" - -extern char *certfile; - -void ssl_init(void); -void ssl_finish(nut_ctype_t *client); - -int ssl_read(nut_ctype_t *client, char *buf, size_t buflen); -int ssl_write(nut_ctype_t *client, const char *buf, size_t buflen); - -void net_starttls(nut_ctype_t *client, int numarg, const char **arg); - -#endif /* SSL_H_SEEN */ diff --git a/server/sstate.c b/server/sstate.c index dfbc2b6..167bc09 100644 --- a/server/sstate.c +++ b/server/sstate.c @@ -3,6 +3,7 @@ Copyright (C) 2003 Russell Kroll 2008 Arjen de Korte + 2012 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -24,7 +25,9 @@ #include "timehead.h" #include "sstate.h" +#include "upsd.h" #include "upstype.h" +#include "nut_stdint.h" #include #include @@ -32,9 +35,9 @@ #include #include #include -#include +#include -static int parse_args(upstype_t *ups, int numargs, char **arg) +static int parse_args(upstype_t *ups, size_t numargs, char **arg) { if (numargs < 1) return 0; @@ -63,6 +66,7 @@ static int parse_args(upstype_t *ups, int numargs, char **arg) if (numargs < 2) return 0; + /* FIXME: all these should return their state_...() value! */ /* ADDCMD */ if (!strcasecmp(arg[0], "ADDCMD")) { state_addcmd(&ups->cmdlist, arg[1]); @@ -88,7 +92,7 @@ static int parse_args(upstype_t *ups, int numargs, char **arg) if (!strcasecmp(arg[0], "SETFLAGS")) { state_setflags(ups->inforoot, arg[1], numargs - 2, &arg[2]); return 1; - } + } /* SETINFO */ if (!strcasecmp(arg[0], "SETINFO")) { @@ -114,24 +118,51 @@ static int parse_args(upstype_t *ups, int numargs, char **arg) return 1; } + /* TRACKING */ + if (!strcasecmp(arg[0], "TRACKING")) { + tracking_set(arg[1], arg[2]); + upsdebugx(1, "TRACKING: ID %s status %s", arg[1], arg[2]); + + /* log actual result of instcmd / setvar */ + if (strncmp(arg[2], "PENDING", 7) != 0) { + upslogx(LOG_INFO, "tracking ID: %s\tresult: %s", arg[1], tracking_get(arg[1])); + } + return 1; + } + + if (numargs < 4) + return 0; + + /* ADDRANGE */ + if (!strcasecmp(arg[0], "ADDRANGE")) { + state_addrange(ups->inforoot, arg[1], atoi(arg[2]), atoi(arg[3])); + return 1; + } + + /* DELRANGE */ + if (!strcasecmp(arg[0], "DELRANGE")) { + state_delrange(ups->inforoot, arg[1], atoi(arg[2]), atoi(arg[3])); + return 1; + } + return 0; } /* nothing fancy - just make the driver say something back to us */ static void sendping(upstype_t *ups) { - int ret; + ssize_t ret; const char *cmd = "PING\n"; + size_t cmdlen = strlen(cmd); if ((!ups) || (ups->sock_fd < 0)) { return; } upsdebugx(3, "Pinging UPS [%s]", ups->name); + ret = write(ups->sock_fd, cmd, cmdlen); - ret = write(ups->sock_fd, cmd, strlen(cmd)); - - if (ret != (int)strlen(cmd)) { + if ((ret < 1) || (ret != (ssize_t)cmdlen)) { upslog_with_errno(LOG_NOTICE, "Send ping to UPS [%s] failed", ups->name); sstate_disconnect(ups); return; @@ -144,10 +175,14 @@ static void sendping(upstype_t *ups) int sstate_connect(upstype_t *ups) { - int ret, fd; + int fd; const char *dumpcmd = "DUMPALL\n"; + size_t dumpcmdlen = strlen(dumpcmd); + ssize_t ret; struct sockaddr_un sa; + check_unix_socket_filename(ups->fn); + memset(&sa, '\0', sizeof(sa)); sa.sun_family = AF_UNIX; snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", ups->fn); @@ -172,7 +207,7 @@ int sstate_connect(upstype_t *ups) return -1; ups->last_connfail = now; - upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s] (%s)", + upslog_with_errno(LOG_ERR, "Can't connect to UPS [%s] (%s)", ups->name, ups->fn); return -1; @@ -195,9 +230,9 @@ int sstate_connect(upstype_t *ups) } /* get a dump started so we have a fresh set of data */ - ret = write(fd, dumpcmd, strlen(dumpcmd)); + ret = write(fd, dumpcmd, dumpcmdlen); - if (ret != (int)strlen(dumpcmd)) { + if ((ret < 1) || (ret != (ssize_t)dumpcmdlen)) { upslog_with_errno(LOG_ERR, "Initial write to UPS [%s] failed", ups->name); close(fd); return -1; @@ -236,7 +271,7 @@ void sstate_disconnect(upstype_t *ups) void sstate_readline(upstype_t *ups) { - int i, ret; + ssize_t i, ret; char buf[SMALLBUF]; if ((!ups) || (ups->sock_fd < 0)) { @@ -266,7 +301,7 @@ void sstate_readline(upstype_t *ups) case 1: /* set the 'last heard' time to now for later staleness checks */ if (parse_args(ups, ups->sock_ctx.numargs, ups->sock_ctx.arglist)) { - time(&ups->last_heard); + time(&ups->last_heard); } continue; @@ -289,24 +324,29 @@ const char *sstate_getinfo(const upstype_t *ups, const char *var) int sstate_getflags(const upstype_t *ups, const char *var) { return state_getflags(ups->inforoot, var); -} +} -int sstate_getaux(const upstype_t *ups, const char *var) +long sstate_getaux(const upstype_t *ups, const char *var) { return state_getaux(ups->inforoot, var); -} +} const enum_t *sstate_getenumlist(const upstype_t *ups, const char *var) { return state_getenumlist(ups->inforoot, var); } +const range_t *sstate_getrangelist(const upstype_t *ups, const char *var) +{ + return state_getrangelist(ups->inforoot, var); +} + const cmdlist_t *sstate_getcmdlist(const upstype_t *ups) { return ups->cmdlist; } -int sstate_dead(upstype_t *ups, int maxage) +int sstate_dead(upstype_t *ups, int arg_maxage) { time_t now; double elapsed; @@ -328,12 +368,12 @@ int sstate_dead(upstype_t *ups, int maxage) elapsed = difftime(now, ups->last_heard); /* somewhere beyond a third of the maximum time - prod it to make it talk */ - if ((elapsed > (maxage / 3)) && (difftime(now, ups->last_ping) > (maxage / 3))) + if ((elapsed > (arg_maxage / 3)) && (difftime(now, ups->last_ping) > (arg_maxage / 3))) sendping(ups); - if (elapsed > maxage) { + if (elapsed > arg_maxage) { upsdebugx(3, "sstate_dead: didn't hear from driver for UPS [%s] for %g seconds (max %d)", - ups->name, elapsed, maxage); + ups->name, elapsed, arg_maxage); return 1; /* dead */ } @@ -357,16 +397,24 @@ void sstate_cmdfree(upstype_t *ups) int sstate_sendline(upstype_t *ups, const char *buf) { - int ret; + ssize_t ret; + size_t buflen; if ((!ups) ||(ups->sock_fd < 0)) { return 0; /* failed */ } - ret = write(ups->sock_fd, buf, strlen(buf)); + buflen = strlen(buf); + if (buflen >= SSIZE_MAX) { + /* Can't compare buflen to ret... */ + upslog_with_errno(LOG_NOTICE, "Send ping to UPS [%s] failed: buffered message too large", ups->name); + return 0; /* failed */ + } - if (ret == (int)strlen(buf)) { - return 1; + ret = write(ups->sock_fd, buf, buflen); + + if (ret == (ssize_t)buflen) { + return 1; } upslog_with_errno(LOG_NOTICE, "Send to UPS [%s] failed", ups->name); diff --git a/server/sstate.h b/server/sstate.h index 622b848..7a7fff6 100644 --- a/server/sstate.h +++ b/server/sstate.h @@ -3,6 +3,7 @@ Copyright (C) 2003 Russell Kroll 2008 Arjen de Korte + 2012 Arnaud Quette This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,8 +20,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef SSTATE_H_SEEN -#define SSTATE_H_SEEN +#ifndef NUT_SSTATE_H_SEEN +#define NUT_SSTATE_H_SEEN 1 #include "state.h" #include "upstype.h" @@ -28,13 +29,20 @@ #define SS_CONNFAIL_INT 300 /* complain about a dead driver every 5 mins */ #define SS_MAX_READ 256 /* don't let drivers tie us up in read() */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + int sstate_connect(upstype_t *ups); void sstate_disconnect(upstype_t *ups); void sstate_readline(upstype_t *ups); const char *sstate_getinfo(const upstype_t *ups, const char *var); int sstate_getflags(const upstype_t *ups, const char *var); -int sstate_getaux(const upstype_t *ups, const char *var); +long sstate_getaux(const upstype_t *ups, const char *var); const enum_t *sstate_getenumlist(const upstype_t *ups, const char *var); +const range_t *sstate_getrangelist(const upstype_t *ups, const char *var); const cmdlist_t *sstate_getcmdlist(const upstype_t *ups); void sstate_makeinfolist(const upstype_t *ups, char *buf, size_t bufsize); void sstate_makerwlist(const upstype_t *ups, char *buf, size_t bufsize); @@ -45,4 +53,10 @@ void sstate_cmdfree(upstype_t *ups); int sstate_sendline(upstype_t *ups, const char *buf); const st_tree_t *sstate_getnode(const upstype_t *ups, const char *varname); -#endif /* SSTATE_H_SEEN */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_SSTATE_H_SEEN */ diff --git a/server/stype.h b/server/stype.h index 0ad185d..54e5ff2 100644 --- a/server/stype.h +++ b/server/stype.h @@ -18,8 +18,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef STYPE_H_SEEN -#define STYPE_H_SEEN 1 +#ifndef NUT_STYPE_H_SEEN +#define NUT_STYPE_H_SEEN 1 #include @@ -31,6 +31,12 @@ #define NI_MAXSERV 32 #endif +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + typedef struct stype_s { char *addr; char *port; @@ -38,4 +44,10 @@ typedef struct stype_s { struct stype_s *next; } stype_t; -#endif /* STYPE_H_SEEN */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_STYPE_H_SEEN */ diff --git a/server/upsd.c b/server/upsd.c index db200aa..6299150 100644 --- a/server/upsd.c +++ b/server/upsd.c @@ -1,9 +1,10 @@ -/* upsd.c - watches ups state files and answers queries +/* upsd.c - watches ups state files and answers queries Copyright (C) - 1999 Russell Kroll - 2008 Arjen de Korte - 2011 Arnaud Quette + 1999 Russell Kroll + 2008 Arjen de Korte + 2011 - 2012 Arnaud Quette + 2019 Eaton (author: Arnaud Quette ) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,6 +21,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" /* must be the first header */ + #include "upsd.h" #include "upstype.h" #include "conf.h" @@ -30,12 +33,19 @@ #include #include #include -#include + +#ifdef HAVE_SYS_SIGNAL_H +#include +#endif +#ifdef HAVE_SIGNAL_H +#include +#endif #include "user.h" #include "nut_ctype.h" +#include "nut_stdint.h" #include "stype.h" -#include "ssl.h" +#include "netssl.h" #include "sstate.h" #include "desc.h" #include "neterr.h" @@ -46,29 +56,38 @@ int allow_severity = LOG_INFO; int deny_severity = LOG_WARNING; #endif /* HAVE_WRAP */ - /* externally-visible settings and pointers */ +/* externally-visible settings and pointers */ - upstype_t *firstups = NULL; +upstype_t *firstups = NULL; - /* default 15 seconds before data is marked stale */ - int maxage = 15; +/* default 15 seconds before data is marked stale */ +int maxage = 15; - /* preloaded to {OPEN_MAX} in main, can be overridden via upsd.conf */ - int maxconn = 0; +/* default to 1h before cleaning up status tracking entries */ +int tracking_delay = 3600; - /* preloaded to STATEPATH in main, can be overridden via upsd.conf */ - char *statepath = NULL; +/* + * Preloaded to ALLOW_NO_DEVICE from upsd.conf or environment variable + * (with higher prio for envvar); defaults to disabled for legacy compat. + */ +int allow_no_device = 0; - /* preloaded to DATADIR in main, can be overridden via upsd.conf */ - char *datapath = NULL; +/* preloaded to {OPEN_MAX} in main, can be overridden via upsd.conf */ +nfds_t maxconn = 0; - /* everything else */ - const char *progname; +/* preloaded to STATEPATH in main, can be overridden via upsd.conf */ +char *statepath = NULL; -static nut_ctype_t *firstclient = NULL; +/* preloaded to DATADIR in main, can be overridden via upsd.conf */ +char *datapath = NULL; + +/* everything else */ +static const char *progname; + +nut_ctype_t *firstclient = NULL; /* static nut_ctype_t *lastclient = NULL; */ - /* default is to listen on all local interfaces */ +/* default is to listen on all local interfaces */ static stype_t *firstaddr = NULL; static int opt_af = AF_UNSPEC; @@ -84,6 +103,28 @@ typedef struct { void *data; } handler_t; + +/* Commands and settings status tracking */ + +/* general enable/disable status info for commands and settings + * (disabled by default) + * Note that only client that requested it will have it enabled + * (see nut_ctype.h) */ +static int tracking_enabled = 0; + +/* Commands and settings status tracking structure */ +typedef struct tracking_s { + char *id; + int status; + time_t request_time; /* for cleanup */ + /* doubly linked list */ + struct tracking_s *prev; + struct tracking_s *next; +} tracking_t; + +static tracking_t *tracking_list = NULL; + + /* pollfd */ static struct pollfd *fds = NULL; static handler_t *handler = NULL; @@ -94,6 +135,11 @@ static char pidfn[SMALLBUF]; /* set by signal handlers */ static int reload_flag = 0, exit_flag = 0; +/* Minimalistic support for UUID v4 */ +/* Ref: RFC 4122 https://tools.ietf.org/html/rfc4122#section-4.1.2 */ +#define UUID4_BYTESIZE 16 + + static const char *inet_ntopW (struct sockaddr_storage *s) { static char str[40]; @@ -204,7 +250,7 @@ static void setuptcp(stype_t *server) upsdebug_with_errno(3, "setuptcp: socket"); continue; } - + if (setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, (void *)&one, sizeof(one)) != 0) { fatal_with_errno(EXIT_FAILURE, "setuptcp: setsockopt"); } @@ -308,12 +354,15 @@ static void client_disconnect(nut_ctype_t *client) return; } -/* send the buffer of length to host */ +/* send the buffer of length to host + * returns effectively a boolean: 0 = failed, 1 = sent ok + */ int sendback(nut_ctype_t *client, const char *fmt, ...) { - int res, len; - char ans[NUT_NET_ANSWER_MAX+1]; - va_list ap; + ssize_t res; + size_t len; + char ans[NUT_NET_ANSWER_MAX+1]; + va_list ap; if (!client) { return 0; @@ -325,15 +374,23 @@ int sendback(nut_ctype_t *client, const char *fmt, ...) len = strlen(ans); + /* System write() and our ssl_write() have a loophole that they write a + * size_t amount of bytes and upon success return that in ssize_t value + */ + assert(len < SSIZE_MAX); + +#ifdef WITH_SSL if (client->ssl) { res = ssl_write(client, ans, len); - } else { + } else +#endif /* WITH_SSL */ + { res = write(client->sock_fd, ans, len); } - upsdebugx(2, "write: [destfd=%d] [len=%d] [%s]", client->sock_fd, len, rtrim(ans, '\n')); + upsdebugx(2, "write: [destfd=%d] [len=%zu] [%s]", client->sock_fd, len, str_rtrim(ans, '\n')); - if (len != res) { + if (res < 0 || len != (size_t)res) { upslog_with_errno(LOG_NOTICE, "write() failed for %s", client->addr); client->last_heard = 0; return 0; /* failed */ @@ -393,20 +450,25 @@ int ups_available(const upstype_t *ups, nut_ctype_t *client) } /* check flags and access for an incoming command from the network */ -static void check_command(int cmdnum, nut_ctype_t *client, int numarg, +static void check_command(int cmdnum, nut_ctype_t *client, size_t numarg, const char **arg) { + upsdebugx(6, "Entering %s: %s", __func__, numarg > 0 ? arg[0] : "<>"); + if (netcmds[cmdnum].flags & FLAG_USER) { + /* command requires previous authentication */ #ifdef HAVE_WRAP struct request_info req; #endif /* HAVE_WRAP */ if (!client->username) { + upsdebugx(1, "%s: client not logged in yet", __func__); send_err(client, NUT_ERR_USERNAME_REQUIRED); return; } if (!client->password) { + upsdebugx(1, "%s: client not logged in yet", __func__); send_err(client, NUT_ERR_PASSWORD_REQUIRED); return; } @@ -416,15 +478,20 @@ static void check_command(int cmdnum, nut_ctype_t *client, int numarg, fromhost(&req); if (!hosts_access(&req)) { - /* tcp-wrappers says access should be denied */ + upsdebugx(1, + "%s: while authenticating %s found that " + "tcp-wrappers says access should be denied", + __func__, client->username); send_err(client, NUT_ERR_ACCESS_DENIED); return; } #endif /* HAVE_WRAP */ } + upsdebugx(6, "%s: Calling command handler for %s", __func__, numarg > 0 ? arg[0] : "<>"); + /* looks good - call the command */ - netcmds[cmdnum].func(client, numarg - 1, &arg[1]); + netcmds[cmdnum].func(client, (numarg < 2) ? 0 : (numarg - 1), (numarg > 1) ? &arg[1] : NULL); } /* parse requests from the network */ @@ -454,7 +521,7 @@ static void parse_net(nut_ctype_t *client) static void client_connect(stype_t *server) { struct sockaddr_storage csock; -#if defined(__hpux) && !defined(_XOPEN_SOURCE_EXTENDED) +#if defined(__hpux) && !defined(_XOPEN_SOURCE_EXTENDED) int clen; #else socklen_t clen; @@ -477,6 +544,8 @@ static void client_connect(stype_t *server) client->addr = xstrdup(inet_ntopW(&csock)); + client->tracking = 0; + pconf_init(&client->ctx, NULL); if (firstclient) { @@ -501,11 +570,15 @@ static void client_connect(stype_t *server) static void client_readline(nut_ctype_t *client) { char buf[SMALLBUF]; - int i, ret; + int i; + ssize_t ret; +#ifdef WITH_SSL if (client->ssl) { ret = ssl_read(client, buf, sizeof(buf)); - } else { + } else +#endif /* WITH_SSL */ + { ret = read(client->sock_fd, buf, sizeof(buf)); } @@ -563,7 +636,7 @@ void server_load(void) for (server = firstaddr; server; server = server->next) { setuptcp(server); } - + /* check if we have at least 1 valid LISTEN interface */ if (firstaddr->sock_fd < 0) { fatalx(EXIT_FAILURE, "no listening interface available"); @@ -601,11 +674,14 @@ static void client_free(void) } } -void driver_free(void) +static void driver_free(void) { upstype_t *ups, *unext; for (ups = firstups; ups; ups = unext) { + upsdebugx(1, "%s: forgetting UPS [%s] (FD %d)", + __func__, ups->name, ups->sock_fd); + unext = ups->next; if (ups->sock_fd != -1) { @@ -634,40 +710,272 @@ static void upsd_cleanup(void) user_flush(); desc_free(); - + server_free(); client_free(); driver_free(); + tracking_free(); free(statepath); free(datapath); free(certfile); + free(certname); + free(certpasswd); free(fds); free(handler); } -void poll_reload(void) +static void poll_reload(void) { - int ret; + long ret; ret = sysconf(_SC_OPEN_MAX); - if (ret < maxconn) { + if ((intmax_t)ret < (intmax_t)maxconn) { fatalx(EXIT_FAILURE, - "Your system limits the maximum number of connections to %d\n" - "but you requested %d. The server won't start until this\n" - "problem is resolved.\n", ret, maxconn); + "Your system limits the maximum number of connections to %ld\n" + "but you requested %jd. The server won't start until this\n" + "problem is resolved.\n", ret, (intmax_t)maxconn); } - fds = xrealloc(fds, maxconn * sizeof(*fds)); - handler = xrealloc(handler, maxconn * sizeof(*handler)); + if (1 > maxconn) { + fatalx(EXIT_FAILURE, + "You requested %jd as maximum number of connections.\n" + "The server won't start until this problem is resolved.\n", (intmax_t)maxconn); + } + + /* How many items can we stuff into the array? */ + size_t maxalloc = SIZE_MAX / sizeof(void *); + if ((uintmax_t)maxalloc < (uintmax_t)maxconn) { + fatalx(EXIT_FAILURE, + "You requested %jd as maximum number of connections, but we can only allocate %zu.\n" + "The server won't start until this problem is resolved.\n", (intmax_t)maxconn, maxalloc); + } + + /* The checks above effectively limit that maxconn is in size_t range */ + fds = xrealloc(fds, (size_t)maxconn * sizeof(*fds)); + handler = xrealloc(handler, (size_t)maxconn * sizeof(*handler)); +} + +/* instant command and setvar status tracking */ + +/* allocate a new status tracking entry */ +int tracking_add(const char *id) +{ + tracking_t *item; + + if ((!tracking_enabled) || (!id)) + return 0; + + item = xcalloc(1, sizeof(*item)); + + item->id = xstrdup(id); + item->status = STAT_PENDING; + time(&item->request_time); + + if (tracking_list) { + tracking_list->prev = item; + item->next = tracking_list; + } + + tracking_list = item; + + return 1; +} + +/* set status of a specific tracking entry */ +int tracking_set(const char *id, const char *value) +{ + tracking_t *item, *next_item; + + /* sanity checks */ + if ((!tracking_list) || (!id) || (!value)) + return 0; + + for (item = tracking_list; item; item = next_item) { + + next_item = item->next; + + if (!strcasecmp(item->id, id)) { + item->status = atoi(value); + return 1; + } + } + + return 0; /* id not found! */ +} + +/* free a specific tracking entry */ +int tracking_del(const char *id) +{ + tracking_t *item, *next_item; + + /* sanity check */ + if ((!tracking_list) || (!id)) + return 0; + + upsdebugx(3, "%s: deleting id %s", __func__, id); + + for (item = tracking_list; item; item = next_item) { + + next_item = item->next; + + if (strcasecmp(item->id, id)) + continue; + + if (item->prev) + item->prev->next = item->next; + else + /* deleting first entry */ + tracking_list = item->next; + + if (item->next) + item->next->prev = item->prev; + + free(item->id); + free(item); + + return 1; + + } + + return 0; /* id not found! */ +} + +/* free all status tracking entries */ +void tracking_free(void) +{ + tracking_t *item, *next_item; + + /* sanity check */ + if (!tracking_list) + return; + + upsdebugx(3, "%s", __func__); + + for (item = tracking_list; item; item = next_item) { + next_item = item->next; + tracking_del(item->id); + } +} + +/* cleanup status tracking entries according to their age and tracking_delay */ +void tracking_cleanup(void) +{ + tracking_t *item, *next_item; + time_t now; + + /* sanity check */ + if (!tracking_list) + return; + + time(&now); + + upsdebugx(3, "%s", __func__); + + for (item = tracking_list; item; item = next_item) { + + next_item = item->next; + + if (difftime(now, item->request_time) > tracking_delay) { + tracking_del(item->id); + } + } +} + +/* get status of a specific tracking entry */ +char *tracking_get(const char *id) +{ + tracking_t *item, *next_item; + + /* sanity checks */ + if ((!tracking_list) || (!id)) + return "ERR UNKNOWN"; + + for (item = tracking_list; item; item = next_item) { + + next_item = item->next; + + if (strcasecmp(item->id, id)) + continue; + + switch (item->status) + { + case STAT_PENDING: + return "PENDING"; + case STAT_HANDLED: + return "SUCCESS"; + case STAT_UNKNOWN: + return "ERR UNKNOWN"; + case STAT_INVALID: + return "ERR INVALID-ARGUMENT"; + case STAT_FAILED: + return "ERR FAILED"; + } + } + + return "ERR UNKNOWN"; /* id not found! */ +} + +/* enable general status tracking (tracking_enabled) and return its value (1). */ +int tracking_enable(void) +{ + tracking_enabled = 1; + + return tracking_enabled; +} + +/* disable general status tracking only if no client use it anymore. + * return the new value for tracking_enabled */ +int tracking_disable(void) +{ + nut_ctype_t *client, *cnext; + + for (client = firstclient; client; client = cnext) { + cnext = client->next; + if (client->tracking == 1) + return 1; + } + return 0; +} + +/* return current general status of tracking (tracking_enabled). */ +int tracking_is_enabled(void) +{ + return tracking_enabled; +} + +/* UUID v4 basic implementation + * Note: 'dest' must be at least `UUID4_LEN` long */ +int nut_uuid_v4(char *uuid_str) +{ + size_t i; + uint8_t nut_uuid[UUID4_BYTESIZE]; + + if (!uuid_str) + return 0; + + for (i = 0; i < UUID4_BYTESIZE; i++) + nut_uuid[i] = (uint8_t)rand() + (uint8_t)rand(); + + /* set variant and version */ + nut_uuid[6] = (nut_uuid[6] & 0x0F) | 0x40; + nut_uuid[8] = (nut_uuid[8] & 0x3F) | 0x80; + + return snprintf(uuid_str, UUID4_LEN, + "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + nut_uuid[0], nut_uuid[1], nut_uuid[2], nut_uuid[3], + nut_uuid[4], nut_uuid[5], nut_uuid[6], nut_uuid[7], + nut_uuid[8], nut_uuid[9], nut_uuid[10], nut_uuid[11], + nut_uuid[12], nut_uuid[13], nut_uuid[14], nut_uuid[15]); } /* service requests and check on new data */ static void mainloop(void) { - int i, ret, nfds = 0; + int ret; + nfds_t i, nfds = 0; upstype_t *ups; nut_ctype_t *client, *cnext; @@ -682,12 +990,19 @@ static void mainloop(void) reload_flag = 0; } + /* cleanup instcmd/setvar status tracking entries if needed */ + tracking_cleanup(); + /* scan through driver sockets */ for (ups = firstups; ups && (nfds < maxconn); ups = ups->next) { /* see if we need to (re)connect to the socket */ if (ups->sock_fd < 0) { + upsdebugx(1, "%s: UPS [%s] is not currently connected", + __func__, ups->name); ups->sock_fd = sstate_connect(ups); + upsdebugx(1, "%s: UPS [%s] is now connected as FD %d", + __func__, ups->name, ups->sock_fd); continue; } @@ -714,6 +1029,7 @@ static void mainloop(void) if (difftime(now, client->last_heard) > 60) { /* shed clients after 1 minute of inactivity */ + /* FIXME: create an upsd.conf parameter (CLIENT_INACTIVITY_DELAY) */ client_disconnect(client); continue; } @@ -748,7 +1064,7 @@ static void mainloop(void) nfds++; } - upsdebugx(2, "%s: polling %d filedescriptors", __func__, nfds); + upsdebugx(2, "%s: polling %jd filedescriptors", __func__, (intmax_t)nfds); ret = poll(fds, nfds, 2000); @@ -777,9 +1093,36 @@ static void mainloop(void) case SERVER: upsdebugx(2, "%s: server disconnected", __func__); break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: upsdebugx(2, "%s: disconnected", __func__); break; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif + } continue; @@ -798,9 +1141,35 @@ static void mainloop(void) case SERVER: client_connect((stype_t *)handler[i].data); break; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT +# pragma GCC diagnostic ignored "-Wcovered-switch-default" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +# pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +/* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */ +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + /* All enum cases defined as of the time of coding + * have been covered above. Handle later definitions, + * memory corruptions and buggy inputs below... + */ default: upsdebugx(2, "%s: has data available", __func__); break; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) ) +# pragma GCC diagnostic pop +#endif } continue; @@ -808,17 +1177,24 @@ static void mainloop(void) } } -static void help(const char *progname) +static void help(const char *arg_progname) + __attribute__((noreturn)); + +static void help(const char *arg_progname) { printf("Network server for UPS data.\n\n"); - printf("usage: %s [OPTIONS]\n", progname); + printf("usage: %s [OPTIONS]\n", arg_progname); printf("\n"); printf(" -c send via signal to background process\n"); printf(" commands:\n"); printf(" - reload: reread configuration files\n"); printf(" - stop: stop process and exit\n"); - printf(" -D raise debugging level\n"); + printf(" -P send the signal above to specified PID (bypassing PID file)\n"); + printf(" -D raise debugging level (and stay foreground by default)\n"); + printf(" -F stay foregrounded even if no debugging is enabled\n"); + printf(" -FF stay foregrounded and still save the PID file\n"); + printf(" -B stay backgrounded even if debugging is bumped\n"); printf(" -h display this help\n"); printf(" -r chroots to \n"); printf(" -q raise log level threshold\n"); @@ -832,6 +1208,7 @@ static void help(const char *progname) static void set_reload_flag(int sig) { + NUT_UNUSED_VARIABLE(sig); reload_flag = 1; } @@ -849,7 +1226,14 @@ static void setup_signals(void) sa.sa_flags = 0; /* basic signal setup to ignore SIGPIPE */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif sa.sa_handler = SIG_IGN; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic pop +#endif sigaction(SIGPIPE, &sa, NULL); /* handle shutdown signals */ @@ -882,10 +1266,11 @@ void check_perms(const char *fn) int main(int argc, char **argv) { - int i, cmd = 0; + int i, cmd = 0, cmdret = 0, foreground = -1; char *chroot_path = NULL; const char *user = RUN_AS_USER; struct passwd *new_uid = NULL; + pid_t oldpid = -1; progname = xbasename(argv[0]); @@ -898,25 +1283,29 @@ int main(int argc, char **argv) printf("Network UPS Tools %s %s\n", progname, UPS_VERSION); - while ((i = getopt(argc, argv, "+h46p:qr:i:fu:Vc:D")) != -1) { + while ((i = getopt(argc, argv, "+h46p:qr:i:fu:Vc:P:DFB")) != -1) { switch (i) { - case 'h': - help(progname); - break; case 'p': case 'i': fatalx(EXIT_FAILURE, "Specifying a listening addresses with '-i
' and '-p '\n" "is deprecated. Use 'LISTEN
[]' in 'upsd.conf' instead.\n" "See 'man 8 upsd.conf' for more information."); +#ifndef HAVE___ATTRIBUTE__NORETURN + exit(EXIT_FAILURE); /* Should not get here in practice, but compiler is afraid we can fall through */ +#endif + case 'q': nut_log_level++; break; + case 'r': chroot_path = optarg; break; + case 'u': user = optarg; break; + case 'V': /* do nothing - we already printed the banner */ exit(EXIT_SUCCESS); @@ -932,9 +1321,25 @@ int main(int argc, char **argv) help(progname); break; + case 'P': + if ((oldpid = parsepid(optarg)) < 0) + help(progname); + break; + case 'D': nut_debug_level++; break; + case 'F': + if (foreground > 0) { + /* specified twice to save PID file anyway */ + foreground = 2; + } else { + foreground = 1; + } + break; + case 'B': + foreground = 0; + break; case '4': opt_af = AF_INET; @@ -944,15 +1349,56 @@ int main(int argc, char **argv) opt_af = AF_INET6; break; + case 'h': default: help(progname); - break; + } + } + + if (foreground < 0) { + if (nut_debug_level > 0) { + foreground = 1; + } else { + foreground = 0; } } if (cmd) { - sendsignalfn(pidfn, cmd); - exit(EXIT_SUCCESS); + if (oldpid < 0) { + cmdret = sendsignalfn(pidfn, cmd); + } else { + cmdret = sendsignalpid(oldpid, cmd); + } + exit((cmdret == 0)?EXIT_SUCCESS:EXIT_FAILURE); + } + + /* otherwise, we are being asked to start. + * so check if a previous instance is running by sending signal '0' + * (Ie 'kill 0') */ + if (oldpid < 0) { + cmdret = sendsignalfn(pidfn, 0); + } else { + cmdret = sendsignalpid(oldpid, 0); + } + switch (cmdret) { + case 0: + printf("Fatal error: A previous upsd instance is already running!\n"); + printf("Either stop the previous instance first, or use the 'reload' command.\n"); + exit(EXIT_FAILURE); + + case -3: + case -2: + upslogx(LOG_WARNING, "Could not %s PID file '%s' " + "to see if previous upsd instance is " + "already running!\n", + (cmdret == -3 ? "find" : "parse"), + pidfn); + break; + + case -1: + default: + /* Just failed to send signal, no competitor running */ + break; } argc -= optind; @@ -978,18 +1424,46 @@ int main(int argc, char **argv) chroot_start(chroot_path); } - /* default to system limit (may be overridden in upsd.conf */ - maxconn = sysconf(_SC_OPEN_MAX); + /* default to system limit (may be overridden in upsd.conf) */ + /* FIXME: Check for overflows (and int size of nfds_t vs. long) - see get_max_pid_t() for example */ + maxconn = (nfds_t)sysconf(_SC_OPEN_MAX); /* handle upsd.conf */ load_upsdconf(0); /* 0 = initial */ + /* CLI debug level can not be smaller than debug_min specified + * in upsd.conf. Note that non-zero debug_min does not impact + * foreground running mode. + */ + if (nut_debug_level_global > nut_debug_level) + nut_debug_level = nut_debug_level_global; + upsdebugx(1, "debug level is '%d'", nut_debug_level); + + { /* scope */ + /* As documented above, the ALLOW_NO_DEVICE can be provided via + * envvars and then has higher priority than an upsd.conf setting + */ + const char *envvar = getenv("ALLOW_NO_DEVICE"); + if ( envvar != NULL) { + if ( (!strncasecmp("TRUE", envvar, 4)) || (!strncasecmp("YES", envvar, 3)) || (!strncasecmp("ON", envvar, 2)) || (!strncasecmp("1", envvar, 1)) ) { + /* Admins of this server expressed a desire to serve + * anything on the NUT protocol, even if nothing is + * configured yet - tell the clients so, properly. + */ + allow_no_device = 1; + } else if ( (!strncasecmp("FALSE", envvar, 5)) || (!strncasecmp("NO", envvar, 2)) || (!strncasecmp("OFF", envvar, 3)) || (!strncasecmp("0", envvar, 1)) ) { + /* Admins of this server expressed a desire to serve + * anything on the NUT protocol, even if nothing is + * configured yet - tell the clients so, properly. + */ + allow_no_device = 0; + } + } + } /* scope */ + /* start server */ server_load(); - /* initialize SSL before we drop privileges (we may not be able to read the keyfile as non-root) */ - ssl_init(); - become_user(new_uid); if (chdir(statepath)) { @@ -1005,7 +1479,11 @@ int main(int argc, char **argv) poll_reload(); if (num_ups == 0) { - fatalx(EXIT_FAILURE, "Fatal error: at least one UPS must be defined in ups.conf"); + if (allow_no_device) { + upslogx(LOG_WARNING, "Normally at least one UPS must be defined in ups.conf, currently there are none (please configure the file and reload the service)"); + } else { + fatalx(EXIT_FAILURE, "Fatal error: at least one UPS must be defined in ups.conf"); + } } /* try to bring in the var/cmd descriptions */ @@ -1014,17 +1492,28 @@ int main(int argc, char **argv) /* handle upsd.users */ user_load(); - if (!nut_debug_level) { + if (!foreground) { background(); writepid(pidfn); } else { - memset(pidfn, 0, sizeof(pidfn)); + if (foreground == 2) { + upslogx(LOG_WARNING, "Running as foreground process, but saving a PID file anyway"); + writepid(pidfn); + } else { + upslogx(LOG_WARNING, "Running as foreground process, not saving a PID file"); + memset(pidfn, 0, sizeof(pidfn)); + } } + /* initialize SSL (keyfile must be readable by nut user) */ + ssl_init(); + while (!exit_flag) { mainloop(); } + ssl_cleanup(); + upslogx(LOG_INFO, "Signal %d: exiting", exit_flag); return EXIT_SUCCESS; } diff --git a/server/upsd.h b/server/upsd.h index 85985fe..1380188 100644 --- a/server/upsd.h +++ b/server/upsd.h @@ -38,6 +38,7 @@ #include "timehead.h" #include +#include /* nfds_t */ #include "parseconf.h" #include "nut_ctype.h" @@ -45,6 +46,12 @@ #define NUT_NET_ANSWER_MAX SMALLBUF +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + /* prototypes from upsd.c */ upstype_t *get_ups_ptr(const char *upsname); @@ -62,11 +69,33 @@ void server_free(void); void check_perms(const char *fn); -/* declarations from upsd.c */ +/* return values for instcmd / setvar status tracking, + * mapped on drivers/upshandler.h, apart from STAT_PENDING (initial state) */ +enum { + STAT_PENDING = -1, /* not yet completed */ + STAT_HANDLED = 0, /* completed successfully (NUT_SUCCESS or "OK") */ + STAT_UNKNOWN, /* unspecified error (NUT_ERR_UNKNOWN) */ + STAT_INVALID, /* invalid command/setvar (NUT_ERR_INVALID_ARGUMENT) */ + STAT_FAILED /* command/setvar failed (NUT_ERR_INSTCMD_FAILED / NUT_ERR_SET_FAILED) */ +}; -extern int maxage, maxconn; +/* Commands and settings status tracking functions */ +int tracking_add(const char *id); +int tracking_set(const char *id, const char *value); +int tracking_del(const char *id); +void tracking_free(void); +void tracking_cleanup(void); +char *tracking_get(const char *id); +int tracking_enable(void); +int tracking_disable(void); +int tracking_is_enabled(void); + +/* declarations from upsd.c */ +extern int maxage, tracking_delay, allow_no_device; +extern nfds_t maxconn; extern char *statepath, *datapath; extern upstype_t *firstups; +extern nut_ctype_t *firstclient; /* map commands onto signals */ @@ -84,4 +113,14 @@ extern upstype_t *firstups; #define shutdown_how 2 #endif +/* UUID v4 generation function + * Note: 'dest' must be at least `UUID4_LEN` long */ +int nut_uuid_v4(char *uuid_str); + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + #endif /* UPSD_H_SEEN */ diff --git a/server/upstype.h b/server/upstype.h index 18c0c42..bc03187 100644 --- a/server/upstype.h +++ b/server/upstype.h @@ -19,11 +19,17 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef UPSTYPE_H_SEEN -#define UPSTYPE_H_SEEN 1 +#ifndef NUT_UPSTYPE_H_SEEN +#define NUT_UPSTYPE_H_SEEN 1 #include "parseconf.h" +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + /* structure for the linked list of each UPS that we track */ typedef struct upstype_s { char *name; @@ -45,11 +51,17 @@ typedef struct upstype_s { int fsd; /* forced shutdown in effect? */ int retain; - + struct upstype_s *next; } upstype_t; extern upstype_t *firstups; -#endif /* UPSTYPE_H_SEEN */ +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_UPSTYPE_H_SEEN */ diff --git a/server/user-data.h b/server/user-data.h index d4ef5be..d5da7b4 100644 --- a/server/user-data.h +++ b/server/user-data.h @@ -1,6 +1,11 @@ /* user-data.h - structures for user.c Copyright (C) 2001 Russell Kroll + 2005 Arnaud Quette + 2007 Peter Selinger + 2008 Arjen de Korte + 2013 Emilien Kia + 2020 Jim Klimov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,6 +22,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef NUT_USERDATA_H_SEEN +#define NUT_USERDATA_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + typedef struct { char *cmd; void *next; @@ -34,3 +48,11 @@ typedef struct { actionlist_t *firstaction; void *next; } ulist_t; + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_USERDATA_H_SEEN */ diff --git a/server/user.c b/server/user.c index e443eee..b0c985b 100644 --- a/server/user.c +++ b/server/user.c @@ -17,6 +17,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include "config.h" /* must be the first header */ + #include #include #include @@ -28,9 +30,9 @@ #include "user.h" #include "user-data.h" - ulist_t *users = NULL; +static ulist_t *users = NULL; - static ulist_t *curr_user; +static ulist_t *curr_user; /* create a new user entry */ static void user_add(const char *un) @@ -57,7 +59,7 @@ static void user_add(const char *un) if (last) { last->next = tmp; } else { - users = tmp; + users = tmp; } /* remember who we're working on */ @@ -78,7 +80,7 @@ static void user_password(const char *pw) } if (curr_user->password) { - fprintf(stderr, "Ignoring duplicate password for %s\n", + fprintf(stderr, "Ignoring duplicate password for %s\n", curr_user->username); return; } @@ -111,6 +113,9 @@ static void user_add_instcmd(const char *cmd) } } + upsdebugx(2, "user_add_instcmd: adding '%s' for %s", + cmd, curr_user->username); + tmp = xcalloc(1, sizeof(*tmp)); tmp->cmd = xstrdup(cmd); @@ -155,6 +160,9 @@ static void user_add_action(const char *act) return; } + upsdebugx(2, "user_add_action: adding '%s' for %s", + act, curr_user->username); + curr_user->firstaction = addaction(curr_user->firstaction, act); } @@ -311,19 +319,20 @@ int user_checkaction(const char *un, const char *pw, const char *action) return 0; /* fail */ } -/* handle "upsmon master" and "upsmon slave" for nicer configurations */ +/* handle "upsmon primary" and "upsmon secondary" for nicer configurations */ +/* FIXME: Protocol update needed to handle master/primary alias (in action and in protocol) */ static void set_upsmon_type(char *type) { - /* master: login, master, fsd */ - if (!strcasecmp(type, "master")) { + /* primary: login, master, fsd */ + if (!strcasecmp(type, "master") || !strcasecmp(type, "primary")) { user_add_action("login"); - user_add_action("master"); + user_add_action("master"); /* Note: this is linked to "MASTER" API command permission */ user_add_action("fsd"); return; } - /* slave: just login */ - if (!strcasecmp(type, "slave")) { + /* secondary: just login */ + if (!strcasecmp(type, "slave") || !strcasecmp(type, "secondary")) { user_add_action("login"); return; } @@ -364,9 +373,9 @@ static void parse_var(char *var, char *val) } /* parse first var+val pair, then flip through remaining vals */ -static void parse_rest(char *var, char *fval, char **arg, int next, int left) +static void parse_rest(char *var, char *fval, char **arg, size_t next, size_t left) { - int i; + size_t i; /* no globals supported yet, so there's no sense in continuing */ if (!curr_user) { @@ -384,7 +393,7 @@ static void parse_rest(char *var, char *fval, char **arg, int next, int left) } } -static void user_parse_arg(int numargs, char **arg) +static void user_parse_arg(size_t numargs, char **arg) { char *ep; @@ -408,7 +417,7 @@ static void user_parse_arg(int numargs, char **arg) /* 0 1 2 ... */ /* foo=bar ... */ - parse_rest(arg[0], ep+1, arg, 1, numargs - 1); + parse_rest(arg[0], ep+1, arg, 1, (numargs < 2) ? 0 : (numargs - 1)); return; } @@ -441,8 +450,8 @@ static void user_parse_arg(int numargs, char **arg) /* foo = bar ... */ /* parse first var/val, plus subsequent values (if any) */ - - parse_rest(arg[0], arg[2], arg, 3, numargs - 3); + + parse_rest(arg[0], arg[2], arg, 3, (numargs < 4) ? 0 : (numargs - 3)); return; } diff --git a/server/user.h b/server/user.h index df10eb2..613a0c5 100644 --- a/server/user.h +++ b/server/user.h @@ -1,6 +1,11 @@ /* user.c - supporting elements of user handling functions for upsd Copyright (C) 2001 Russell Kroll + 2005 Arnaud Quette + 2007 Peter Selinger + 2008 Arjen de Korte + 2013 Emilien Kia + 2020 Jim Klimov This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -17,6 +22,15 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifndef NUT_USER_H_SEEN +#define NUT_USER_H_SEEN 1 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + void user_load(void); int user_checkinstcmd(const char *un, const char *pw, const char *cmd); @@ -26,3 +40,11 @@ void user_flush(void); /* cheat - we don't want the full upsd.h included here */ void check_perms(const char *fn); + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + +#endif /* NUT_USER_H_SEEN */ diff --git a/test-driver b/test-driver new file mode 100755 index 0000000..9759384 --- /dev/null +++ b/test-driver @@ -0,0 +1,150 @@ +#! /bin/sh +# test-driver - basic testsuite driver script. + +scriptversion=2018-03-07.03; # UTC + +# Copyright (C) 2011-2020 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +# Make unconditional expansion of undefined variables an error. This +# helps a lot in preventing typo-related bugs. +set -u + +usage_error () +{ + echo "$0: $*" >&2 + print_usage >&2 + exit 2 +} + +print_usage () +{ + cat <$log_file 2>&1 +estatus=$? + +if test $enable_hard_errors = no && test $estatus -eq 99; then + tweaked_estatus=1 +else + tweaked_estatus=$estatus +fi + +case $tweaked_estatus:$expect_failure in + 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; + 0:*) col=$grn res=PASS recheck=no gcopy=no;; + 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; + 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; + *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; + *:*) col=$red res=FAIL recheck=yes gcopy=yes;; +esac + +# Report the test outcome and exit status in the logs, so that one can +# know whether the test passed or failed simply by looking at the '.log' +# file, without the need of also peaking into the corresponding '.trs' +# file (automake bug#11814). +echo "$res $test_name (exit status: $estatus)" >>$log_file + +# Report outcome to console. +echo "${col}${res}${std}: $test_name" + +# Register the test result, and other relevant metadata. +echo ":test-result: $res" > $trs_file +echo ":global-test-result: $res" >> $trs_file +echo ":recheck: $recheck" >> $trs_file +echo ":copy-in-global-log: $gcopy" >> $trs_file + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..e9cfd70 --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,117 @@ +# Network UPS Tools: tests + +SUBDIRS = . NIT + +all: $(TESTS) + +EXTRA_DIST = nut-driver-enumerator-test.sh nut-driver-enumerator-test--ups.conf + +TESTS = nutlogtest +CLEANFILES = *.trs *.log + +AM_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/drivers +AM_CXXFLAGS = -I$(top_srcdir)/include + +check_PROGRAMS = $(TESTS) + +# NUT Integration Testing suite +check-NIT check-NIT-devel: + cd "$(builddir)/NIT" && $(MAKE) $@ + +nutlogtest_SOURCES = nutlogtest.c +nutlogtest_LDADD = $(top_builddir)/common/libcommon.la + +# Separate the .deps of other dirs from this one +LINKED_SOURCE_FILES = hidparser.c + +# NOTE: Not using "$<" due to a legacy Sun/illumos dmake bug with resolver +# of dynamic vars, see e.g. https://man.omnios.org/man1/make#BUGS +hidparser.c: $(top_srcdir)/drivers/hidparser.c + test -s "$@" || ln -s -f "$(top_srcdir)/drivers/hidparser.c" "$@" + +if WITH_USB +TESTS += getvaluetest + +getvaluetest_SOURCES = getvaluetest.c +nodist_getvaluetest_SOURCES = hidparser.c +# Pull the right include path for chosen libusb version: +getvaluetest_CFLAGS = $(AM_CFLAGS) $(LIBUSB_CFLAGS) +getvaluetest_LDADD = $(top_builddir)/common/libcommon.la +endif + +# Make sure out-of-dir dependencies exist (especially when dev-building parts): +$(top_builddir)/common/libcommon.la: dummy + @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +### Optional tests which can not be built everywhere +# List of src files for CppUnit tests +CPPUNITTESTSRC = example.cpp nutclienttest.cpp +# The test driver which orchestrates running those tests above +CPPUNITTESTERSRC = cpputest.cpp + +CPPCLIENTTESTSRC = cpputest-client.cpp + +TESTS_CXX11 = cppunittest + +if HAVE_CXX11 +if HAVE_CPPUNIT +# Note: per configure script this "SHOULD" also assume +# that we HAVE_CXX11 - but better have it explicit + +TESTS += $(TESTS_CXX11) + +# Note: we only build it, but do not run directly (NIT prepares the sandbox) +check_PROGRAMS += cppnit + +if WITH_VALGRIND +check-local: $(check_PROGRAMS) + RES=0; for P in $^ ; do $(VALGRIND) ./$$P || { RES=$$? ; echo "FAILED: $(VALGRIND) ./$$P" >&2; }; done; exit $$RES +endif + +cppunittest_CXXFLAGS = $(AM_CXXFLAGS) $(CPPUNIT_CFLAGS) $(CPPUNIT_CXXFLAGS) $(CPPUNIT_NUT_CXXFLAGS) $(CXXFLAGS) +cppunittest_LDFLAGS = $(CPPUNIT_LDFLAGS) $(CPPUNIT_LIBS) +cppunittest_LDADD = $(top_builddir)/clients/libnutclient.la $(top_builddir)/clients/libnutclientstub.la +cppunittest_SOURCES = $(CPPUNITTESTSRC) $(CPPUNITTESTERSRC) + +cppnit_CXXFLAGS = $(AM_CXXFLAGS) $(CPPUNIT_CFLAGS) $(CPPUNIT_CXXFLAGS) $(CPPUNIT_NUT_CXXFLAGS) $(CXXFLAGS) +cppnit_LDFLAGS = $(CPPUNIT_LDFLAGS) $(CPPUNIT_LIBS) +cppnit_LDADD = $(top_builddir)/clients/libnutclient.la $(top_builddir)/clients/libnutclientstub.la +cppnit_SOURCES = $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) + +# Make sure out-of-dir C++ dependencies exist (especially when dev-building +# only some parts of NUT): +$(top_builddir)/clients/libnutclient.la \ +$(top_builddir)/clients/libnutclientstub.la: dummy + @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +else !HAVE_CPPUNIT +# Just redistribute test source into tarball if not building tests + +EXTRA_DIST += $(CPPUNITTESTSRC) $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) + +cppnit: + @echo "SKIP: $@ not implemented without C++11 and CPPUNIT enabled" >&2 ; exit 1 + +endif !HAVE_CPPUNIT + +else !HAVE_CXX11 +# Just redistribute test source into tarball if not building C++ at all + +EXTRA_DIST += $(CPPUNITTESTSRC) $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) + +cppnit: + @echo "SKIP: $@ not implemented without C++11 and CPPUNIT enabled" >&2 ; exit 1 + +endif !HAVE_CXX11 + +dummy: + +BUILT_SOURCES = $(LINKED_SOURCE_FILES) +CLEANFILES += $(LINKED_SOURCE_FILES) +CLEANFILES += $(TESTS) $(TESTS_CXX11) +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +# NOTE: Do not clean ".deps" in SUBDIRS of the main project, +# the root Makefile.am takes care of that! +#clean-local: +# rm -rf $(builddir)/.deps diff --git a/tests/Makefile.in b/tests/Makefile.in new file mode 100644 index 0000000..25d882b --- /dev/null +++ b/tests/Makefile.in @@ -0,0 +1,1547 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# Network UPS Tools: tests +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +TESTS = nutlogtest$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_3) +check_PROGRAMS = $(am__EXEEXT_4) $(am__EXEEXT_5) +@WITH_USB_TRUE@am__append_1 = getvaluetest + +# Note: per configure script this "SHOULD" also assume +# that we HAVE_CXX11 - but better have it explicit +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@am__append_2 = $(TESTS_CXX11) + +# Note: we only build it, but do not run directly (NIT prepares the sandbox) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@am__append_3 = cppnit + +# Just redistribute test source into tarball if not building tests +@HAVE_CPPUNIT_FALSE@@HAVE_CXX11_TRUE@am__append_4 = $(CPPUNITTESTSRC) $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) + +# Just redistribute test source into tarball if not building C++ at all +@HAVE_CXX11_FALSE@am__append_5 = $(CPPUNITTESTSRC) $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) +subdir = tests +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/nut_arg_with.m4 \ + $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ + $(top_srcdir)/m4/nut_check_libavahi.m4 \ + $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ + $(top_srcdir)/m4/nut_check_libgd.m4 \ + $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ + $(top_srcdir)/m4/nut_check_libneon.m4 \ + $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ + $(top_srcdir)/m4/nut_check_libpowerman.m4 \ + $(top_srcdir)/m4/nut_check_libusb.m4 \ + $(top_srcdir)/m4/nut_check_libwrap.m4 \ + $(top_srcdir)/m4/nut_check_os.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ + $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ + $(top_srcdir)/m4/nut_type_socklen_t.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/include/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +@WITH_USB_TRUE@am__EXEEXT_1 = getvaluetest$(EXEEXT) +am__EXEEXT_2 = cppunittest$(EXEEXT) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@am__EXEEXT_3 = $(am__EXEEXT_2) +am__EXEEXT_4 = nutlogtest$(EXEEXT) $(am__EXEEXT_1) $(am__EXEEXT_3) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@am__EXEEXT_5 = cppnit$(EXEEXT) +am__cppnit_SOURCES_DIST = cpputest-client.cpp cpputest.cpp +am__objects_1 = cppnit-cpputest-client.$(OBJEXT) +am__objects_2 = cppnit-cpputest.$(OBJEXT) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@am_cppnit_OBJECTS = \ +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@ $(am__objects_1) \ +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@ $(am__objects_2) +cppnit_OBJECTS = $(am_cppnit_OBJECTS) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@cppnit_DEPENDENCIES = $(top_builddir)/clients/libnutclient.la \ +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@ $(top_builddir)/clients/libnutclientstub.la +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +cppnit_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(cppnit_CXXFLAGS) \ + $(CXXFLAGS) $(cppnit_LDFLAGS) $(LDFLAGS) -o $@ +am__cppunittest_SOURCES_DIST = example.cpp nutclienttest.cpp \ + cpputest.cpp +am__objects_3 = cppunittest-example.$(OBJEXT) \ + cppunittest-nutclienttest.$(OBJEXT) +am__objects_4 = cppunittest-cpputest.$(OBJEXT) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@am_cppunittest_OBJECTS = \ +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@ $(am__objects_3) \ +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@ $(am__objects_4) +cppunittest_OBJECTS = $(am_cppunittest_OBJECTS) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@cppunittest_DEPENDENCIES = $(top_builddir)/clients/libnutclient.la \ +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@ $(top_builddir)/clients/libnutclientstub.la +cppunittest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(cppunittest_CXXFLAGS) \ + $(CXXFLAGS) $(cppunittest_LDFLAGS) $(LDFLAGS) -o $@ +am__getvaluetest_SOURCES_DIST = getvaluetest.c +@WITH_USB_TRUE@am_getvaluetest_OBJECTS = \ +@WITH_USB_TRUE@ getvaluetest-getvaluetest.$(OBJEXT) +@WITH_USB_TRUE@nodist_getvaluetest_OBJECTS = \ +@WITH_USB_TRUE@ getvaluetest-hidparser.$(OBJEXT) +getvaluetest_OBJECTS = $(am_getvaluetest_OBJECTS) \ + $(nodist_getvaluetest_OBJECTS) +@WITH_USB_TRUE@getvaluetest_DEPENDENCIES = \ +@WITH_USB_TRUE@ $(top_builddir)/common/libcommon.la +getvaluetest_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(getvaluetest_CFLAGS) \ + $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am_nutlogtest_OBJECTS = nutlogtest.$(OBJEXT) +nutlogtest_OBJECTS = $(am_nutlogtest_OBJECTS) +nutlogtest_DEPENDENCIES = $(top_builddir)/common/libcommon.la +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/cppnit-cpputest-client.Po \ + ./$(DEPDIR)/cppnit-cpputest.Po \ + ./$(DEPDIR)/cppunittest-cpputest.Po \ + ./$(DEPDIR)/cppunittest-example.Po \ + ./$(DEPDIR)/cppunittest-nutclienttest.Po \ + ./$(DEPDIR)/getvaluetest-getvaluetest.Po \ + ./$(DEPDIR)/getvaluetest-hidparser.Po \ + ./$(DEPDIR)/nutlogtest.Po +am__mv = mv -f +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \ + $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(cppnit_SOURCES) $(cppunittest_SOURCES) \ + $(getvaluetest_SOURCES) $(nodist_getvaluetest_SOURCES) \ + $(nutlogtest_SOURCES) +DIST_SOURCES = $(am__cppnit_SOURCES_DIST) \ + $(am__cppunittest_SOURCES_DIST) \ + $(am__getvaluetest_SOURCES_DIST) $(nutlogtest_SOURCES) +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ + distclean-recursive maintainer-clean-recursive +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + check recheck distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' +RECHECK_LOGS = $(TEST_LOGS) +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp \ + $(top_srcdir)/test-driver +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +am__relativize = \ + dir0=`pwd`; \ + sed_first='s,^\([^/]*\)/.*$$,\1,'; \ + sed_rest='s,^[^/]*/*,,'; \ + sed_last='s,^.*/\([^/]*\)$$,\1,'; \ + sed_butlast='s,/*[^/]*$$,,'; \ + while test -n "$$dir1"; do \ + first=`echo "$$dir1" | sed -e "$$sed_first"`; \ + if test "$$first" != "."; then \ + if test "$$first" = ".."; then \ + dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \ + dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \ + else \ + first2=`echo "$$dir2" | sed -e "$$sed_first"`; \ + if test "$$first2" = "$$first"; then \ + dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \ + else \ + dir2="../$$dir2"; \ + fi; \ + dir0="$$dir0"/"$$first"; \ + fi; \ + fi; \ + dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \ + done; \ + reldir="$$dir2" +A2X = @A2X@ +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BINDIR = @BINDIR@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONFPATH = @CONFPATH@ +CPP = @CPP@ +CPPCHECK = @CPPCHECK@ +CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DBLATEX = @DBLATEX@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ +DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ +DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ +DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ +LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ +LIBGD_CFLAGS = @LIBGD_CFLAGS@ +LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ +LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ +LIBIPMI_LIBS = @LIBIPMI_LIBS@ +LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ +LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ +LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ +LIBNEON_LIBS = @LIBNEON_LIBS@ +LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ +LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ +LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ +LIBS = @LIBS@ +LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ +LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ +LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ +LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ +LIBUSB_LIBS = @LIBUSB_LIBS@ +LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ +LIBWRAP_LIBS = @LIBWRAP_LIBS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LN_S_R = @LN_S_R@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OS_NAME = @OS_NAME@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ +RANLIB = @RANLIB@ +RUN_AS_GROUP = @RUN_AS_GROUP@ +RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ +SED = @SED@ +SERLIBS = @SERLIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ +STATEPATH = @STATEPATH@ +STRIP = @STRIP@ +SUN_LIBUSB = @SUN_LIBUSB@ +TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +auglensdir = @auglensdir@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +cgiexecdir = @cgiexecdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +devddir = @devddir@ +docdir = @docdir@ +driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +hotplugdir = @hotplugdir@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +now = @now@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +systemdshutdowndir = @systemdshutdowndir@ +systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +udevdir = @udevdir@ +SUBDIRS = . NIT +EXTRA_DIST = nut-driver-enumerator-test.sh \ + nut-driver-enumerator-test--ups.conf $(am__append_4) \ + $(am__append_5) +CLEANFILES = *.trs *.log $(LINKED_SOURCE_FILES) $(TESTS) \ + $(TESTS_CXX11) +AM_CFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/drivers +AM_CXXFLAGS = -I$(top_srcdir)/include +nutlogtest_SOURCES = nutlogtest.c +nutlogtest_LDADD = $(top_builddir)/common/libcommon.la + +# Separate the .deps of other dirs from this one +LINKED_SOURCE_FILES = hidparser.c +@WITH_USB_TRUE@getvaluetest_SOURCES = getvaluetest.c +@WITH_USB_TRUE@nodist_getvaluetest_SOURCES = hidparser.c +# Pull the right include path for chosen libusb version: +@WITH_USB_TRUE@getvaluetest_CFLAGS = $(AM_CFLAGS) $(LIBUSB_CFLAGS) +@WITH_USB_TRUE@getvaluetest_LDADD = $(top_builddir)/common/libcommon.la + +### Optional tests which can not be built everywhere +# List of src files for CppUnit tests +CPPUNITTESTSRC = example.cpp nutclienttest.cpp +# The test driver which orchestrates running those tests above +CPPUNITTESTERSRC = cpputest.cpp +CPPCLIENTTESTSRC = cpputest-client.cpp +TESTS_CXX11 = cppunittest +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@cppunittest_CXXFLAGS = $(AM_CXXFLAGS) $(CPPUNIT_CFLAGS) $(CPPUNIT_CXXFLAGS) $(CPPUNIT_NUT_CXXFLAGS) $(CXXFLAGS) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@cppunittest_LDFLAGS = $(CPPUNIT_LDFLAGS) $(CPPUNIT_LIBS) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@cppunittest_LDADD = $(top_builddir)/clients/libnutclient.la $(top_builddir)/clients/libnutclientstub.la +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@cppunittest_SOURCES = $(CPPUNITTESTSRC) $(CPPUNITTESTERSRC) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@cppnit_CXXFLAGS = $(AM_CXXFLAGS) $(CPPUNIT_CFLAGS) $(CPPUNIT_CXXFLAGS) $(CPPUNIT_NUT_CXXFLAGS) $(CXXFLAGS) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@cppnit_LDFLAGS = $(CPPUNIT_LDFLAGS) $(CPPUNIT_LIBS) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@cppnit_LDADD = $(top_builddir)/clients/libnutclient.la $(top_builddir)/clients/libnutclientstub.la +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@cppnit_SOURCES = $(CPPCLIENTTESTSRC) $(CPPUNITTESTERSRC) +BUILT_SOURCES = $(LINKED_SOURCE_FILES) +MAINTAINERCLEANFILES = Makefile.in .dirstamp +all: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) all-recursive + +.SUFFIXES: +.SUFFIXES: .c .cpp .lo .log .o .obj .test .test$(EXEEXT) .trs +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu tests/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +clean-checkPROGRAMS: + @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \ + echo " rm -f" $$list; \ + rm -f $$list || exit $$?; \ + test -n "$(EXEEXT)" || exit 0; \ + list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ + echo " rm -f" $$list; \ + rm -f $$list + +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@cppnit$(EXEEXT): $(cppnit_OBJECTS) $(cppnit_DEPENDENCIES) $(EXTRA_cppnit_DEPENDENCIES) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@ @rm -f cppnit$(EXEEXT) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@ $(AM_V_CXXLD)$(cppnit_LINK) $(cppnit_OBJECTS) $(cppnit_LDADD) $(LIBS) + +cppunittest$(EXEEXT): $(cppunittest_OBJECTS) $(cppunittest_DEPENDENCIES) $(EXTRA_cppunittest_DEPENDENCIES) + @rm -f cppunittest$(EXEEXT) + $(AM_V_CXXLD)$(cppunittest_LINK) $(cppunittest_OBJECTS) $(cppunittest_LDADD) $(LIBS) + +getvaluetest$(EXEEXT): $(getvaluetest_OBJECTS) $(getvaluetest_DEPENDENCIES) $(EXTRA_getvaluetest_DEPENDENCIES) + @rm -f getvaluetest$(EXEEXT) + $(AM_V_CCLD)$(getvaluetest_LINK) $(getvaluetest_OBJECTS) $(getvaluetest_LDADD) $(LIBS) + +nutlogtest$(EXEEXT): $(nutlogtest_OBJECTS) $(nutlogtest_DEPENDENCIES) $(EXTRA_nutlogtest_DEPENDENCIES) + @rm -f nutlogtest$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(nutlogtest_OBJECTS) $(nutlogtest_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cppnit-cpputest-client.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cppnit-cpputest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cppunittest-cpputest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cppunittest-example.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cppunittest-nutclienttest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getvaluetest-getvaluetest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getvaluetest-hidparser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nutlogtest.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.c.lo: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< + +getvaluetest-getvaluetest.o: getvaluetest.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getvaluetest_CFLAGS) $(CFLAGS) -MT getvaluetest-getvaluetest.o -MD -MP -MF $(DEPDIR)/getvaluetest-getvaluetest.Tpo -c -o getvaluetest-getvaluetest.o `test -f 'getvaluetest.c' || echo '$(srcdir)/'`getvaluetest.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getvaluetest-getvaluetest.Tpo $(DEPDIR)/getvaluetest-getvaluetest.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getvaluetest.c' object='getvaluetest-getvaluetest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getvaluetest_CFLAGS) $(CFLAGS) -c -o getvaluetest-getvaluetest.o `test -f 'getvaluetest.c' || echo '$(srcdir)/'`getvaluetest.c + +getvaluetest-getvaluetest.obj: getvaluetest.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getvaluetest_CFLAGS) $(CFLAGS) -MT getvaluetest-getvaluetest.obj -MD -MP -MF $(DEPDIR)/getvaluetest-getvaluetest.Tpo -c -o getvaluetest-getvaluetest.obj `if test -f 'getvaluetest.c'; then $(CYGPATH_W) 'getvaluetest.c'; else $(CYGPATH_W) '$(srcdir)/getvaluetest.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getvaluetest-getvaluetest.Tpo $(DEPDIR)/getvaluetest-getvaluetest.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='getvaluetest.c' object='getvaluetest-getvaluetest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getvaluetest_CFLAGS) $(CFLAGS) -c -o getvaluetest-getvaluetest.obj `if test -f 'getvaluetest.c'; then $(CYGPATH_W) 'getvaluetest.c'; else $(CYGPATH_W) '$(srcdir)/getvaluetest.c'; fi` + +getvaluetest-hidparser.o: hidparser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getvaluetest_CFLAGS) $(CFLAGS) -MT getvaluetest-hidparser.o -MD -MP -MF $(DEPDIR)/getvaluetest-hidparser.Tpo -c -o getvaluetest-hidparser.o `test -f 'hidparser.c' || echo '$(srcdir)/'`hidparser.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getvaluetest-hidparser.Tpo $(DEPDIR)/getvaluetest-hidparser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hidparser.c' object='getvaluetest-hidparser.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getvaluetest_CFLAGS) $(CFLAGS) -c -o getvaluetest-hidparser.o `test -f 'hidparser.c' || echo '$(srcdir)/'`hidparser.c + +getvaluetest-hidparser.obj: hidparser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getvaluetest_CFLAGS) $(CFLAGS) -MT getvaluetest-hidparser.obj -MD -MP -MF $(DEPDIR)/getvaluetest-hidparser.Tpo -c -o getvaluetest-hidparser.obj `if test -f 'hidparser.c'; then $(CYGPATH_W) 'hidparser.c'; else $(CYGPATH_W) '$(srcdir)/hidparser.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/getvaluetest-hidparser.Tpo $(DEPDIR)/getvaluetest-hidparser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hidparser.c' object='getvaluetest-hidparser.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(getvaluetest_CFLAGS) $(CFLAGS) -c -o getvaluetest-hidparser.obj `if test -f 'hidparser.c'; then $(CYGPATH_W) 'hidparser.c'; else $(CYGPATH_W) '$(srcdir)/hidparser.c'; fi` + +.cpp.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cpp.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cpp.lo: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCXX_TRUE@ $(LTCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< + +cppnit-cpputest-client.o: cpputest-client.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppnit_CXXFLAGS) $(CXXFLAGS) -MT cppnit-cpputest-client.o -MD -MP -MF $(DEPDIR)/cppnit-cpputest-client.Tpo -c -o cppnit-cpputest-client.o `test -f 'cpputest-client.cpp' || echo '$(srcdir)/'`cpputest-client.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppnit-cpputest-client.Tpo $(DEPDIR)/cppnit-cpputest-client.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cpputest-client.cpp' object='cppnit-cpputest-client.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppnit_CXXFLAGS) $(CXXFLAGS) -c -o cppnit-cpputest-client.o `test -f 'cpputest-client.cpp' || echo '$(srcdir)/'`cpputest-client.cpp + +cppnit-cpputest-client.obj: cpputest-client.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppnit_CXXFLAGS) $(CXXFLAGS) -MT cppnit-cpputest-client.obj -MD -MP -MF $(DEPDIR)/cppnit-cpputest-client.Tpo -c -o cppnit-cpputest-client.obj `if test -f 'cpputest-client.cpp'; then $(CYGPATH_W) 'cpputest-client.cpp'; else $(CYGPATH_W) '$(srcdir)/cpputest-client.cpp'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppnit-cpputest-client.Tpo $(DEPDIR)/cppnit-cpputest-client.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cpputest-client.cpp' object='cppnit-cpputest-client.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppnit_CXXFLAGS) $(CXXFLAGS) -c -o cppnit-cpputest-client.obj `if test -f 'cpputest-client.cpp'; then $(CYGPATH_W) 'cpputest-client.cpp'; else $(CYGPATH_W) '$(srcdir)/cpputest-client.cpp'; fi` + +cppnit-cpputest.o: cpputest.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppnit_CXXFLAGS) $(CXXFLAGS) -MT cppnit-cpputest.o -MD -MP -MF $(DEPDIR)/cppnit-cpputest.Tpo -c -o cppnit-cpputest.o `test -f 'cpputest.cpp' || echo '$(srcdir)/'`cpputest.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppnit-cpputest.Tpo $(DEPDIR)/cppnit-cpputest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cpputest.cpp' object='cppnit-cpputest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppnit_CXXFLAGS) $(CXXFLAGS) -c -o cppnit-cpputest.o `test -f 'cpputest.cpp' || echo '$(srcdir)/'`cpputest.cpp + +cppnit-cpputest.obj: cpputest.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppnit_CXXFLAGS) $(CXXFLAGS) -MT cppnit-cpputest.obj -MD -MP -MF $(DEPDIR)/cppnit-cpputest.Tpo -c -o cppnit-cpputest.obj `if test -f 'cpputest.cpp'; then $(CYGPATH_W) 'cpputest.cpp'; else $(CYGPATH_W) '$(srcdir)/cpputest.cpp'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppnit-cpputest.Tpo $(DEPDIR)/cppnit-cpputest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cpputest.cpp' object='cppnit-cpputest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppnit_CXXFLAGS) $(CXXFLAGS) -c -o cppnit-cpputest.obj `if test -f 'cpputest.cpp'; then $(CYGPATH_W) 'cpputest.cpp'; else $(CYGPATH_W) '$(srcdir)/cpputest.cpp'; fi` + +cppunittest-example.o: example.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -MT cppunittest-example.o -MD -MP -MF $(DEPDIR)/cppunittest-example.Tpo -c -o cppunittest-example.o `test -f 'example.cpp' || echo '$(srcdir)/'`example.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppunittest-example.Tpo $(DEPDIR)/cppunittest-example.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='example.cpp' object='cppunittest-example.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -c -o cppunittest-example.o `test -f 'example.cpp' || echo '$(srcdir)/'`example.cpp + +cppunittest-example.obj: example.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -MT cppunittest-example.obj -MD -MP -MF $(DEPDIR)/cppunittest-example.Tpo -c -o cppunittest-example.obj `if test -f 'example.cpp'; then $(CYGPATH_W) 'example.cpp'; else $(CYGPATH_W) '$(srcdir)/example.cpp'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppunittest-example.Tpo $(DEPDIR)/cppunittest-example.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='example.cpp' object='cppunittest-example.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -c -o cppunittest-example.obj `if test -f 'example.cpp'; then $(CYGPATH_W) 'example.cpp'; else $(CYGPATH_W) '$(srcdir)/example.cpp'; fi` + +cppunittest-nutclienttest.o: nutclienttest.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -MT cppunittest-nutclienttest.o -MD -MP -MF $(DEPDIR)/cppunittest-nutclienttest.Tpo -c -o cppunittest-nutclienttest.o `test -f 'nutclienttest.cpp' || echo '$(srcdir)/'`nutclienttest.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppunittest-nutclienttest.Tpo $(DEPDIR)/cppunittest-nutclienttest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nutclienttest.cpp' object='cppunittest-nutclienttest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -c -o cppunittest-nutclienttest.o `test -f 'nutclienttest.cpp' || echo '$(srcdir)/'`nutclienttest.cpp + +cppunittest-nutclienttest.obj: nutclienttest.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -MT cppunittest-nutclienttest.obj -MD -MP -MF $(DEPDIR)/cppunittest-nutclienttest.Tpo -c -o cppunittest-nutclienttest.obj `if test -f 'nutclienttest.cpp'; then $(CYGPATH_W) 'nutclienttest.cpp'; else $(CYGPATH_W) '$(srcdir)/nutclienttest.cpp'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppunittest-nutclienttest.Tpo $(DEPDIR)/cppunittest-nutclienttest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='nutclienttest.cpp' object='cppunittest-nutclienttest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -c -o cppunittest-nutclienttest.obj `if test -f 'nutclienttest.cpp'; then $(CYGPATH_W) 'nutclienttest.cpp'; else $(CYGPATH_W) '$(srcdir)/nutclienttest.cpp'; fi` + +cppunittest-cpputest.o: cpputest.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -MT cppunittest-cpputest.o -MD -MP -MF $(DEPDIR)/cppunittest-cpputest.Tpo -c -o cppunittest-cpputest.o `test -f 'cpputest.cpp' || echo '$(srcdir)/'`cpputest.cpp +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppunittest-cpputest.Tpo $(DEPDIR)/cppunittest-cpputest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cpputest.cpp' object='cppunittest-cpputest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -c -o cppunittest-cpputest.o `test -f 'cpputest.cpp' || echo '$(srcdir)/'`cpputest.cpp + +cppunittest-cpputest.obj: cpputest.cpp +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -MT cppunittest-cpputest.obj -MD -MP -MF $(DEPDIR)/cppunittest-cpputest.Tpo -c -o cppunittest-cpputest.obj `if test -f 'cpputest.cpp'; then $(CYGPATH_W) 'cpputest.cpp'; else $(CYGPATH_W) '$(srcdir)/cpputest.cpp'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/cppunittest-cpputest.Tpo $(DEPDIR)/cppunittest-cpputest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='cpputest.cpp' object='cppunittest-cpputest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(cppunittest_CXXFLAGS) $(CXXFLAGS) -c -o cppunittest-cpputest.obj `if test -f 'cpputest.cpp'; then $(CYGPATH_W) 'cpputest.cpp'; else $(CYGPATH_W) '$(srcdir)/cpputest.cpp'; fi` + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +# This directory's subdirectories are mostly independent; you can cd +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || eval $$failcom; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ + include_option=--etags-include; \ + empty_fix=.; \ + else \ + include_option=--include; \ + empty_fix=; \ + fi; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test ! -f $$subdir/TAGS || \ + set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ + fi; \ + done; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: $(check_PROGRAMS) + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all $(check_PROGRAMS) + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +nutlogtest.log: nutlogtest$(EXEEXT) + @p='nutlogtest$(EXEEXT)'; \ + b='nutlogtest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +getvaluetest.log: getvaluetest$(EXEEXT) + @p='getvaluetest$(EXEEXT)'; \ + b='getvaluetest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +cppunittest.log: cppunittest$(EXEEXT) + @p='cppunittest$(EXEEXT)'; \ + b='cppunittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ + dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ + $(am__relativize); \ + new_distdir=$$reldir; \ + dir1=$$subdir; dir2="$(top_distdir)"; \ + $(am__relativize); \ + new_top_distdir=$$reldir; \ + echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \ + echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \ + ($(am__cd) $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$$new_top_distdir" \ + distdir="$$new_distdir" \ + am__remove_distdir=: \ + am__skip_length_check=: \ + am__skip_mode_fix=: \ + distdir) \ + || exit 1; \ + fi; \ + done +@HAVE_CPPUNIT_FALSE@check-local: +@HAVE_CXX11_FALSE@check-local: +@WITH_VALGRIND_FALSE@check-local: +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS check-local +check: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: +install: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-recursive +install-exec: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-recursive + +clean-am: clean-checkPROGRAMS clean-generic clean-libtool \ + mostlyclean-am + +distclean: distclean-recursive + -rm -f ./$(DEPDIR)/cppnit-cpputest-client.Po + -rm -f ./$(DEPDIR)/cppnit-cpputest.Po + -rm -f ./$(DEPDIR)/cppunittest-cpputest.Po + -rm -f ./$(DEPDIR)/cppunittest-example.Po + -rm -f ./$(DEPDIR)/cppunittest-nutclienttest.Po + -rm -f ./$(DEPDIR)/getvaluetest-getvaluetest.Po + -rm -f ./$(DEPDIR)/getvaluetest-hidparser.Po + -rm -f ./$(DEPDIR)/nutlogtest.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +html: html-recursive + +html-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-dvi: install-dvi-recursive + +install-dvi-am: + +install-exec-am: + +install-html: install-html-recursive + +install-html-am: + +install-info: install-info-recursive + +install-info-am: + +install-man: + +install-pdf: install-pdf-recursive + +install-pdf-am: + +install-ps: install-ps-recursive + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + -rm -f ./$(DEPDIR)/cppnit-cpputest-client.Po + -rm -f ./$(DEPDIR)/cppnit-cpputest.Po + -rm -f ./$(DEPDIR)/cppunittest-cpputest.Po + -rm -f ./$(DEPDIR)/cppunittest-example.Po + -rm -f ./$(DEPDIR)/cppunittest-nutclienttest.Po + -rm -f ./$(DEPDIR)/getvaluetest-getvaluetest.Po + -rm -f ./$(DEPDIR)/getvaluetest-hidparser.Po + -rm -f ./$(DEPDIR)/nutlogtest.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +pdf: pdf-recursive + +pdf-am: + +ps: ps-recursive + +ps-am: + +uninstall-am: + +.MAKE: $(am__recursive_targets) all check check-am install install-am \ + install-exec install-strip + +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am \ + am--depfiles check check-TESTS check-am check-local clean \ + clean-checkPROGRAMS clean-generic clean-libtool cscopelist-am \ + ctags ctags-am distclean distclean-compile distclean-generic \ + distclean-libtool distclean-tags distdir dvi dvi-am html \ + html-am info info-am install install-am install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am install-info \ + install-info-am install-man install-pdf install-pdf-am \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs installdirs-am maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + recheck tags tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +all: $(TESTS) + +# NUT Integration Testing suite +check-NIT check-NIT-devel: + cd "$(builddir)/NIT" && $(MAKE) $@ + +# NOTE: Not using "$<" due to a legacy Sun/illumos dmake bug with resolver +# of dynamic vars, see e.g. https://man.omnios.org/man1/make#BUGS +hidparser.c: $(top_srcdir)/drivers/hidparser.c + test -s "$@" || ln -s -f "$(top_srcdir)/drivers/hidparser.c" "$@" + +# Make sure out-of-dir dependencies exist (especially when dev-building parts): +$(top_builddir)/common/libcommon.la: dummy + @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@@WITH_VALGRIND_TRUE@check-local: $(check_PROGRAMS) +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@@WITH_VALGRIND_TRUE@ RES=0; for P in $^ ; do $(VALGRIND) ./$$P || { RES=$$? ; echo "FAILED: $(VALGRIND) ./$$P" >&2; }; done; exit $$RES + +# Make sure out-of-dir C++ dependencies exist (especially when dev-building +# only some parts of NUT): +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@$(top_builddir)/clients/libnutclient.la \ +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@$(top_builddir)/clients/libnutclientstub.la: dummy +@HAVE_CPPUNIT_TRUE@@HAVE_CXX11_TRUE@ @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +@HAVE_CPPUNIT_FALSE@@HAVE_CXX11_TRUE@cppnit: +@HAVE_CPPUNIT_FALSE@@HAVE_CXX11_TRUE@ @echo "SKIP: $@ not implemented without C++11 and CPPUNIT enabled" >&2 ; exit 1 + +@HAVE_CXX11_FALSE@cppnit: +@HAVE_CXX11_FALSE@ @echo "SKIP: $@ not implemented without C++11 and CPPUNIT enabled" >&2 ; exit 1 + +dummy: + +# NOTE: Do not clean ".deps" in SUBDIRS of the main project, +# the root Makefile.am takes care of that! +#clean-local: +# rm -rf $(builddir)/.deps + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/tests/NIT/Makefile.am b/tests/NIT/Makefile.am new file mode 100644 index 0000000..6ce64a9 --- /dev/null +++ b/tests/NIT/Makefile.am @@ -0,0 +1,26 @@ +EXTRA_DIST = nit.sh README + +if WITH_CHECK_NIT +check: check-NIT +else +check: + @echo "NO-OP: $@ in `pwd` is inactive by default. Run 'configure --enable-check-NIT' or 'make check-NIT' explicitly" >&2 +endif + +# Run in builddir, use script from srcdir +# Avoid running "$<" - not all make implementations handle that +check-NIT: $(abs_srcdir)/nit.sh + "$(abs_srcdir)/nit.sh" + +# Make sure pre-requisites for NIT are fresh as we iterate +check-NIT-devel: $(abs_srcdir)/nit.sh + @cd .. && ( $(MAKE) -s cppnit || echo "OPTIONAL C++ test client test will be skipped" ) + @cd "$(top_builddir)/clients" && $(MAKE) -s upsc upsrw upsmon + @cd "$(top_builddir)/server" && $(MAKE) -s upsd + @cd "$(top_builddir)/drivers" && $(MAKE) -s dummy-ups + @$(MAKE) check-NIT + +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +clean-local: + rm -rf tmp diff --git a/tests/NIT/Makefile.in b/tests/NIT/Makefile.in new file mode 100644 index 0000000..1cc9271 --- /dev/null +++ b/tests/NIT/Makefile.in @@ -0,0 +1,585 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ +target_triplet = @target@ +subdir = tests/NIT +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ + $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ + $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ + $(top_srcdir)/m4/lt~obsolete.m4 \ + $(top_srcdir)/m4/nut_arg_with.m4 \ + $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ + $(top_srcdir)/m4/nut_check_libavahi.m4 \ + $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ + $(top_srcdir)/m4/nut_check_libgd.m4 \ + $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ + $(top_srcdir)/m4/nut_check_libneon.m4 \ + $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ + $(top_srcdir)/m4/nut_check_libpowerman.m4 \ + $(top_srcdir)/m4/nut_check_libusb.m4 \ + $(top_srcdir)/m4/nut_check_libwrap.m4 \ + $(top_srcdir)/m4/nut_check_os.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ + $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ + $(top_srcdir)/m4/nut_type_socklen_t.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/include/config.h +CONFIG_CLEAN_FILES = +CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +SOURCES = +DIST_SOURCES = +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +am__DIST_COMMON = $(srcdir)/Makefile.in README +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +A2X = @A2X@ +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +BINDIR = @BINDIR@ +CC = @CC@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CONFPATH = @CONFPATH@ +CPP = @CPP@ +CPPCHECK = @CPPCHECK@ +CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DBLATEX = @DBLATEX@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ +DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ +DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ +DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ +GREP = @GREP@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LDFLAGS = @LDFLAGS@ +LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ +LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ +LIBGD_CFLAGS = @LIBGD_CFLAGS@ +LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ +LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ +LIBIPMI_LIBS = @LIBIPMI_LIBS@ +LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ +LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ +LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ +LIBNEON_LIBS = @LIBNEON_LIBS@ +LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ +LIBNETSNMP_LIBS = @LIBNETSNMP_LIBS@ +LIBOBJS = @LIBOBJS@ +LIBPOWERMAN_CFLAGS = @LIBPOWERMAN_CFLAGS@ +LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ +LIBS = @LIBS@ +LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ +LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ +LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ +LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ +LIBUSB_LIBS = @LIBUSB_LIBS@ +LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ +LIBWRAP_LIBS = @LIBWRAP_LIBS@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LN_S_R = @LN_S_R@ +LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MANIFEST_TOOL = @MANIFEST_TOOL@ +MKDIR_P = @MKDIR_P@ +NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ +NM = @NM@ +NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ +OBJDUMP = @OBJDUMP@ +OBJEXT = @OBJEXT@ +OS_NAME = @OS_NAME@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ +PKG_CONFIG = @PKG_CONFIG@ +PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ +PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ +PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ +RANLIB = @RANLIB@ +RUN_AS_GROUP = @RUN_AS_GROUP@ +RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ +SED = @SED@ +SERLIBS = @SERLIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ +STATEPATH = @STATEPATH@ +STRIP = @STRIP@ +SUN_LIBUSB = @SUN_LIBUSB@ +TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ +VERSION = @VERSION@ +WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +auglensdir = @auglensdir@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +cgiexecdir = @cgiexecdir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +devddir = @devddir@ +docdir = @docdir@ +driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +hotplugdir = @hotplugdir@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +now = @now@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +pkgconfigdir = @pkgconfigdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +systemdshutdowndir = @systemdshutdowndir@ +systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ +target = @target@ +target_alias = @target_alias@ +target_cpu = @target_cpu@ +target_os = @target_os@ +target_vendor = @target_vendor@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ +udevdir = @udevdir@ +EXTRA_DIST = nit.sh README +MAINTAINERCLEANFILES = Makefile.in .dirstamp +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ + && { if test -f $@; then exit 0; else break; fi; }; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tests/NIT/Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --gnu tests/NIT/Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +$(am__aclocal_m4_deps): + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs +tags TAGS: + +ctags CTAGS: + +cscope cscopelist: + + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile +installdirs: +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) +clean: clean-am + +clean-am: clean-generic clean-libtool clean-local mostlyclean-am + +distclean: distclean-am + -rm -f Makefile +distclean-am: clean-am distclean-generic + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: + +.MAKE: install-am install-strip + +.PHONY: all all-am check check-am clean clean-generic clean-libtool \ + clean-local cscopelist-am ctags-am distclean distclean-generic \ + distclean-libtool distdir dvi dvi-am html html-am info info-am \ + install install-am install-data install-data-am install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-info install-info-am install-man \ + install-pdf install-pdf-am install-ps install-ps-am \ + install-strip installcheck installcheck-am installdirs \ + maintainer-clean maintainer-clean-generic mostlyclean \ + mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ + tags-am uninstall uninstall-am + +.PRECIOUS: Makefile + + +@WITH_CHECK_NIT_TRUE@check: check-NIT +@WITH_CHECK_NIT_FALSE@check: +@WITH_CHECK_NIT_FALSE@ @echo "NO-OP: $@ in `pwd` is inactive by default. Run 'configure --enable-check-NIT' or 'make check-NIT' explicitly" >&2 + +# Run in builddir, use script from srcdir +# Avoid running "$<" - not all make implementations handle that +check-NIT: $(abs_srcdir)/nit.sh + "$(abs_srcdir)/nit.sh" + +# Make sure pre-requisites for NIT are fresh as we iterate +check-NIT-devel: $(abs_srcdir)/nit.sh + @cd .. && ( $(MAKE) -s cppnit || echo "OPTIONAL C++ test client test will be skipped" ) + @cd "$(top_builddir)/clients" && $(MAKE) -s upsc upsrw upsmon + @cd "$(top_builddir)/server" && $(MAKE) -s upsd + @cd "$(top_builddir)/drivers" && $(MAKE) -s dummy-ups + @$(MAKE) check-NIT + +clean-local: + rm -rf tmp + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/tests/NIT/README b/tests/NIT/README new file mode 100644 index 0000000..30da730 --- /dev/null +++ b/tests/NIT/README @@ -0,0 +1,17 @@ +NUT Integration Testing suite +============================= + +This suite aims to simplify running `upsd`, a `dummy-ups` driver and +a few clients to query them, as part of regular `make check` routine +or separately with existing binaries (should not impact any existing +installation data, processes or communications). + +WARNING: Current working directory when starting the script should be +the location where it may create temporary data (e.g. the BUILDDIR). + +See also +link:https://git.launchpad.net/ubuntu/+source/nut/tree/debian/tests/test-nut.py[The NUT testing script] +available in the +link:https://code.edge.launchpad.net/qa-regression-testing[Ubuntu QA Regression Testing suite] +doing a similar job with NUT installed from packages and configuring +it via files in standard path names. diff --git a/tests/NIT/nit.sh b/tests/NIT/nit.sh new file mode 100755 index 0000000..f404cb9 --- /dev/null +++ b/tests/NIT/nit.sh @@ -0,0 +1,853 @@ +#!/bin/sh + +# NUT Integration Testing suite, assumes the codebase was built and +# arranges running of the binaries to test driver-client-server +# ability to start and their interactions. +# +# Note: currently it is a PoC-quality mess that gets the job done +# but could be refactored for better maintainability and generic +# approach. Part of the goal was to let this script set up the +# sandbox to run tests which could be defined in other files. +# +# WARNING: Current working directory when starting the script should be +# the location where it may create temporary data (e.g. the BUILDDIR). +# Caller can export envvars to impact the script behavior, e.g.: +# DEBUG=true to print debug messages, running processes, etc. +# DEBUG_SLEEP=60 to sleep after tests, with driver+server running +# NUT_DEBUG_MIN=3 to set (minimum) debug level for drivers, upsd... +# NUT_PORT=12345 custom port for upsd to listen and clients to query +# +# Design note: written with dumbed-down POSIX shell syntax, to +# properly work in whatever different OSes have (bash, dash, +# ksh, busybox sh...) +# +# Copyright +# 2022 Jim Klimov +# +# License: GPLv2+ + +TZ=UTC +LANG=C +LC_ALL=C +export TZ LANG LC_ALL + +log_separator() { + echo "" >&2 + echo "================================" >&2 +} + +shouldDebug() { + [ -n "$DEBUG" ] || [ -n "$DEBUG_SLEEP" ] +} + +log_debug() { + if shouldDebug ; then + echo "[DEBUG] $@" >&2 + fi +} + +log_info() { + echo "[INFO] $@" >&2 +} + +log_error() { + echo "[ERROR] $@" >&2 +} + +die() { + echo "[FATAL] $@" >&2 + exit 1 +} + +# Note: current directory is assumed to be writeable for temporary +# data, e.g. the $(builddir) from the Makefile. Static resources +# from the source codebase are where the script resides, e.g. +# the $(srcdir) from the Makefile. If we are not in the source +# tree, tests would use binaries in PATH (e.g. packaged install). +BUILDDIR="`pwd`" +TOP_BUILDDIR="" +case "${BUILDDIR}" in + */tests/NIT) + TOP_BUILDDIR="`cd "${BUILDDIR}"/../.. && pwd`" ;; + *) log_info "Current directory '${BUILDDIR}' is not a .../tests/NIT" ;; +esac +if ! test -w "${BUILDDIR}" ; then + log_error "BUILDDIR='${BUILDDIR}' is not writeable, tests may fail below" +fi + +SRCDIR="`dirname "$0"`" +SRCDIR="`cd "$SRCDIR" && pwd`" +TOP_SRCDIR="" +case "${SRCDIR}" in + */tests/NIT) + TOP_SRCDIR="`cd "${SRCDIR}"/../.. && pwd`" ;; + *) log_info "Script source directory '${SRCDIR}' is not a .../tests/NIT" ;; +esac + +# No fuss about LD_LIBRARY_PATH: for binaries that need it, +# PATH entries below would contain libtool wrapper scripts; +# for other builds we use system default or caller's env. +PATH_ADD="${BUILDDIR}" +if [ x"${SRCDIR}" != x"${BUILDDIR}" ]; then + PATH_ADD="${PATH_ADD}:${SRCDIR}" +fi + +if [ x"${TOP_BUILDDIR}" != x ]; then + PATH_ADD="${PATH_ADD}:${TOP_BUILDDIR}/clients:${TOP_BUILDDIR}/drivers:${TOP_BUILDDIR}/server:${TOP_BUILDDIR}/tools:${TOP_BUILDDIR}/tools/nut-scanner" +fi + +if [ x"${TOP_SRCDIR}" != x ]; then + PATH_ADD="${PATH_ADD}:${TOP_SRCDIR}/clients:${TOP_SRCDIR}/drivers:${TOP_SRCDIR}/server:${TOP_SRCDIR}/tools:${TOP_SRCDIR}/tools/nut-scanner" +fi + +PATH="${PATH_ADD}:${PATH}" +export PATH +unset PATH_ADD + +log_debug "Using PATH='$PATH'" + +for PROG in upsd upsc dummy-ups upsmon ; do + (command -v ${PROG}) || die "Useless setup: ${PROG} not found in PATH: ${PATH}" +done + +PID_UPSD="" +PID_DUMMYUPS="" +PID_DUMMYUPS1="" +PID_DUMMYUPS2="" + +TESTDIR="$BUILDDIR/tmp" +# Technically the limit is sizeof(sockaddr.sun_path) for complete socket +# pathname, which varies 104-108 chars max on systems seen in CI farm; +# we reserve 17 chars for "/dummy-ups-dummy" longest filename. +if [ `echo "$TESTDIR" | wc -c` -gt 80 ]; then + log_info "'$TESTDIR' is too long to store AF_UNIX socket files, will mktemp" + if ! ( [ -n "${TMPDIR-}" ] && [ -d "${TMPDIR-}" ] && [ -w "${TMPDIR-}" ] ) ; then + if [ -d /dev/shm ] && [ -w /dev/shm ]; then TMPDIR=/dev/shm ; else TMPDIR=/tmp ; fi + fi + TESTDIR="`mktemp -d "${TMPDIR}/nit-tmp.$$.XXXXXX"`" || die "Failed to mktemp" +else + rm -rf "${TESTDIR}" || true +fi +log_info "Using '$TESTDIR' for generated configs and state files" + +mkdir -p "${TESTDIR}/etc" "${TESTDIR}/run" && chmod 750 "${TESTDIR}/run" \ +|| die "Failed to create temporary FS structure for the NIT" + +stop_daemons() { + if [ -n "$PID_UPSD$PID_DUMMYUPS$PID_DUMMYUPS1$PID_DUMMYUPS2" ] ; then + log_info "Stopping test daemons" + kill -15 $PID_UPSD $PID_DUMMYUPS $PID_DUMMYUPS1 $PID_DUMMYUPS2 2>/dev/null + fi +} + +trap 'RES=$?; stop_daemons; if [ "${TESTDIR}" != "${BUILDDIR}/tmp" ] ; then rm -rf "${TESTDIR}" ; fi; exit $RES;' 0 1 2 3 15 + +NUT_STATEPATH="${TESTDIR}/run" +NUT_ALTPIDPATH="${TESTDIR}/run" +NUT_CONFPATH="${TESTDIR}/etc" +export NUT_STATEPATH NUT_ALTPIDPATH NUT_CONFPATH + +# TODO: Find a portable way to (check and) grab a random unprivileged port? +[ -n "${NUT_PORT-}" ] && [ "$NUT_PORT" -gt 0 ] && [ "$NUT_PORT" -lt 65536 ] \ +|| { + DELTA1="`date +%S`" || DELTA1=0 + DELTA2="`expr $$ % 99`" || DELTA2=0 + + NUT_PORT="`expr 34931 + $DELTA1 + $DELTA2`" \ + && [ "$NUT_PORT" -gt 0 ] && [ "$NUT_PORT" -lt 65536 ] \ + || NUT_PORT=34931 +} +export NUT_PORT + +### upsd.conf: ################################################## + +generatecfg_upsd_trivial() { + # Populate the configs for the run + cat > "$NUT_CONFPATH/upsd.conf" << EOF +STATEPATH "$NUT_STATEPATH" +LISTEN localhost $NUT_PORT +EOF + [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: upsd.conf" + chmod 640 "$NUT_CONFPATH/upsd.conf" + + # Some systems listining on symbolic "localhost" actually + # only bind to IPv6, and Python telnetlib resolves IPv4 + # and fails its connection tests. Others fare well with + # both addresses in one command. + for LH in 127.0.0.1 '::1' ; do + if ( + ( cat /etc/hosts || getent hosts ) | grep "$LH" \ + || ping -c 1 "$LH" + ) 2>/dev/null >/dev/null ; then + echo "LISTEN $LH $NUT_PORT" >> "$NUT_CONFPATH/upsd.conf" + fi + done + + if [ -n "${NUT_DEBUG_MIN-}" ] ; then + echo "DEBUG_MIN ${NUT_DEBUG_MIN}" >> "$NUT_CONFPATH/upsd.conf" || exit + fi +} + +generatecfg_upsd_nodev() { + generatecfg_upsd_trivial + echo "ALLOW_NO_DEVICE true" >> "$NUT_CONFPATH/upsd.conf" \ + || die "Failed to populate temporary FS structure for the NIT: upsd.conf" +} + +### upsd.users: ################################################## + +TESTPASS_ADMIN='mypass' +TESTPASS_TESTER='pass words' +TESTPASS_UPSMON_PRIMARY='P@ssW0rdAdm' +TESTPASS_UPSMON_SECONDARY='P@ssW0rd' + +generatecfg_upsdusers_trivial() { + cat > "$NUT_CONFPATH/upsd.users" << EOF +[admin] + password = $TESTPASS_ADMIN + actions = SET + instcmds = ALL + +[tester] + password = "${TESTPASS_TESTER}" + instcmds = test.battery.start + instcmds = test.battery.stop + +[dummy-admin-m] + password = "${TESTPASS_UPSMON_PRIMARY}" + upsmon master + +[dummy-admin] + password = "${TESTPASS_UPSMON_PRIMARY}" + upsmon primary + +[dummy-user-s] + password = "${TESTPASS_UPSMON_SECONDARY}" + upsmon slave + +[dummy-user] + password = "${TESTPASS_UPSMON_SECONDARY}" + upsmon secondary +EOF + [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: upsd.users" + chmod 640 "$NUT_CONFPATH/upsd.users" +} + +### upsmon.conf: ################################################## + +generatecfg_upsmon_trivial() { + # Populate the configs for the run + ( echo 'MINSUPPLIES 0' > "$NUT_CONFPATH/upsmon.conf" || exit + echo 'SHUTDOWNCMD "echo TESTING_DUMMY_SHUTDOWN_NOW"' >> "$NUT_CONFPATH/upsmon.conf" || exit + + if [ -n "${NUT_DEBUG_MIN-}" ] ; then + echo "DEBUG_MIN ${NUT_DEBUG_MIN}" >> "$NUT_CONFPATH/upsmon.conf" || exit + fi + ) || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" + chmod 640 "$NUT_CONFPATH/upsmon.conf" +} + +generatecfg_upsmon_master() { + generatecfg_upsmon_trivial + echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-admin-m' '${TESTPASS_UPSMON_PRIMARY}' master" >> "$NUT_CONFPATH/upsmon.conf" \ + || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" +} + +generatecfg_upsmon_primary() { + generatecfg_upsmon_trivial + echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-admin' '${TESTPASS_UPSMON_PRIMARY}' primary" >> "$NUT_CONFPATH/upsmon.conf" \ + || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" +} + +generatecfg_upsmon_slave() { + generatecfg_upsmon_trivial + echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-user-s' '${TESTPASS_UPSMON_SECONDARY}' slave" >> "$NUT_CONFPATH/upsmon.conf" \ + || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" +} + +generatecfg_upsmon_secondary() { + generatecfg_upsmon_trivial + echo "MONITOR 'dummy@localhost:$NUT_PORT' 0 'dummy-user' '${TESTPASS_UPSMON_SECONDARY}' secondary" >> "$NUT_CONFPATH/upsmon.conf" \ + || die "Failed to populate temporary FS structure for the NIT: upsmon.conf" +} + +### ups.conf: ################################################## + +generatecfg_ups_trivial() { + # Populate the configs for the run + ( echo 'maxretry = 3' > "$NUT_CONFPATH/ups.conf" || exit + if [ x"${TOP_BUILDDIR}" != x ]; then + echo "driverpath = '${TOP_BUILDDIR}/drivers'" >> "$NUT_CONFPATH/ups.conf" || exit + fi + if [ -n "${NUT_DEBUG_MIN-}" ] ; then + echo "debug_min = ${NUT_DEBUG_MIN}" >> "$NUT_CONFPATH/ups.conf" || exit + fi + ) || die "Failed to populate temporary FS structure for the NIT: ups.conf" + chmod 640 "$NUT_CONFPATH/ups.conf" + +} + +generatecfg_ups_dummy() { + generatecfg_ups_trivial + + cat > "$NUT_CONFPATH/dummy.seq" << EOF +ups.status: OB +TIMER 5 +ups.status: OL +TIMER 5 +EOF + [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: dummy.seq" + + cat >> "$NUT_CONFPATH/ups.conf" << EOF +[dummy] + driver = dummy-ups + desc = "Crash Dummy" + port = dummy.seq + #mode = dummy-loop +EOF + [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: ups.conf" + + if [ x"${TOP_SRCDIR}" != x ]; then + cp "${TOP_SRCDIR}/data/evolution500.seq" "${TOP_SRCDIR}/data/epdu-managed.dev" "$NUT_CONFPATH/" + + cat >> "$NUT_CONFPATH/ups.conf" << EOF +[UPS1] + driver = dummy-ups + desc = "Example event sequence" + port = evolution500.seq + +[UPS2] + driver = dummy-ups + desc = "Example ePDU data dump" + port = epdu-managed.dev + mode = dummy-once +EOF + [ $? = 0 ] || die "Failed to populate temporary FS structure for the NIT: ups.conf" + + # HACK: Avoid empty ups.status that may be present in example docs + # FIXME: Might we actually want that value (un-)set for tests?.. + # TODO: Check if the problem was with dummy-ups looping? [#1385] + for F in "$NUT_CONFPATH/"*.dev "$NUT_CONFPATH/"*.seq ; do + sed -e 's,^ups.status: *$,ups.status: OL BOOST,' -i'.bak' "$F" + grep -E '^ups.status:' "$F" >/dev/null || { echo "ups.status: OL BOOST" >> "$F"; } + done + fi + +} + +##################################################### + +isPidAlive() { + [ -n "$1" ] && [ "$1" -gt 0 ] || return + [ -d "/proc/$1" ] || kill -0 "$1" 2>/dev/null +} + +FAILED=0 +PASSED=0 + +testcase_upsd_no_configs_at_all() { + log_separator + log_info "Test UPSD without configs at all" + upsd -F + if [ "$?" = 0 ]; then + log_error "upsd should fail without configs" + FAILED="`expr $FAILED + 1`" + else + log_info "OK, upsd failed to start in wrong conditions" + PASSED="`expr $PASSED + 1`" + fi +} + +testcase_upsd_no_configs_driver_file() { + log_separator + log_info "Test UPSD without driver config file" + generatecfg_upsd_trivial + upsd -F + if [ "$?" = 0 ]; then + log_error "upsd should fail without driver config file" + FAILED="`expr $FAILED + 1`" + else + log_info "OK, upsd failed to start in wrong conditions" + PASSED="`expr $PASSED + 1`" + fi +} + +testcase_upsd_no_configs_in_driver_file() { + log_separator + log_info "Test UPSD without drivers defined in config file" + generatecfg_upsd_trivial + generatecfg_ups_trivial + upsd -F + if [ "$?" = 0 ]; then + log_error "upsd should fail without drivers defined in config file" + FAILED="`expr $FAILED + 1`" + else + log_info "OK, upsd failed to start in wrong conditions" + PASSED="`expr $PASSED + 1`" + fi +} + +testcase_upsd_allow_no_device() { + log_separator + log_info "Test UPSD allowed to run without driver configs" + generatecfg_upsd_nodev + generatecfg_upsdusers_trivial + generatecfg_ups_trivial + upsd -F & + PID_UPSD="$!" + sleep 2 + if isPidAlive "$PID_UPSD"; then + log_info "OK, upsd is running" + PASSED="`expr $PASSED + 1`" + + log_separator + log_info "Test that UPSD responds to UPSC" + OUT="`upsc -l localhost:$NUT_PORT`" || die "upsd does not respond: $OUT" + if [ -n "$OUT" ] ; then + log_error "got reply for upsc listing when none was expected: $OUT" + FAILED="`expr $FAILED + 1`" + else + log_info "OK, empty response as expected" + PASSED="`expr $PASSED + 1`" + fi + else + log_error "upsd was expected to be running although no devices are defined" + FAILED="`expr $FAILED + 1`" + fi + kill -15 $PID_UPSD + wait $PID_UPSD +} + +testgroup_upsd_invalid_configs() { + testcase_upsd_no_configs_at_all + testcase_upsd_no_configs_driver_file + testcase_upsd_no_configs_in_driver_file +} + +testgroup_upsd_questionable_configs() { + testcase_upsd_allow_no_device +} + +######################################################### +### Tests in a common sandbox with driver(s) + server ### +######################################################### + +SANDBOX_CONFIG_GENERATED=false +sandbox_generate_configs() { + if $SANDBOX_CONFIG_GENERATED ; then return ; fi + + log_info "Generating configs for sandbox" + generatecfg_upsd_nodev + generatecfg_upsdusers_trivial + generatecfg_ups_dummy + SANDBOX_CONFIG_GENERATED=true +} + +sandbox_forget_configs() { + SANDBOX_CONFIG_GENERATED=false + if [ -z "${DEBUG_SLEEP-}" ] ; then + stop_daemons + fi +} + +sandbox_start_upsd() { + if isPidAlive "$PID_UPSD" ; then + return 0 + fi + + sandbox_generate_configs + + log_info "Starting UPSD for sandbox" + upsd -F & + PID_UPSD="$!" + sleep 5 +} + +sandbox_start_drivers() { + if isPidAlive "$PID_DUMMYUPS" \ + && { [ x"${TOP_SRCDIR}" != x ] && isPidAlive "$PID_DUMMYUPS1" && isPidAlive "$PID_DUMMYUPS2" \ + || [ x"${TOP_SRCDIR}" = x ] ; } \ + ; then + # All drivers expected for this environment are already running + return 0 + fi + + sandbox_generate_configs + + log_info "Starting dummy-ups driver(s) for sandbox" + #upsdrvctl -F start dummy & + dummy-ups -a dummy -F & + PID_DUMMYUPS="$!" + + if [ x"${TOP_SRCDIR}" != x ]; then + dummy-ups -a UPS1 -F & + PID_DUMMYUPS1="$!" + + dummy-ups -a UPS2 -F & + PID_DUMMYUPS2="$!" + fi + + sleep 5 + + if shouldDebug ; then + (ps -ef || ps -xawwu) 2>/dev/null | grep -E '(ups|nut|dummy)' || true + fi +} + +testcase_sandbox_start_upsd_alone() { + log_separator + log_info "Test starting UPSD but not a driver before it" + sandbox_start_upsd + + EXPECTED_UPSLIST='dummy' + if [ x"${TOP_SRCDIR}" != x ]; then + EXPECTED_UPSLIST="$EXPECTED_UPSLIST +UPS1 +UPS2" + fi + + log_info "Query listing from UPSD by UPSC (driver not running yet)" + OUT="`upsc -l localhost:$NUT_PORT`" || die "upsd does not respond: $OUT" + if [ x"$OUT" != x"$EXPECTED_UPSLIST" ] ; then + log_error "got this reply for upsc listing when '$EXPECTED_UPSLIST' was expected: $OUT" + FAILED="`expr $FAILED + 1`" + else + PASSED="`expr $PASSED + 1`" + fi + + log_info "Query driver state from UPSD by UPSC (driver not running yet)" + OUT="`upsc dummy@localhost:$NUT_PORT 2>&1`" && { + log_error "upsc was supposed to answer with error exit code: $OUT" + FAILED="`expr $FAILED + 1`" + } + if ! echo "$OUT" | grep 'Error: Driver not connected' ; then + log_error "got reply for upsc query when 'Error: Driver not connected' was expected: '$OUT'" + FAILED="`expr $FAILED + 1`" + else + PASSED="`expr $PASSED + 1`" + fi +} + +testcase_sandbox_start_upsd_after_drivers() { + # Historically this is a fallback from testcase_sandbox_start_drivers_after_upsd + kill -15 $PID_UPSD 2>/dev/null + wait $PID_UPSD + upsd -F & + PID_UPSD="$!" + + sandbox_start_drivers + sandbox_start_upsd + + sleep 5 + upsc dummy@localhost:$NUT_PORT || die "upsd does not respond" +} + +testcase_sandbox_start_drivers_after_upsd() { + #sandbox_start_upsd + testcase_sandbox_start_upsd_alone + sandbox_start_drivers + + log_info "Query driver state from UPSD by UPSC after driver startup" + upsc dummy@localhost:$NUT_PORT || { + # Should not get to this, except on very laggy systems maybe + log_error "Query failed, retrying with UPSD started after drivers" + testcase_sandbox_start_upsd_after_drivers + } + + if [ x"${TOP_SRCDIR}" != x ]; then + log_info "Wait for dummy UPSes with larger data sets to initialize" + for U in UPS1 UPS2 ; do + COUNTDOWN=60 + while ! upsc $U@localhost:$NUT_PORT ups.status ; do + sleep 1 + COUNTDOWN="`expr $COUNTDOWN - 1`" + # Systemic error, e.g. could not create socket file? + [ "$COUNTDOWN" -lt 1 ] && die "Dummy driver did not start or respond in time" + done + done + fi + + log_info "Expected drivers are now responding via UPSD" +} + +testcase_sandbox_upsc_query_model() { + OUT="`upsc dummy@localhost:$NUT_PORT device.model`" || die "upsd does not respond: $OUT" + if [ x"$OUT" != x"Dummy UPS" ] ; then + log_error "got this reply for upsc query when 'Dummy UPS' was expected: $OUT" + FAILED="`expr $FAILED + 1`" + else + PASSED="`expr $PASSED + 1`" + fi +} + +testcase_sandbox_upsc_query_bogus() { + log_info "Query driver state from UPSD by UPSC for bogus info" + OUT="`upsc dummy@localhost:$NUT_PORT ups.bogus.value 2>&1`" && { + log_error "upsc was supposed to answer with error exit code: $OUT" + FAILED="`expr $FAILED + 1`" + } + if ! echo "$OUT" | grep 'Error: Variable not supported by UPS' ; then + log_error "got reply for upsc query when 'Error: Variable not supported by UPS' was expected: $OUT" + FAILED="`expr $FAILED + 1`" + else + PASSED="`expr $PASSED + 1`" + fi +} + +testcase_sandbox_upsc_query_timer() { + log_separator + log_info "Test that dummy-ups TIMER action changes the reported state" + # Driver is set up to flip ups.status every 5 sec, so check every 3 + OUT1="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT1" ; sleep 3 + OUT2="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT2" + OUT3="" + OUT4="" + if [ x"$OUT1" = x"$OUT2" ]; then + sleep 3 + OUT3="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT3" + if [ x"$OUT2" = x"$OUT3" ]; then + sleep 3 + OUT4="`upsc dummy@localhost:$NUT_PORT ups.status`" || die "upsd does not respond: $OUT4" + fi + fi + if echo "$OUT1$OUT2$OUT3$OUT4" | grep "OB" && echo "$OUT1$OUT2$OUT3$OUT4" | grep "OL" ; then + log_info "OK, ups.status flips over time" + PASSED="`expr $PASSED + 1`" + else + log_error "ups.status did not flip over time" + FAILED="`expr $FAILED + 1`" + fi +} + +isTestablePython() { + # We optionally make python module (if interpreter is found): + if [ x"${TOP_BUILDDIR}" = x ] \ + || [ ! -x "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" ] \ + ; then + return 1 + fi + return 0 +} + +testcase_sandbox_python_without_credentials() { + isTestablePython || return 0 + log_separator + log_info "Call Python module test suite: PyNUT (NUT Python bindings) without login credentials" + if ( unset NUT_USER || true + unset NUT_PASS || true + "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" + ) ; then + log_info "OK, PyNUT did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "PyNUT complained, check above" + FAILED="`expr $FAILED + 1`" + fi +} + +testcase_sandbox_python_with_credentials() { + isTestablePython || return 0 + + # That script says it expects data/evolution500.seq (as the UPS1 dummy) + # but the dummy data does not currently let issue the commands and + # setvars tested from python script. + log_separator + log_info "Call Python module test suite: PyNUT (NUT Python bindings) with login credentials" + if ( + NUT_USER='admin' + NUT_PASS="${TESTPASS_ADMIN}" + export NUT_USER NUT_PASS + "${TOP_BUILDDIR}/scripts/python/module/test_nutclient.py" + ) ; then + log_info "OK, PyNUT did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "PyNUT complained, check above" + FAILED="`expr $FAILED + 1`" + fi +} + +testcases_sandbox_python() { + isTestablePython || return 0 + testcase_sandbox_python_without_credentials + testcase_sandbox_python_with_credentials +} + +#################################### + +isTestableCppNIT() { + # We optionally make and here can run C++ client tests: + if [ x"${TOP_BUILDDIR}" = x ] \ + || [ ! -x "${TOP_BUILDDIR}/tests/cppnit" ] \ + ; then + return 1 + fi + return 0 +} + +testcase_sandbox_cppnit_without_creds() { + isTestableCppNIT || return 0 + + log_separator + log_info "Call libnutclient test suite: cppnit without login credentials" + if ( unset NUT_USER || true + unset NUT_PASS || true + "${TOP_BUILDDIR}/tests/cppnit" + ) ; then + log_info "OK, cppnit did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "cppnit complained, check above" + FAILED="`expr $FAILED + 1`" + fi +} + +testcase_sandbox_cppnit_simple_admin() { + isTestableCppNIT || return 0 + + log_separator + log_info "Call libnutclient test suite: cppnit with login credentials: simple admin" + if ( + NUT_USER='admin' + NUT_PASS="${TESTPASS_ADMIN}" + if [ x"${TOP_SRCDIR}" != x ]; then + # Avoid dummies with TIMER flip-flops + NUT_SETVAR_DEVICE='UPS2' + else + # Risks failure when lauching sub-test at the wrong second + NUT_SETVAR_DEVICE='dummy' + fi + unset NUT_PRIMARY_DEVICE + export NUT_USER NUT_PASS NUT_SETVAR_DEVICE + "${TOP_BUILDDIR}/tests/cppnit" + ) ; then + log_info "OK, cppnit did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "cppnit complained, check above" + FAILED="`expr $FAILED + 1`" + fi +} + +testcase_sandbox_cppnit_upsmon_primary() { + isTestableCppNIT || return 0 + + log_separator + log_info "Call libnutclient test suite: cppnit with login credentials: upsmon-primary" + if ( + NUT_USER='dummy-admin' + NUT_PASS="${TESTPASS_UPSMON_PRIMARY}" + NUT_PRIMARY_DEVICE='dummy' + unset NUT_SETVAR_DEVICE + export NUT_USER NUT_PASS NUT_PRIMARY_DEVICE + "${TOP_BUILDDIR}/tests/cppnit" + ) ; then + log_info "OK, cppnit did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "cppnit complained, check above" + FAILED="`expr $FAILED + 1`" + fi +} + +testcase_sandbox_cppnit_upsmon_master() { + isTestableCppNIT || return 0 + + log_separator + log_info "Call libnutclient test suite: cppnit with login credentials: upsmon-master" + if ( + NUT_USER='dummy-admin-m' + NUT_PASS="${TESTPASS_UPSMON_PRIMARY}" + NUT_PRIMARY_DEVICE='dummy' + unset NUT_SETVAR_DEVICE + export NUT_USER NUT_PASS NUT_PRIMARY_DEVICE + "${TOP_BUILDDIR}/tests/cppnit" + ) ; then + log_info "OK, cppnit did not complain" + PASSED="`expr $PASSED + 1`" + else + log_error "cppnit complained, check above" + FAILED="`expr $FAILED + 1`" + fi +} + +testcases_sandbox_cppnit() { + isTestableCppNIT || return 0 + testcase_sandbox_cppnit_without_creds + testcase_sandbox_cppnit_upsmon_primary + testcase_sandbox_cppnit_upsmon_master + testcase_sandbox_cppnit_simple_admin +} + +# TODO: Some upsmon tests? + +testgroup_sandbox() { + testcase_sandbox_start_drivers_after_upsd + testcase_sandbox_upsc_query_model + testcase_sandbox_upsc_query_bogus + testcase_sandbox_upsc_query_timer + testcases_sandbox_python + testcases_sandbox_cppnit + + sandbox_forget_configs +} + +testgroup_sandbox_python() { + # Arrange for quick test iterations + testcase_sandbox_start_drivers_after_upsd + testcases_sandbox_python + sandbox_forget_configs +} + +testgroup_sandbox_cppnit() { + # Arrange for quick test iterations + testcase_sandbox_start_drivers_after_upsd + testcases_sandbox_cppnit + sandbox_forget_configs +} + +testgroup_sandbox_cppnit_simple_admin() { + # Arrange for quick test iterations + testcase_sandbox_start_drivers_after_upsd + testcase_sandbox_cppnit_simple_admin + sandbox_forget_configs +} + +################################################################ + +case "${NIT_CASE}" in + cppnit) testgroup_sandbox_cppnit ;; + python) testgroup_sandbox_python ;; + testcase_*|testgroup_*|testcases_*|testgroups_*) + "${NIT_CASE}" ;; + "") # Default test groups: + testgroup_upsd_invalid_configs + testgroup_upsd_questionable_configs + testgroup_sandbox + ;; + *) die "Unsupported NIT_CASE='$NIT_CASE' was requested" ;; +esac + +log_separator +log_info "OVERALL: PASSED=$PASSED FAILED=$FAILED" + +# Allow to leave the sandbox daemons running for a while, +# to experiment with them interactively: +if [ -n "${DEBUG_SLEEP-}" ] ; then + log_separator + log_info "Sleeping now as asked, so you can play with the driver and server running; hint: export NUT_PORT=$NUT_PORT" + log_separator + if [ "${DEBUG_SLEEP-}" -gt 0 ] ; then + sleep "${DEBUG_SLEEP}" + else + sleep 60 + fi + log_info "Sleep finished" + log_separator +fi + +if [ "$PASSED" = 0 ] || [ "$FAILED" != 0 ] ; then + die "Some test scenarios failed!" +else + log_info "SUCCESS" +fi diff --git a/tests/cpputest-client.cpp b/tests/cpputest-client.cpp new file mode 100644 index 0000000..9a6169a --- /dev/null +++ b/tests/cpputest-client.cpp @@ -0,0 +1,415 @@ +/* cpputest-client - CppUnit libnutclient active test + + Module for NUT `cpputest` runner, to check client-server interactions + as part of NIT (NUT Integration Testing) suite and similar scenarios. + This is an "active" NUT client talking to an `upsd` on $NUT_PORT, + as opposed to nutclienttest.cpp which unit-tests the class API etc. + in isolated-binary fashion. + + Copyright (C) 2022 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "common.h" + +/* Current CPPUnit offends the honor of C++98 */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS) +#pragma GCC diagnostic push +# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS +# pragma GCC diagnostic ignored "-Wglobal-constructors" +# endif +# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS +# pragma GCC diagnostic ignored "-Wexit-time-destructors" +# endif +#endif + +#include +#include +#include +#include +#include + +namespace nut { + +class NutActiveClientTest : public CppUnit::TestFixture +{ + /* Note: is "friend" of nut::TcpClient class + * to test a few of its protected methods */ + + CPPUNIT_TEST_SUITE( NutActiveClientTest ); + CPPUNIT_TEST( test_query_ver ); + CPPUNIT_TEST( test_list_ups ); + CPPUNIT_TEST( test_auth_user ); + CPPUNIT_TEST( test_auth_primary ); + CPPUNIT_TEST_SUITE_END(); + +private: + /* Fed by caller via envvars: */ + uint16_t NUT_PORT = 0; + std::string NUT_USER = ""; + std::string NUT_PASS = ""; + std::string NUT_PRIMARY_DEVICE = ""; + std::string NUT_SETVAR_DEVICE = ""; + +public: + void setUp() override; + void tearDown() override; + + void test_query_ver(); + void test_list_ups(); + void test_auth_user(); + void test_auth_primary(); +}; + +// Registers the fixture into the 'registry' +CPPUNIT_TEST_SUITE_REGISTRATION( NutActiveClientTest ); + +} // namespace nut {} + +#ifndef _NUTCLIENTTEST_BUILD +# define _NUTCLIENTTEST_BUILD 1 +#endif + +#include "../clients/nutclient.h" +#include "../clients/nutclientmem.h" + +namespace nut { + +extern "C" { +strarr stringset_to_strarr(const std::set& strset); +strarr stringvector_to_strarr(const std::vector& strset); +} // extern "C" + +void NutActiveClientTest::setUp() +{ + /* NUT_PORT etc. are provided by external test suite driver */ + char * s; + + s = std::getenv("NUT_PORT"); + if (s) { + long l = atol(s); + if (l < 1 || l > 65535) { + throw std::runtime_error("NUT_PORT specified by caller is out of range"); + } + NUT_PORT = static_cast(l); + } else { + throw std::runtime_error("NUT_PORT not specified by caller, NIT should call this test"); + } + + s = std::getenv("NUT_USER"); + if (s) { + NUT_USER = s; + } // else stays empty + + s = std::getenv("NUT_PASS"); + if (s) { + NUT_PASS = s; + } // else stays empty + + s = std::getenv("NUT_PRIMARY_DEVICE"); + if (s) { + NUT_PRIMARY_DEVICE = s; + } // else stays empty + + s = std::getenv("NUT_SETVAR_DEVICE"); + if (s) { + NUT_SETVAR_DEVICE = s; + } // else stays empty +} + +void NutActiveClientTest::tearDown() +{ +} + +void NutActiveClientTest::test_query_ver() { + nut::TcpClient c("localhost", NUT_PORT); + std::string s; + + std::cerr << "[D] C++ NUT Client lib test running against Data Server at: " + << c.getHost() << ':' << c.getPort() << std::endl; + + CPPUNIT_ASSERT_MESSAGE( + "TcpClient is not connected after constructor", + c.isConnected()); + + /* Note: generic client code can not use protected methods + * like low-level sendQuery(), list(), get() and some more, + * but this NutActiveClientTest is a friend of TcpClient: + */ + s = c.sendQuery("VER"); + std::cerr << "[D] Got Data Server VER: " << s << std::endl; + + try { + s = c.sendQuery("PROTVER"); + std::cerr << "[D] Got PROTVER: " << s << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Did not get PROTVER: " << ex.what() << std::endl; + } + + try { + s = c.sendQuery("NETVER"); + std::cerr << "[D] Got NETVER (obsolete): " << s << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Did not get NETVER (obsolete): " << ex.what() << std::endl; + } + + try { + c.logout(); + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not get LOGOUT: " << ex.what() << std::endl; + } + + try { + c.disconnect(); + } + catch(nut::NutException& ex) + { + /* NUT_UNUSED_VARIABLE(ex); */ + std::cerr << "[D] Could not get disconnect(): " << ex.what() << std::endl; + } +} + +void NutActiveClientTest::test_list_ups() { + nut::TcpClient c("localhost", NUT_PORT); + std::set devs; + bool noException = true; + + try { + devs = c.getDeviceNames(); + std::cerr << "[D] Got device list (" << devs.size() << "): ["; + for (std::set::iterator it = devs.begin(); + it != devs.end(); it++ + ) { + if (it != devs.begin()) { + std::cerr << ", "; + } + std::cerr << '"' << *it << '"'; + } + std::cerr << "]" << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not device list: " << ex.what() << std::endl; + noException = false; + } + + c.logout(); + c.disconnect(); + + CPPUNIT_ASSERT_MESSAGE( + "Failed to list UPS with TcpClient: threw NutException", + noException); +} + +void NutActiveClientTest::test_auth_user() { + if (NUT_USER.empty()) { + std::cerr << "[D] SKIPPING test_auth_user()" << std::endl; + return; + } + + nut::TcpClient c("localhost", NUT_PORT); + bool noException = true; + try { + c.authenticate(NUT_USER, NUT_PASS); + std::cerr << "[D] Authenticated without exceptions" << std::endl; + /* Note: no high hopes here, credentials are checked by server + * when running critical commands, not at auth request itself */ + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not authenticate as a simple user: " << ex.what() << std::endl; + noException = false; + } + + if (!NUT_SETVAR_DEVICE.empty()) { + try { + TrackingResult tres; + TrackingID tid; + int i; + std::string nutVar = "ups.status"; /* Has a risk of flip-flop with NIT dummy setup */ + std::string s1 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + std::string sTest = s1 + "-test"; + + std::cerr << "[D] Got initial device '" << NUT_SETVAR_DEVICE + << "' variable '" << nutVar << "' value: " << s1 << std::endl; + CPPUNIT_ASSERT_MESSAGE( + "Did not expect empty value here", + !s1.empty()); + + tid = c.setDeviceVariable(NUT_SETVAR_DEVICE, nutVar, sTest); + while ( (tres = c.getTrackingResult(tid)) == PENDING) { + usleep(100); + } + if (tres != SUCCESS) { + std::cerr << "[D] Failed to set device variable: " + << "tracking result is " << tres << std::endl; + noException = false; + } + /* Check what we got after set */ + /* Note that above we told the server to tell the driver + * to set a dstate entry; below we ask the server to ask + * the driver and relay the answer to us. The dummy-ups + * driver may also be in a sleeping state between cycles. + * Data propagation may be not instantaneous, so we loop + * for a while to see the expected value (or give up). + */ + std::string s2; + for (i = 0; i < 100 ; i++) { + s2 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + if (s2 == sTest) + break; + usleep(100000); + } + std::cerr << "[D] Read back: " << s2 + << " after " << (100 * i) << "msec" + << std::endl; + + /* Fix it back */ + tid = c.setDeviceVariable(NUT_SETVAR_DEVICE, nutVar, s1); + while ( (tres = c.getTrackingResult(tid)) == PENDING) { + usleep(100); + } + if (tres != SUCCESS) { + std::cerr << "[D] Failed to set device variable: " + << "tracking result is " << tres << std::endl; + noException = false; + } + std::string s3; + for (i = 0; i < 100 ; i++) { + s3 = c.getDeviceVariableValue(NUT_SETVAR_DEVICE, nutVar)[0]; + if (s3 == s1) + break; + usleep(100000); + } + std::cerr << "[D] Read back: " << s3 + << " after " << (100 * i) << "msec" + << std::endl; + + if (s3 != s1) { + std::cerr << "[D] Final device variable value '" << s3 + << "' differs from original '" << s1 + << "'" << std::endl; + noException = false; + } + + if (s2 == s1) { + std::cerr << "[D] Tweaked device variable value '" << s2 + << "' does not differ from original '" << s1 + << "'" << std::endl; + noException = false; + } + + if (noException) { + std::cerr << "[D] Tweaked device variable value OK" << std::endl; + } + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Failed to set device variable: " + << ex.what() << std::endl; + noException = false; + } + } else { + std::cerr << "[D] SKIPPING test_auth_user() active test " + << "(got no NUT_SETVAR_DEVICE to poke)" << std::endl; + } + + c.logout(); + c.disconnect(); + + CPPUNIT_ASSERT_MESSAGE( + "Failed to auth as user with TcpClient or tweak device variable", + noException); +} + +void NutActiveClientTest::test_auth_primary() { + if (NUT_USER.empty() || NUT_PRIMARY_DEVICE.empty()) { + std::cerr << "[D] SKIPPING test_auth_primary()" << std::endl; + return; + } + + nut::TcpClient c("localhost", NUT_PORT); + bool noException = true; + try { + c.authenticate(NUT_USER, NUT_PASS); + std::cerr << "[D] Authenticated without exceptions" << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not authenticate as an upsmon primary user: " + << ex.what() << std::endl; + noException = false; + } + + try { + Device d = c.getDevice(NUT_PRIMARY_DEVICE); + bool gotPrimary = false; + bool gotMaster = false; + + try { + c.deviceMaster(NUT_PRIMARY_DEVICE); + gotMaster = true; + std::cerr << "[D] Elevated as MASTER without exceptions" << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not elevate as MASTER for " + << "NUT_PRIMARY_DEVICE " << NUT_PRIMARY_DEVICE << ": " + << ex.what() << std::endl; + } + + try { + c.devicePrimary(NUT_PRIMARY_DEVICE); + gotPrimary = true; + std::cerr << "[D] Elevated as PRIMARY without exceptions" << std::endl; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] Could not elevate as PRIMARY for " + << "NUT_PRIMARY_DEVICE " << NUT_PRIMARY_DEVICE << ": " + << ex.what() << std::endl; + } + + if (!gotMaster && !gotPrimary) + noException = false; + } + catch(nut::NutException& ex) + { + std::cerr << "[D] NUT_PRIMARY_DEVICE " << NUT_PRIMARY_DEVICE + << " not found on Data Server: " + << ex.what() << std::endl; + noException = false; + } + + c.logout(); + c.disconnect(); + + CPPUNIT_ASSERT_MESSAGE( + "Failed to auth as user with TcpClient: threw NutException", + noException); +} + +} // namespace nut {} + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS) +#pragma GCC diagnostic pop +#endif diff --git a/tests/cpputest.cpp b/tests/cpputest.cpp new file mode 100644 index 0000000..d0b0adb --- /dev/null +++ b/tests/cpputest.cpp @@ -0,0 +1,93 @@ +/* cpputest - basic runner for unit tests + + Copyright (C) + 2012 Emilien Kia + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include +#include +#include +#include +#include +#include +#include "common.h" + +// Inspired by https://stackoverflow.com/a/66702001 +class MyCustomProgressTestListener : public CppUnit::TextTestProgressListener { + public: + virtual void startTest(CppUnit::Test *test) override; +}; + +// Implement out of class declaration to avoid +// error: 'MyCustomProgressTestListener' has no out-of-line virtual method +// definitions; its vtable will be emitted in every translation unit +// [-Werror,-Wweak-vtables] +void MyCustomProgressTestListener::startTest(CppUnit::Test *test) { + //fprintf(stderr, "starting test %s\n", test->getName().c_str()); + std::cerr << "starting test " << test->getName() << std::endl; +} + +int main(int argc, char* argv[]) +{ + bool verbose = false; + if (argc > 1) { + if (strcmp("-v", argv[1]) == 0 || strcmp("--verbose", argv[1]) == 0 ) { + verbose = true; + } + } + + /* Get the top level suite from the registry */ + std::cerr << "D: Getting test suite..." << std::endl; + CppUnit::Test *suite = CppUnit::TestFactoryRegistry::getRegistry().makeTest(); + + /* Adds the test to the list of test to run */ + std::cerr << "D: Preparing test runner..." << std::endl; + CppUnit::TextUi::TestRunner runner; + runner.addTest( suite ); + + /* Change the default outputter to a compiler error format outputter */ + std::cerr << "D: Setting test runner outputter..." << std::endl; + runner.setOutputter( new CppUnit::CompilerOutputter( &runner.result(), + std::cerr ) ); + + if (verbose) { + /* Add a listener to report test names */ + std::cerr << "D: Setting test runner listener for test names..." << std::endl; + MyCustomProgressTestListener progress; + runner.eventManager().addListener(&progress); + } + + /* Run the tests. */ + bool wasSucessful = false; + try { + std::cerr << "D: Launching the test run..." << std::endl; + wasSucessful = runner.run(); + } + catch ( std::invalid_argument &e ) // Test path not resolved + { + std::cerr << std::endl + << "ERROR: " << e.what() + << std::endl; + wasSucessful = false; + } + + /* Return error code 1 if the one of test failed. */ + std::cerr << "D: Got to the end of test suite with code " << + "'" << ( wasSucessful ? "true" : "false" ) << "'" << std::endl; + return wasSucessful ? 0 : 1; +} diff --git a/tests/example.cpp b/tests/example.cpp new file mode 100644 index 0000000..cb52e93 --- /dev/null +++ b/tests/example.cpp @@ -0,0 +1,79 @@ +/* example - CppUnit unit test example + + Copyright (C) + 2012 Emilien Kia + 2020 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "common.h" + +/* Current CPPUnit offends the honor of C++98 */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS) +#pragma GCC diagnostic push +# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS +# pragma GCC diagnostic ignored "-Wglobal-constructors" +# endif +# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS +# pragma GCC diagnostic ignored "-Wexit-time-destructors" +# endif +#endif + +#include + +class ExampleTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE( ExampleTest ); + CPPUNIT_TEST( testOne ); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() override; + void tearDown() override; + + void testOne(); +}; + +// Registers the fixture into the 'registry' +CPPUNIT_TEST_SUITE_REGISTRATION( ExampleTest ); + + +void ExampleTest::setUp() +{ +} + + +void ExampleTest::tearDown() +{ +} + + +void ExampleTest::testOne() +{ + // Set up + int i = 1; + float f = 1.0; + + // Process + int cast = static_cast(f); + + // Check + CPPUNIT_ASSERT_EQUAL( i, cast ); +} + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS) +#pragma GCC diagnostic pop +#endif diff --git a/tests/getvaluetest.c b/tests/getvaluetest.c new file mode 100644 index 0000000..769df06 --- /dev/null +++ b/tests/getvaluetest.c @@ -0,0 +1,282 @@ +/* getvaluetest - check that the bitness/endianness dependent conversions + * of representation of numeric types between wire protocols and different + * CPU computations produce expected results. + * + * See also: + * https://github.com/networkupstools/nut/pull/1055 + * https://github.com/networkupstools/nut/pull/1040 + * https://github.com/networkupstools/nut/pull/1024 + * https://github.com/networkupstools/nut/issues/1023 + * + * Copyright (C) + * 2021 Nick Briggs + * 2022 Jim Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include "hidtypes.h" +#include "usb-common.h" +#include "common.h" + +void GetValue(const unsigned char *Buf, HIDData_t *pData, long *pValue); + +static void Usage(char *name) { + printf("%s [ ]\n", name); + printf(" - string of hex digit pairs, space separated\n"); + printf(" - offset of the report item value in bits, typically 0..31\n"); + printf(" - size of the report item value in bits: typically 1..32\n"); + printf(" - logical minimum value for report item\n"); + printf(" - logical maximum value for report item\n"); + printf(" - expected value\n"); + printf("\n"); + printf("%s \"0c 64 11 0d\" 8 16 0 65535 3345\n", name); + printf("\nIf no arguments are given a builtin set of tests are run.\n"); +} + +static void PrintBufAndData(uint8_t *buf, size_t bufSize, HIDData_t *pData) { + size_t i; + + printf("buf \""); + for (i = 0; i < bufSize - 1; i++) { + printf("%02x ", buf[i]); + } + printf("%02x\"", buf[bufSize - 1]); + printf(" offset %u size %u logmin %ld (0x%lx) logmax %ld (0x%lx)", + pData->Offset, pData->Size, pData->LogMin, pData->LogMin, pData->LogMax, pData->LogMax); +} + +static int RunBuiltInTests(char *argv[]) { + NUT_UNUSED_VARIABLE(argv); + + int exitStatus = 0; + size_t i; + char *next; + uint8_t reportBuf[64]; + size_t bufSize; + HIDData_t data; + long value; + + static struct { + char *buf; /* item data, starts with report id byte, then remaining report bytes */ + uint8_t Offset; /* item offset in bits, typically 0..31 */ + uint8_t Size; /* item size in bits, typically 1..32 */ + long LogMin, LogMax; /* logical minimum and maximum values */ + long expectedValue; /* the expected result of decoding the value in the buffer */ + } testData[] = { + {.buf = "00 ff ff ff ff", .Offset = 0, .Size = 32, .LogMin = -1, .LogMax = 2147483647, .expectedValue = -1}, + {.buf = "00 ff", .Offset = 0, .Size = 8, .LogMin = -1, .LogMax = 127, .expectedValue = -1}, + {.buf = "00 ff", .Offset = 0, .Size = 8, .LogMin = 0, .LogMax = 127, .expectedValue = 127}, + {.buf = "00 ff", .Offset = 0, .Size = 8, .LogMin = 0, .LogMax = 255, .expectedValue = 255}, + {.buf = "33 00 0a 08 80", .Offset = 0, .Size = 32, .LogMin = 0, .LogMax = 65535, .expectedValue = 2560}, + {.buf = "00 00 08 00 00", .Offset = 0, .Size = 32, .LogMin = 0, .LogMax = 65535, .expectedValue = 2048}, + {.buf = "06 00 00 08", .Offset = 0, .Size = 8, .LogMin = 0, .LogMax = 255, .expectedValue = 0}, + {.buf = "06 00 00 08", .Offset = 8, .Size = 8, .LogMin = 0, .LogMax = 255, .expectedValue = 0}, + {.buf = "06 00 00 08", .Offset = 16, .Size = 8, .LogMin = 0, .LogMax = 255, .expectedValue = 8}, + {.buf = "16 0c 00 00 00", .Offset = 0, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0}, + {.buf = "16 0c 00 00 00", .Offset = 1, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0}, + {.buf = "16 0c 00 00 00", .Offset = 2, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 1}, + {.buf = "16 0c 00 00 00", .Offset = 3, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 1}, + {.buf = "16 0c 00 00 00", .Offset = 4, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0}, + {.buf = "16 0c 00 00 00", .Offset = 5, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0}, + {.buf = "16 0c 00 00 00", .Offset = 6, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0}, + {.buf = "16 0c 00 00 00", .Offset = 7, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0}, + {.buf = "16 0c 00 00 00", .Offset = 8, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0}, + {.buf = "16 0c 00 00 00", .Offset = 9, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0}, + {.buf = "16 0c 00 00 00", .Offset = 10, .Size = 1, .LogMin = 0, .LogMax = 1, .expectedValue = 0} + }; + + for (i = 0; i < sizeof(testData)/sizeof(testData[0]); i++) { + next = testData[i].buf; + for (bufSize = 0; *next != 0; bufSize++) { + reportBuf[bufSize] = (uint8_t) strtol(next, (char **)&next, 16); + } + memset((void *)&data, 0, sizeof(data)); + data.Offset = testData[i].Offset; + data.Size = testData[i].Size; + data.LogMin = testData[i].LogMin; + data.LogMax = testData[i].LogMax; + + GetValue(reportBuf, &data, &value); + + printf("Test #%zd ", i + 1); + PrintBufAndData(reportBuf, bufSize, &data); + if (value == testData[i].expectedValue) { + printf(" value %ld PASS\n", value); + } else { + printf(" value %ld FAIL expected %ld\n", value, testData[i].expectedValue); + exitStatus = 1; + } + } + + /* Emulate rdlen calculations in libusb{0,1}.c or + * langid calculations in nutdrv_qx.c; in these + * cases we take two bytes (cast from usb_ctrl_char + * type, may be signed depending on used API version) + * from the protocol buffer, and build a platform + * dependent representation of a two-byte word. + */ + usb_ctrl_char bufC[2]; + signed char bufS[2]; + unsigned char bufU[2]; + int rdlen; + + /* Example from issue https://github.com/networkupstools/nut/issues/1261 + * where resulting length 0x01a9 should be "425" but ended up "-87" */ + bufC[0] = (usb_ctrl_char)0xa9; + bufC[1] = (usb_ctrl_char)0x01; + + bufS[0] = (signed char)0xa9; + bufS[1] = (signed char)0x01; + + bufU[0] = (unsigned char)0xa9; + bufU[1] = (unsigned char)0x01; + + /* Check different conversion methods and hope current build CPU, + * C implementation etc. do not mess up bit-shifting vs rotation, + * zeroing high bits, int type width extension et al. If something + * is mismatched below, the production NUT code may need adaptations + * for that platform to count stuff correctly! + */ + printf("\nTesting bit-shifting approaches used in codebase to get 2-byte int lengths from wire bytes:\n"); + printf("(Expected correct value is '425', incorrect '-87' or other)\n"); + +#define REPORT_VERDICT(expected) { if (expected) { printf(" - PASS\n"); } else { printf(" - FAIL\n"); exitStatus = 1; } } + + rdlen = bufC[0] | (bufC[1] << 8); + printf(" * reference: no casting, usb_ctrl_char :\t%d\t(depends on libusb API built against)", rdlen); + REPORT_VERDICT (rdlen == 425 || rdlen == -87) + + rdlen = bufS[0] | (bufS[1] << 8); + printf(" * reference: no casting, signed char :\t%d\t(expected '-87' here)", rdlen); + REPORT_VERDICT (rdlen == -87) + + rdlen = bufU[0] | (bufU[1] << 8); + printf(" * reference: no casting, unsigned char :\t%d\t(expected '425')", rdlen); + REPORT_VERDICT (rdlen == 425) + + + rdlen = (uint8_t)bufC[0] | ((uint8_t)bufC[1] << 8); + printf(" * uint8_t casting, usb_ctrl_char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = (uint8_t)bufS[0] | ((uint8_t)bufS[1] << 8); + printf(" * uint8_t casting, signed char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = (uint8_t)bufU[0] | ((uint8_t)bufU[1] << 8); + printf(" * uint8_t casting, unsigned char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + + rdlen = ((uint8_t)bufC[0]) | (((uint8_t)bufC[1]) << 8); + printf(" * uint8_t casting with parentheses, usb_ctrl_char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = ((uint8_t)bufS[0]) | (((uint8_t)bufS[1]) << 8); + printf(" * uint8_t casting with parentheses, signed char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = ((uint8_t)bufU[0]) | (((uint8_t)bufU[1]) << 8); + printf(" * uint8_t casting with parentheses, unsigned char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + + rdlen = ((uint16_t)(bufC[0]) & 0x00FF) | (((uint16_t)(bufC[1]) & 0x00FF) << 8); + printf(" * uint16_t casting with 8-bit mask, usb_ctrl_char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = ((uint16_t)(bufS[0]) & 0x00FF) | (((uint16_t)(bufS[1]) & 0x00FF) << 8); + printf(" * uint16_t casting with 8-bit mask, signed char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = ((uint16_t)(bufU[0]) & 0x00FF) | (((uint16_t)(bufU[1]) & 0x00FF) << 8); + printf(" * uint16_t casting with 8-bit mask, unsigned char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + + rdlen = 256 * (uint8_t)(bufC[1]) + (uint8_t)(bufC[0]); + printf(" * uint8_t casting with multiplication, usb_ctrl_char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = 256 * (uint8_t)(bufS[1]) + (uint8_t)(bufS[0]); + printf(" * uint8_t casting with multiplication, signed char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + rdlen = 256 * (uint8_t)(bufU[1]) + (uint8_t)(bufU[0]); + printf(" * uint8_t casting with multiplication, unsigned char :\t%d", rdlen); + REPORT_VERDICT (rdlen == 425) + + + return (exitStatus); +} + +static int RunCommandLineTest(char *argv[]) { + uint8_t reportBuf[64]; + size_t bufSize; + char *start, *end; + HIDData_t data; + long value, expectedValue; + + start = argv[1]; + end = NULL; + for (bufSize = 0; *start != 0; bufSize++) { + reportBuf[bufSize] = (uint8_t) strtol(start, (char **)&end, 16); + if (start == end) break; + start = end; + } + memset((void *)&data, 0, sizeof(data)); + data.Offset = (uint8_t) atoi(argv[2]); + data.Size = (uint8_t) atoi(argv[3]); + data.LogMin = strtol(argv[4], 0, 0); + data.LogMax = strtol(argv[5], 0, 0); + expectedValue = strtol(argv[6], 0, 0); + + GetValue(reportBuf, &data, &value); + + printf("Test #0 "); + PrintBufAndData(reportBuf, bufSize, &data); + if (value == expectedValue) { + printf(" value %ld PASS\n", value); + return (0); + } else { + printf(" value %ld FAIL expected %ld\n", value, expectedValue); + return (1); + } +} + +int main (int argc, char *argv[]) { + int status; + + switch (argc) { + case 1: + status = RunBuiltInTests(argv); + break; + case 7: + status = RunCommandLineTest(argv); + break; + default: + Usage(argv[0]); + status = 2; + } + return(status); +} diff --git a/tests/nut-driver-enumerator-test--ups.conf b/tests/nut-driver-enumerator-test--ups.conf new file mode 100644 index 0000000..c2916e2 --- /dev/null +++ b/tests/nut-driver-enumerator-test--ups.conf @@ -0,0 +1,71 @@ +# This is an ups.conf file for nut-driver-enumerator-test.sh +# It is intentionally written in different samples of markup, +# do not clean it up ;) + +maxstartdelay=180 +globalflag + +[dummy1] +driver = dummy-ups + port = file1.dev + desc = "This is ups-1" +[epdu-2] +# This is an ePDU +driver=netxml-ups +port="http://172.16.1.2" +synchronous=yes +[epdu-2-snmp] +driver=snmp-ups +port=172.16.1.2 +synchronous=no + +[usb_3] + driver = "usbhid-ups" + port = "auto" + +[serial.4] + + driver = serial-ups +driverflag + port = /dev/ttyS1 # some path + + +[dummy-proxy] +driver = "dummy-ups " + port = remoteUPS@RemoteHost.local + +[dummy-proxy-localhost] +driver = 'dummy-ups ' + port = "localUPS@127.0.0.1" + +[valueHasEquals] + driver = dummy=ups + port = file1.dev # key = val, right? + +[valueHasHashtag] + driver = dummy-ups + port = file#1.dev + +[valueHasQuotedHashtag] + driver = dummy-ups + port = "file#1.dev" + +[qx-serial] + driver=nutdrv_qx + port = /dev/ttyb + +[qx-usb1] + driver=nutdrv_qx + port = auto +[qx-usb2] + driver=nutdrv_qx + port = /dev/usb/8 +[sectionWithComment]# Some comment + driver=nutdrv_qx#comment + port = /dev/usb/8 + desc="value with [brackets]" + [brackets with spaces are not sections] # but rather an invalid mess as binary parser may think + [sectionWithCommentWhitespace] # Some comment with a space and tab + driver=nutdrv_qx # comment + port = /dev/usb/8 # comment + commentedDriverFlag # This flag gotta mean something diff --git a/tests/nut-driver-enumerator-test.sh b/tests/nut-driver-enumerator-test.sh new file mode 100755 index 0000000..dd91a58 --- /dev/null +++ b/tests/nut-driver-enumerator-test.sh @@ -0,0 +1,314 @@ +#!/bin/sh + +# Copyright (C) 2018 Eaton +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# +#! \file nut-driver-enumerator-test.sh +# \author Jim Klimov +# \brief Self-test for nut-driver-enumerator.sh utility +# \details Automated sanity test for nut-driver-enumerator.sh(.in) +# using different shells (per $SHELL_PROGS) and CLI requests +# for regression and compatibility tests as well as for TDD +# fueled by pre-decided expected outcomes. + +### Use a standard locale setup so sorting in expected results is not confused +LANG=C +LC_ALL=C +TZ=UTC +export LANG LC_ALL TZ + +### Note: These are relative to where the selftest script lives, +### not the NUT top_srcdir etc. They can be exported by a Makefile. +[ -n "${BUILDDIR-}" ] || BUILDDIR="`dirname $0`" +[ -n "${SRCDIR-}" ] || SRCDIR="`dirname $0`" +[ -n "${SHELL_PROGS-}" ] || SHELL_PROGS="/bin/sh" +case "${DEBUG-}" in + [Yy]|[Yy][Ee][Ss]) DEBUG=yes ;; + [Tt][Rr][Aa][Cc][Ee]) DEBUG=trace ;; + *) DEBUG="" ;; +esac + +SYSTEMD_CONFPATH="${BUILDDIR}/selftest-rw/systemd-units" +export SYSTEMD_CONFPATH + +NUT_CONFPATH="${BUILDDIR}/selftest-rw/nut" +export NUT_CONFPATH + +[ -n "${UPSCONF-}" ] || UPSCONF="${SRCDIR}/nut-driver-enumerator-test--ups.conf" +[ ! -s "${UPSCONF-}" ] && echo "FATAL : testing ups.conf not found as '$UPSCONF'" >&2 && exit 1 +export UPSCONF + +if [ ! -n "${NDE-}" ] ; then + for NDE in \ + "${BUILDDIR}/../scripts/upsdrvsvcctl/nut-driver-enumerator.sh" \ + "${SRCDIR}/../scripts/upsdrvsvcctl/nut-driver-enumerator.sh.in" \ + ; do [ -s "$NDE" ] && break ; done +fi +[ ! -s "${NDE-}" ] && echo "FATAL : testing nut-driver-enumerator.sh implementation not found as '$NDE'" >&2 && exit 1 + +# TODO : Add tests that generate configuration files for units +#mkdir -p "${NUT_CONFPATH}" "${SYSTEMD_CONFPATH}" || exit + +FAIL_COUNT=0 +GOOD_COUNT=0 +callNDE() { + case "$DEBUG" in + yes) time $USE_SHELL $NDE "$@" ;; + trace) time $USE_SHELL -x $NDE "$@" ;; + *) $USE_SHELL $NDE "$@" 2>/dev/null ;; + esac +} + +run_testcase() { + # First 3 args are required as defined below; the rest are + # CLI arg(s) to nut-driver-enumerator.sh + CASE_DESCR="$1" + EXPECT_CODE="$2" + EXPECT_TEXT="$3" + shift 3 + + printf "Testing : SHELL='%s'\tCASE='%s'\t" "$USE_SHELL" "$CASE_DESCR" + OUT="`callNDE "$@"`" ; RESCODE=$? + printf "Got : RESCODE='%s'\t" "$RESCODE" + + RES=0 + if [ "$RESCODE" = "$EXPECT_CODE" ]; then + printf "STATUS_CODE='MATCHED'\t" + GOOD_COUNT="`expr $GOOD_COUNT + 1`" + else + printf "STATUS_CODE='MISMATCH' expect_code=%s received_code=%s\t" "$EXPECT_CODE" "$RESCODE" >&2 + FAIL_COUNT="`expr $FAIL_COUNT + 1`" + RES="`expr $RES + 1`" + fi + + if [ "$OUT" = "$EXPECT_TEXT" ]; then + printf "STATUS_TEXT='MATCHED'\n" + GOOD_COUNT="`expr $GOOD_COUNT + 1`" + else + printf "STATUS_TEXT='MISMATCH'\n" + printf '\t--- expected ---\n%s\n\t--- received ---\n%s\n\t--- MISMATCH ABOVE\n\n' "$EXPECT_TEXT" "$OUT" >&2 + # Give a nice output to help track the problem: + ( rm -f "/tmp/.nde.text.expected.$$" "/tmp/.nde.text.actual.$$" \ + && echo "$EXPECT_TEXT" > "/tmp/.nde.text.expected.$$" \ + && echo "$OUT" > "/tmp/.nde.text.actual.$$" \ + && diff -u "/tmp/.nde.text.expected.$$" "/tmp/.nde.text.actual.$$" ) 2>/dev/null || true + rm -f "/tmp/.nde.text.expected.$$" "/tmp/.nde.text.actual.$$" + FAIL_COUNT="`expr $FAIL_COUNT + 1`" + RES="`expr $RES + 2`" + fi + if [ "$RES" != 0 ] || [ -n "$DEBUG" ] ; then echo "" ; fi + return $RES +} + +################################################################## +# Note: expectations in test cases below are tightly connected # +# to both the current code in the script and content of the test # +# configuration file. # +################################################################## + +testcase_bogus_args() { + run_testcase "Reject unknown args" 1 "" \ + --some-bogus-arg +} + +testcase_list_all_devices() { + # We expect a list of unbracketed names from the device sections + # Note: unlike other outputs, this list is alphabetically sorted + run_testcase "List all device names from sections" 0 \ +"dummy-proxy +dummy-proxy-localhost +dummy1 +epdu-2 +epdu-2-snmp +qx-serial +qx-usb1 +qx-usb2 +sectionWithComment +sectionWithCommentWhitespace +serial.4 +usb_3 +valueHasEquals +valueHasHashtag +valueHasQuotedHashtag" \ + --list-devices +} + +testcase_show_all_configs() { + # We expect whitespace trimmed, comment-only lines removed + run_testcase "Show all configs" 0 \ +'maxstartdelay=180 +globalflag +[dummy1] +driver=dummy-ups +port=file1.dev +desc="This is ups-1" +[epdu-2] +driver=netxml-ups +port=http://172.16.1.2 +synchronous=yes +[epdu-2-snmp] +driver=snmp-ups +port=172.16.1.2 +synchronous=no +[usb_3] +driver=usbhid-ups +port=auto +[serial.4] +driver=serial-ups +driverflag +port=/dev/ttyS1 # some path +[dummy-proxy] +driver="dummy-ups " +port=remoteUPS@RemoteHost.local +[dummy-proxy-localhost] +driver='"'dummy-ups '"' +port=localUPS@127.0.0.1 +[valueHasEquals] +driver=dummy=ups +port=file1.dev # key = val, right? +[valueHasHashtag] +driver=dummy-ups +port=file#1.dev +[valueHasQuotedHashtag] +driver=dummy-ups +port=file#1.dev +[qx-serial] +driver=nutdrv_qx +port=/dev/ttyb +[qx-usb1] +driver=nutdrv_qx +port=auto +[qx-usb2] +driver=nutdrv_qx +port=/dev/usb/8 +[sectionWithComment] +driver=nutdrv_qx#comment +port=/dev/usb/8 +desc="value with [brackets]" +[brackets with spaces are not sections] # but rather an invalid mess as binary parser may think +[sectionWithCommentWhitespace] +driver=nutdrv_qx # comment +port=/dev/usb/8 # comment +commentedDriverFlag # This flag gotta mean something' \ + --show-all-configs +} + +testcase_upslist_debug() { + # We expect a list of names, ports and decided MEDIA type (for dependencies) + run_testcase "List decided MEDIA and config checksums for all devices" 0 \ +"INST: 68b329da9893e34099c7d8ad5cb9c940~[]: DRV='' PORT='' MEDIA='' SECTIONMD5='9a1f372a850f1ee3ab1fc08b185783e0' +INST: 010cf0aed6dd49865bb49b70267946f5~[dummy-proxy]: DRV='dummy-ups ' PORT='remoteUPS@RemoteHost.local' MEDIA='network' SECTIONMD5='aff543fc07d7fbf83e81001b181c8b97' +INST: 1ea79c6eea3681ba73cc695f3253e605~[dummy-proxy-localhost]: DRV='dummy-ups ' PORT='localUPS@127.0.0.1' MEDIA='network-localhost' SECTIONMD5='73e6b7e3e3b73558dc15253d8cca51b2' +INST: 76b645e28b0b53122b4428f4ab9eb4b9~[dummy1]: DRV='dummy-ups' PORT='file1.dev' MEDIA='' SECTIONMD5='9e0a326b67e00d455494f8b4258a01f1' +INST: a293d65e62e89d6cc3ac6cb88bc312b8~[epdu-2]: DRV='netxml-ups' PORT='http://172.16.1.2' MEDIA='network' SECTIONMD5='0d9a0147dcf87c7c720e341170f69ed4' +INST: 9a5561464ff8c78dd7cb544740ce2adc~[epdu-2-snmp]: DRV='snmp-ups' PORT='172.16.1.2' MEDIA='network' SECTIONMD5='2631b6c21140cea0dd30bb88b942ce3f' +INST: 16adbdafb22d9fdff1d09038520eb32e~[qx-serial]: DRV='nutdrv_qx' PORT='/dev/ttyb' MEDIA='serial' SECTIONMD5='e3e6e586fbe5b3c0a89432f4b993f4ad' +INST: a21bd2b786228b9619f6adba6db8fa83~[qx-usb1]: DRV='nutdrv_qx' PORT='auto' MEDIA='usb' SECTIONMD5='a6139c5da35bef89dc5b96e2296f5369' +INST: 0066605e07c66043a17eccecbeea1ac5~[qx-usb2]: DRV='nutdrv_qx' PORT='/dev/usb/8' MEDIA='usb' SECTIONMD5='5722dd9c21d07a1f5bcb516dbc458deb' +INST: 1280a731e03116f77290e51dd2a2f37e~[sectionWithComment]: DRV='nutdrv_qx#comment' PORT='/dev/usb/8' MEDIA='' SECTIONMD5='be30e15e17d0579c85eecaf176b4a064' +INST: 770abd5659061a29ed3ae4f7c0b00915~[sectionWithCommentWhitespace]: DRV='nutdrv_qx # comment' PORT='/dev/usb/8 # comment' MEDIA='' SECTIONMD5='c757822a331521cdc97310d0241eba28' +INST: efdb1b4698215fdca36b9bc06d24661d~[serial.4]: DRV='serial-ups' PORT='/dev/ttyS1 # some path' MEDIA='' SECTIONMD5='9c485f733aa6d6c85c1724f162929443' +INST: f4a1c33db201c2ca897a3337993c10fc~[usb_3]: DRV='usbhid-ups' PORT='auto' MEDIA='usb' SECTIONMD5='1f6a24becde9bd31c9852610658ef84a' +INST: 8e5686f92a5ba11901996c813e7bb23d~[valueHasEquals]: DRV='dummy=ups' PORT='file1.dev # key = val, right?' MEDIA='' SECTIONMD5='2f04d65da53e3b13771bb65422f0f4c0' +INST: 99da99b1e301e84f34f349443aac545b~[valueHasHashtag]: DRV='dummy-ups' PORT='file#1.dev' MEDIA='' SECTIONMD5='6029bda216de0cf1e81bd55ebd4a0fff' +INST: d50c3281f9b68a94bf9df72a115fbb5c~[valueHasQuotedHashtag]: DRV='dummy-ups' PORT='file#1.dev' MEDIA='' SECTIONMD5='af59c3c0caaa68dcd796d7145ae403ee'" \ + upslist_debug + + # FIXME : in [valueHasEquals] and [serial.4] the PORT value is quite bogus + # with its embedded comments. Check vs. binary config parser, whether in + # unquoted case only first token is the valid value, and how comments are + # handled in general? + # FIXME : in [valueHasHashtag] the line after "#" should likely be dropped + # (check in binary config parser first) while in [valueHasQuotedHashtag] + # it should stay. +} + +testcase_getValue() { + run_testcase "Query a configuration key (SDP)" 0 \ + "file1.dev" \ + --show-device-config-value dummy1 port + + run_testcase "Query a configuration key (other)" 0 \ + "yes" \ + --show-device-config-value epdu-2 synchronous + + run_testcase "Query a configuration key (originally quoted)" 0 \ + 'This is ups-1' \ + --show-device-config-value dummy1 desc + + run_testcase "Query a configuration flag (driver)" 0 \ + "driverflag" \ + --show-config-value 'serial.4' driverflag + + run_testcase "Query a missing configuration flag (driver)" 1 \ + "" \ + --show-config-value 'valueHasQuotedHashtag' nosuchflag + + run_testcase "Query multiple configuration keys (originally quoted)" 0 \ + 'This is ups-1 +file1.dev' \ + --show-device-config-value dummy1 desc port + + run_testcase "Query multiple configuration keys with some missing (originally quoted)" 1 \ + 'This is ups-1 + +file1.dev' \ + --show-device-config-value dummy1 desc unknownkey port +} + +testcase_globalSection() { + run_testcase "Display global config" 0 \ + "maxstartdelay=180 +globalflag" \ + --show-config '' + + run_testcase "Query a configuration key (global)" 0 \ + "180" \ + --show-config-value '' maxstartdelay + + run_testcase "Query a configuration flag (global)" 0 \ + "globalflag" \ + --show-config-value '' globalflag + + run_testcase "Query a missing configuration flag (global)" 1 \ + "" \ + --show-config-value '' nosuchflag +} + + +# Combine the cases above into a stack +testsuite() { + testcase_bogus_args + testcase_list_all_devices + testcase_show_all_configs + testcase_getValue + testcase_globalSection + # This one can take a while, put it last + testcase_upslist_debug +} + +# If no args... +for USE_SHELL in $SHELL_PROGS ; do + case "$USE_SHELL" in + busybox|busybox_sh) USE_SHELL="busybox sh" ;; + esac + testsuite +done +# End of loop over shells + +echo "Test suite for nut-driver-enumerator has completed with $FAIL_COUNT failed cases and $GOOD_COUNT good cases" >&2 + +[ "$FAIL_COUNT" = 0 ] || { echo "As a developer, you may want to export DEBUG=trace or export DEBUG=yes and re-run the test; also make sure you meant the nut-driver-enumerator.sh implementation as NDE='$NDE'" >&2 ; exit 1; } diff --git a/tests/nutclienttest.cpp b/tests/nutclienttest.cpp new file mode 100644 index 0000000..774e8c6 --- /dev/null +++ b/tests/nutclienttest.cpp @@ -0,0 +1,525 @@ +/* nutclienttest - CppUnit nutclient unit test + + Copyright (C) 2016 Emilien Kia + Copyright (C) 2020 - 2021 Jim Klimov + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "common.h" + +/* Current CPPUnit offends the honor of C++98 */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS) +#pragma GCC diagnostic push +# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS +# pragma GCC diagnostic ignored "-Wglobal-constructors" +# endif +# ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS +# pragma GCC diagnostic ignored "-Wexit-time-destructors" +# endif +#endif + +#include + +namespace nut { + +class NutClientTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE( NutClientTest ); + CPPUNIT_TEST( test_strarr_alloc ); + CPPUNIT_TEST( test_stringset_to_strarr ); + CPPUNIT_TEST( test_stringvector_to_strarr ); + + CPPUNIT_TEST( test_copy_constructor_dev ); + CPPUNIT_TEST( test_copy_assignment_dev ); + + CPPUNIT_TEST( test_copy_constructor_cmd ); + CPPUNIT_TEST( test_copy_assignment_cmd ); + + CPPUNIT_TEST( test_copy_constructor_var ); + CPPUNIT_TEST( test_copy_assignment_var ); + + CPPUNIT_TEST( test_nutclientstub_dev ); + CPPUNIT_TEST_SUITE_END(); + +public: + void setUp() override; + void tearDown() override; + + void test_strarr_alloc(); + void test_stringset_to_strarr(); + void test_stringvector_to_strarr(); + + void test_copy_constructor_dev(); + void test_copy_assignment_dev(); + + void test_copy_constructor_cmd(); + void test_copy_assignment_cmd(); + + void test_copy_constructor_var(); + void test_copy_assignment_var(); + + void test_nutclientstub_dev(); +}; + +// Registers the fixture into the 'registry' +CPPUNIT_TEST_SUITE_REGISTRATION( NutClientTest ); + +} // namespace nut {} + +#ifndef _NUTCLIENTTEST_BUILD +# define _NUTCLIENTTEST_BUILD 1 +#endif + +#include "../clients/nutclient.h" +#include "../clients/nutclientmem.h" + +namespace nut { + +extern "C" { +strarr stringset_to_strarr(const std::set& strset); +strarr stringvector_to_strarr(const std::vector& strset); +} // extern "C" + +void NutClientTest::setUp() +{ +} + +void NutClientTest::tearDown() +{ +} + +void NutClientTest::test_strarr_alloc() +{ + bool noException = true; + + strarr arr = nullptr; + + try { + arr = strarr_alloc(5); + } + catch(nut::NutException& ex) + { + NUT_UNUSED_VARIABLE(ex); + noException = false; + } + CPPUNIT_ASSERT_MESSAGE( + "Failed strarr_alloc(...): throw exception", + noException); + + CPPUNIT_ASSERT_MESSAGE( + "Failed strarr_alloc(...): result is null", + arr != nullptr); + + strarr_free(arr); +} + +void NutClientTest::test_stringset_to_strarr() +{ + std::set strset; + strset.insert("test"); + strset.insert("hello"); + strset.insert("world"); + + strarr arr = stringset_to_strarr(strset); + CPPUNIT_ASSERT_MESSAGE( + "stringset_to_strarr(...) result is null", + arr != nullptr); + + std::set res; + + char** ptr = arr; + while(*ptr != nullptr) + { + res.insert(std::string(*ptr)); + ptr++; + } + + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "stringset_to_strarr(...) result has not 3 items", + static_cast(3), res.size()); + CPPUNIT_ASSERT_MESSAGE( + "stringset_to_strarr(...) result has not item \"test\"", + res.find("test") != res.end()); + CPPUNIT_ASSERT_MESSAGE( + "stringset_to_strarr(...) result has not item \"hello\"", + res.find("hello") != res.end()); + CPPUNIT_ASSERT_MESSAGE( + "stringset_to_strarr(...) result has not item \"world\"", + res.find("world") != res.end()); + + strarr_free(arr); +} + +void NutClientTest::test_stringvector_to_strarr() +{ + std::vector strset; + strset.push_back("test"); + strset.push_back("hello"); + strset.push_back("world"); + + strarr arr = stringvector_to_strarr(strset); + CPPUNIT_ASSERT_MESSAGE( + "stringvector_to_strarr(...) result is null", + arr != nullptr); + + char** ptr = arr; + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "stringvector_to_strarr(...) result has not item 0==\"test\"", + std::string("test"), std::string(*ptr)); + ++ptr; + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "stringvector_to_strarr(...) result has not item 1==\"hello\"", + std::string("hello"), std::string(*ptr)); + ++ptr; + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "stringvector_to_strarr(...) result has not item 2==\"world\"", + std::string("world"), std::string(*ptr)); + ++ptr; + + /* https://stackoverflow.com/a/12565009/4715872 + * Can not compare nullptr_t and another data type (char*) + * with CPPUNIT template assertEquals() + */ + CPPUNIT_ASSERT_MESSAGE( + "stringvector_to_strarr(...) result has not only 3 items", + nullptr == *ptr); + + strarr_free(arr); +} + +void NutClientTest::test_copy_constructor_dev() { + nut::TcpClient c; + nut::Device i(&c, "ups1"); + nut::Device j(i); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Failed to assign value of Device variable j by initializing from i", + i, j); +} + +void NutClientTest::test_copy_assignment_dev() { + nut::TcpClient c; + nut::Device i(&c, "ups1"); + nut::Device j(nullptr, "ups2"); + + CPPUNIT_ASSERT_ASSERTION_FAIL_MESSAGE( + "Device variables i and j were initialized differently " + "but claim to be equal", + CPPUNIT_ASSERT_EQUAL(i, j) ); + + j = i; + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Failed to assign value of Device Command j by equating to i", + i, j); +} + +void NutClientTest::test_copy_constructor_cmd() { + nut::TcpClient c; + nut::Device d(nullptr, "ups1"); + + nut::Command i(&d, "cmd1"); + nut::Command j(i); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Failed to assign value of Command variable j by initializing from i", + i, j); +} + +void NutClientTest::test_copy_assignment_cmd() { + nut::TcpClient c; + nut::Device d(nullptr, "ups1"); + + nut::Command i(&d, "var1"); + nut::Command j(nullptr, "var2"); + + CPPUNIT_ASSERT_ASSERTION_FAIL_MESSAGE( + "Command variables i and j were initialized differently " + "but claim to be equal", + CPPUNIT_ASSERT_EQUAL(i, j) ); + + j = i; + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Failed to assign value of Command variable j by equating to i", + i, j); +} + +void NutClientTest::test_copy_constructor_var() { + nut::TcpClient c; + nut::Device d(nullptr, "ups1"); + + nut::Variable i(&d, "var1"); + nut::Variable j(i); + + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Failed to assign value of Variable variable j by initializing from i", + i, j); +} + +void NutClientTest::test_copy_assignment_var() { + nut::TcpClient c; + nut::Device d(nullptr, "ups1"); + + nut::Variable i(&d, "var1"); + nut::Variable j(nullptr, "var2"); + + CPPUNIT_ASSERT_ASSERTION_FAIL_MESSAGE( + "Variable variables i and j were initialized differently " + "but claim to be equal", + CPPUNIT_ASSERT_EQUAL(i, j) ); + + j = i; + CPPUNIT_ASSERT_EQUAL_MESSAGE( + "Failed to assign value of Variable variable j by equating to i", + i, j); +} + +void NutClientTest::test_nutclientstub_dev() { + bool noException = true; + + nut::MemClientStub c; + nut::Device d(nullptr, "ups_1"); + try + { + // set mono value + c.setDeviceVariable("ups_1", "name_1", "value_1"); + // get mono value + ListValue values = c.getDeviceVariableValue("ups_1", "name_1"); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: mono wrong values number", + values.size() == 1); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: mono bad value", + values[0] == std::string("value_1")); + + // set multi value + ListValue values_multi = { "multi_1", "multi_2" }; + c.setDeviceVariable("ups_1", "name_multi_1", values_multi); + // get multi value + values = c.getDeviceVariableValue("ups_1", "name_multi_1"); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: multi wrong values number", + values.size() == 2); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: multi first bad value", + values[0] == std::string("multi_1")); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: multi second bad value", + values[1] == std::string("multi_2")); + + // get object values + ListObject objects = c.getDeviceVariableValues("ups_1"); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: objects wrong values number", + objects.size() == 2); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: objects mono wrong values number", + objects["name_1"].size() == 1); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: objects mono bad value", + objects["name_1"][0] == std::string("value_1")); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: objects multi wrong values number", + objects["name_multi_1"].size() == 2); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: objects mono bad value", + objects["name_multi_1"][0] == std::string("multi_1")); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: objects mono bad value", + objects["name_multi_1"][1] == std::string("multi_2")); + + // get device values + std::set devices_name = { "ups_1" }; + ListDevice devices = c.getDevicesVariableValues(devices_name); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: devices wrong values number", + devices.size() == 1); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: devices mono wrong values number", + devices["ups_1"]["name_1"].size() == 1); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: devices mono bad value", + devices["ups_1"]["name_1"][0] == std::string("value_1")); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: devices multi wrong values number", + devices["ups_1"]["name_multi_1"].size() == 2); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: devices mono bad value", + devices["ups_1"]["name_multi_1"][0] == std::string("multi_1")); + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: devices mono bad value", + devices["ups_1"]["name_multi_1"][1] == std::string("multi_2")); + } + catch(nut::NutException& ex) + { + NUT_UNUSED_VARIABLE(ex); + noException = false; + } + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: throw exception", + noException); + + // List of functions not implemented (should return exception) + noException = true; + try { + std::set cmd = c.getDeviceCommandNames("ups-1"); + CPPUNIT_ASSERT_MESSAGE( + "Variable not use", + cmd.size() == 0); + } + catch(nut::NutException& ex) + { + NUT_UNUSED_VARIABLE(ex); + noException = false; + } + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: throw no exception", + !noException); + + noException = true; + try { + std::string desc = c.getDeviceCommandDescription("ups-1", "cmd-1"); + CPPUNIT_ASSERT_MESSAGE( + "Variable not use", + desc.empty()); + } + catch(nut::NutException& ex) + { + NUT_UNUSED_VARIABLE(ex); + noException = false; + } + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: throw no exception", + !noException); + + noException = true; + try { + TrackingID id = c.executeDeviceCommand("ups-1", "cmd-1", "param-1"); + CPPUNIT_ASSERT_MESSAGE( + "Variable not use", + id.empty()); + } + catch(nut::NutException& ex) + { + NUT_UNUSED_VARIABLE(ex); + noException = false; + } + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: throw no exception", + !noException); + + noException = true; + try { + c.deviceLogin("ups-1"); + } + catch(nut::NutException& ex) + { + NUT_UNUSED_VARIABLE(ex); + noException = false; + } + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: throw no exception", + !noException); + + noException = true; + try { + c.deviceMaster("ups-1"); + } + catch(nut::NutException& ex) + { + NUT_UNUSED_VARIABLE(ex); + noException = false; + } + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: throw no exception", + !noException); + + noException = true; + try { + c.deviceForcedShutdown("ups-1"); + } + catch(nut::NutException& ex) + { + NUT_UNUSED_VARIABLE(ex); + noException = false; + } + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: throw no exception", + !noException); + + noException = true; + try { + c.deviceGetNumLogins("ups-1"); + } + catch(nut::NutException& ex) + { + NUT_UNUSED_VARIABLE(ex); + noException = false; + } + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: throw no exception", + !noException); + + noException = true; + try { + TrackingResult result = c.getTrackingResult("track-1"); + CPPUNIT_ASSERT_MESSAGE( + "Variable not use", + result == TrackingResult::SUCCESS); + } + catch(nut::NutException& ex) + { + NUT_UNUSED_VARIABLE(ex); + noException = false; + } + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: throw no exception", + !noException); + + noException = true; + try { + bool status = c.isFeatureEnabled(Feature("feature-1")); + CPPUNIT_ASSERT_MESSAGE( + "Variable not use", + !status); + } + catch(nut::NutException& ex) + { + NUT_UNUSED_VARIABLE(ex); + noException = false; + } + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: throw no exception", + !noException); + + noException = true; + try { + c.setFeature(Feature("feature-1"), true); + } + catch(nut::NutException& ex) + { + NUT_UNUSED_VARIABLE(ex); + noException = false; + } + CPPUNIT_ASSERT_MESSAGE( + "Failed stub tcp client: throw no exception", + !noException); +} + +} // namespace nut {} + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_EXIT_TIME_DESTRUCTORS || defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_GLOBAL_CONSTRUCTORS) +#pragma GCC diagnostic pop +#endif diff --git a/tests/nutlogtest.c b/tests/nutlogtest.c new file mode 100644 index 0000000..09342c7 --- /dev/null +++ b/tests/nutlogtest.c @@ -0,0 +1,65 @@ +/* nutlogtest - some trivial usage for upslog*() and upsdebug*() related + * routines to sanity-check their code (compiler does not warn, test runs + * do not crash). + * + * Copyright (C) + * 2020 Jim Klimov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "common.h" + +int main(void) { + const char *s1 = "!NULL"; + const char *s2 = NULL; + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_OVERFLOW) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat-overflow" +#endif + upsdebugx(0, "D: checking with libc handling of NULL: '%s' vs '%s'", s1, s2); + +/* This explicitly does not work with -Wformat, due to verbatim NULL without a var: + * nutlogtest.c:20:5: error: reading through null pointer (argument 4) [-Werror=format=] + * and also due to (void*) vs (char*) in naive case: + * upsdebugx(0, "D: '%s' vs '%s'", NUT_STRARG(NULL), NULL); + * but with casting the explicit NULL remains: + * upsdebugx(0, "D: '%s' vs '%s'", NUT_STRARG((char *)NULL), (char *)NULL); + */ + + upsdebugx(0, "D: checking with NUT_STRARG macro: '%s' vs '%s'", NUT_STRARG(s2), s2); + +#ifdef NUT_STRARG +#undef NUT_STRARG +#endif + +#define NUT_STRARG(x) (x?x:"") + +/* This explicitly does not work with -Wformat, due to a NULL in the '%s' + * format string expansion (e.g. due to NUT PR #675 conversion to macros): + * ../include/common.h:155:41: warning: '%s' directive argument is null [-Wformat-overflow=] + * <...snip...> + * nutlogtest.c:45:63: note: format string is defined here + * 45 | upsdebugx(0, "D: checking with NUT_STRARG macro: '%s' vs '%s'", NUT_STRARG(s2), s2); + * | ^~ + */ + upsdebugx(0, "D: checking that macro wrap trick works: '%s' vs '%s'", NUT_STRARG(s2), s2); + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_OVERFLOW) +# pragma GCC diagnostic pop +#endif + + return 0; +} diff --git a/tools/Makefile.am b/tools/Makefile.am index 0d9828b..9095195 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,80 +1,119 @@ -# TODO: remove redundancies! - -# Force build in ./ before nut-scanner, to have nutscan-{usb,snmp}.h -# built before going into the nut-scanner sub-directory +# SUBDIRS are explicitly a listing of all the directories that make +# must recurse into BEFORE processing the current directory. +# +# These python scripts must be moved into a sub-directory, and _only_ +# executed IF they need to be, and all the nut-scanner sources need +# to be moved out of a sub-directory into this directory. +# +# Anyway, for the time being, we force build in ./ before nut-scanner, +# to have nutscan-{usb,snmp}.h built before going into the nut-scanner +# sub-directory. For good measure we also call this from nut-scanner's +# make, to handle developer workflow (editing the *.c sources this uses). SUBDIRS = . nut-scanner -EXTRA_DIST = nut-usbinfo.pl nut-hclinfo.py device-recorder.sh svn2cl.authors nut-snmpinfo.py +PYTHON = @PYTHON@ -all: nut-scanner-deps +EXTRA_DIST = nut-usbinfo.pl nut-recorder.sh nut-ddl-dump.sh nut-dumpdiff.sh \ + gitlog2changelog.py.in nut-snmpinfo.py.in driver-list-format.sh -nut-scanner-deps: - @if python -c 1; then \ - echo "Regenerating the SNMP helper files."; \ - $(top_srcdir)/tools/nut-snmpinfo.py; \ +GENERATED_SNMP_FILES = nut-scanner/nutscan-snmp.h + +GENERATED_USB_FILES = nut-scanner/nutscan-usb.h + +# Hotplug output file +GENERATED_USB_OS_FILES = ../scripts/hotplug/libhid.usermap + +# udev output file +GENERATED_USB_OS_FILES += ../scripts/udev/nut-usbups.rules.in + +# BSD devd output file +GENERATED_USB_OS_FILES += ../scripts/devd/nut-usb.conf.in + +# UPower output file +GENERATED_USB_OS_FILES += ../scripts/upower/95-upower-hid.rules + +CLEANFILES = $(GENERATED_SNMP_FILES) $(GENERATED_USB_FILES) +# We do not clean away these files, some are even tracked in Git: +#CLEANFILES += $(GENERATED_USB_OS_FILES) + +all: nut-scanner-deps $(GENERATED_USB_OS_FILES) + +# This target is called from the making of nut-scanner to ensure its bits +nut-scanner-deps: $(GENERATED_SNMP_FILES) $(GENERATED_USB_FILES) + +# Aliases for particular files, if someone has a need: +nut-scanner-deps-snmpinfo: $(GENERATED_SNMP_FILES) +nut-scanner-deps-usb: $(GENERATED_USB_FILES) + +# The distributed nut-snmpinfo.py.in template is assumed to only differ from +# a generated nut-snmpinfo.py by the @PYTHON@ shebang. +$(GENERATED_SNMP_FILES): $(top_srcdir)/drivers/*-mib.c + @if [ -n "$(PYTHON)" ] && $(PYTHON) -c 1; then \ + echo "Regenerating the SNMP helper files in SRC dir with '$(PYTHON)'."; \ + TOP_SRCDIR="$(top_srcdir)" ; export TOP_SRCDIR; \ + TOP_BUILDDIR="$(top_builddir)" ; export TOP_BUILDDIR; \ + cd $(builddir) && $(PYTHON) $(top_srcdir)/tools/nut-snmpinfo.py.in; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Python is not available."; \ - echo "Skipping the SNMP helper files regeneration."; \ + echo "Skipping the SNMP helper files regeneration in SRC dir."; \ echo "----------------------------------------------------------------------"; \ fi +$(GENERATED_USB_FILES): $(top_srcdir)/drivers/*-hid.c $(top_srcdir)/drivers/*usb*.c $(top_srcdir)/drivers/nutdrv_qx.c @if perl -e 1; then \ - echo "Regenerating the USB helper files."; \ - $(top_srcdir)/tools/nut-usbinfo.pl; \ + echo "Regenerating the USB helper files in SRC dir."; \ + TOP_SRCDIR="$(top_srcdir)" ; export TOP_SRCDIR; \ + TOP_BUILDDIR="$(top_builddir)" ; export TOP_BUILDDIR; \ + cd $(builddir) && $(top_srcdir)/tools/nut-usbinfo.pl; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Perl is not available."; \ - echo "Skipping the USB helper files regeneration."; \ - echo "----------------------------------------------------------------------"; \ - fi - -website: - @if python -c "import json,simplejson,lxml"; then \ - echo "Regenerating the HTML and JSON formated HCL tables."; \ - $(top_srcdir)/tools/nut-hclinfo.py; \ - else \ - echo "----------------------------------------------------------------------"; \ - echo "Warning: either Python, or a required module (json, simplejson, lxml) "; \ - echo "is not available."; \ - echo "Skipping the HTML and JSON formated HCL tables regeneration."; \ + echo "Skipping the USB helper files regeneration in SRC dir."; \ echo "----------------------------------------------------------------------"; \ fi # call the USB info script upon "make dist", and if Perl is present # call the SNMP info script upon "make dist", and if Python is present # and call both for building nut-scanner -# also generate HCL data files +# Also ensure that data/driver.list is well formatted +# NOTE: Beware that current working directory for the script should be builddir +# so it may write the files in "dist" case (read-only sources), but the script +# is called from the distdir where its copy is present. +# The distributed nut-snmpinfo.py.in template is assumed to only differ from +# a generated nut-snmpinfo.py by the @PYTHON@ shebang. dist-hook: - @if python -c 1; then \ - echo "Regenerating the SNMP helper files."; \ - $(distdir)/nut-snmpinfo.py; \ + @if [ -n "$(PYTHON)" ] && $(PYTHON) -c 1; then \ + echo "Regenerating the SNMP helper files in DIST dir with '$(PYTHON)'."; \ + TOP_SRCDIR="$(top_srcdir)" ; export TOP_SRCDIR; \ + TOP_BUILDDIR="$(top_builddir)" ; export TOP_BUILDDIR; \ + $(PYTHON) $(distdir)/nut-snmpinfo.py.in; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Python is not available."; \ - echo "Skipping the SNMP helper files regeneration."; \ + echo "Skipping the SNMP helper files regeneration in DIST dir."; \ echo "----------------------------------------------------------------------"; \ fi @if perl -e 1; then \ - echo "Regenerating the USB helper files."; \ + echo "Regenerating the USB helper files in DIST dir."; \ + TOP_SRCDIR="$(top_srcdir)" ; export TOP_SRCDIR; \ + TOP_BUILDDIR="$(top_builddir)" ; export TOP_BUILDDIR; \ $(distdir)/nut-usbinfo.pl; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Perl is not available."; \ - echo "Skipping the USB helper files regeneration."; \ + echo "Skipping the USB helper files regeneration in DIST dir."; \ echo "----------------------------------------------------------------------"; \ fi - @if python -c "import json,simplejson,lxml"; then \ - echo "Regenerating the HTML and JSON formated HCL tables."; \ - $(distdir)/nut-hclinfo.py; \ - else \ - echo "----------------------------------------------------------------------"; \ - echo "Warning: either Python, or a required module (json, simplejson, lxml) "; \ - echo "is not available."; \ - echo "Skipping the HTML and JSON formated HCL tables regeneration."; \ - echo "----------------------------------------------------------------------"; \ - fi + @$(distdir)/driver-list-format.sh; + +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +# Can be recreated by `make` or `configure`, +# impacted by choice of PYTHON version: +DISTCLEANFILES = gitlog2changelog.py +DISTCLEANFILES += nut-snmpinfo.py .PHONY: nut-scanner-deps nut-scanner-snmp-deps nut-scanner-usb-deps diff --git a/tools/Makefile.in b/tools/Makefile.in index 66f35e8..51acd39 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -14,9 +13,62 @@ # PARTICULAR PURPOSE. @SET_MAKE@ - -# TODO: remove redundancies! VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -37,53 +89,104 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ subdir = tools -DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h -CONFIG_CLEAN_FILES = +CONFIG_CLEAN_FILES = gitlog2changelog.py nut-snmpinfo.py CONFIG_CLEAN_VPATH_FILES = +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = SOURCES = DIST_SOURCES = -RECURSIVE_TARGETS = all-recursive check-recursive dvi-recursive \ - html-recursive info-recursive install-data-recursive \ - install-dvi-recursive install-exec-recursive \ - install-html-recursive install-info-recursive \ - install-pdf-recursive install-ps-recursive install-recursive \ - installcheck-recursive installdirs-recursive pdf-recursive \ - ps-recursive uninstall-recursive +RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ + ctags-recursive dvi-recursive html-recursive info-recursive \ + install-data-recursive install-dvi-recursive \ + install-exec-recursive install-html-recursive \ + install-info-recursive install-pdf-recursive \ + install-ps-recursive install-recursive installcheck-recursive \ + installdirs-recursive pdf-recursive ps-recursive \ + tags-recursive uninstall-recursive +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \ distclean-recursive maintainer-clean-recursive -AM_RECURSIVE_TARGETS = $(RECURSIVE_TARGETS:-recursive=) \ - $(RECURSIVE_CLEAN_TARGETS:-recursive=) tags TAGS ctags CTAGS \ - distdir +am__recursive_targets = \ + $(RECURSIVE_TARGETS) \ + $(RECURSIVE_CLEAN_TARGETS) \ + $(am__extra_recursive_targets) +AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \ + distdir distdir-am +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags DIST_SUBDIRS = $(SUBDIRS) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(srcdir)/gitlog2changelog.py.in $(srcdir)/nut-snmpinfo.py.in DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) am__relativize = \ dir0=`pwd`; \ @@ -113,8 +216,11 @@ am__relativize = \ A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -125,16 +231,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -143,11 +258,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -157,14 +269,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -175,21 +288,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -203,35 +325,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -242,8 +375,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -262,18 +399,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -284,10 +424,41 @@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ -# Force build in ./ before nut-scanner, to have nutscan-{usb,snmp}.h -# built before going into the nut-scanner sub-directory +# SUBDIRS are explicitly a listing of all the directories that make +# must recurse into BEFORE processing the current directory. +# +# These python scripts must be moved into a sub-directory, and _only_ +# executed IF they need to be, and all the nut-scanner sources need +# to be moved out of a sub-directory into this directory. +# +# Anyway, for the time being, we force build in ./ before nut-scanner, +# to have nutscan-{usb,snmp}.h built before going into the nut-scanner +# sub-directory. For good measure we also call this from nut-scanner's +# make, to handle developer workflow (editing the *.c sources this uses). SUBDIRS = . nut-scanner -EXTRA_DIST = nut-usbinfo.pl nut-hclinfo.py device-recorder.sh svn2cl.authors nut-snmpinfo.py +EXTRA_DIST = nut-usbinfo.pl nut-recorder.sh nut-ddl-dump.sh nut-dumpdiff.sh \ + gitlog2changelog.py.in nut-snmpinfo.py.in driver-list-format.sh + +GENERATED_SNMP_FILES = nut-scanner/nutscan-snmp.h +GENERATED_USB_FILES = nut-scanner/nutscan-usb.h + +# Hotplug output file + +# udev output file + +# BSD devd output file + +# UPower output file +GENERATED_USB_OS_FILES = ../scripts/hotplug/libhid.usermap \ + ../scripts/udev/nut-usbups.rules.in \ + ../scripts/devd/nut-usb.conf.in \ + ../scripts/upower/95-upower-hid.rules +CLEANFILES = $(GENERATED_SNMP_FILES) $(GENERATED_USB_FILES) +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +# Can be recreated by `make` or `configure`, +# impacted by choice of PYTHON version: +DISTCLEANFILES = gitlog2changelog.py nut-snmpinfo.py all: all-recursive .SUFFIXES: @@ -303,14 +474,13 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tools/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tools/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -321,6 +491,10 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): +gitlog2changelog.py: $(top_builddir)/config.status $(srcdir)/gitlog2changelog.py.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ +nut-snmpinfo.py: $(top_builddir)/config.status $(srcdir)/nut-snmpinfo.py.in + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ mostlyclean-libtool: -rm -f *.lo @@ -329,22 +503,25 @@ clean-libtool: -rm -rf .libs _libs # This directory's subdirectories are mostly independent; you can cd -# into them and run `make' without going through this Makefile. -# To change the values of `make' variables: instead of editing Makefiles, -# (1) if the variable is set in `config.status', edit `config.status' -# (which will cause the Makefiles to be regenerated when you run `make'); -# (2) otherwise, pass the desired values on the `make' command line. -$(RECURSIVE_TARGETS): - @fail= failcom='exit 1'; \ - for f in x $$MAKEFLAGS; do \ - case $$f in \ - *=* | --[!k]*);; \ - *k*) failcom='fail=yes';; \ - esac; \ - done; \ +# into them and run 'make' without going through this Makefile. +# To change the values of 'make' variables: instead of editing Makefiles, +# (1) if the variable is set in 'config.status', edit 'config.status' +# (which will cause the Makefiles to be regenerated when you run 'make'); +# (2) otherwise, pass the desired values on the 'make' command line. +$(am__recursive_targets): + @fail=; \ + if $(am__make_keepgoing); then \ + failcom='fail=yes'; \ + else \ + failcom='exit 1'; \ + fi; \ dot_seen=no; \ target=`echo $@ | sed s/-recursive//`; \ - list='$(SUBDIRS)'; for subdir in $$list; do \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + for subdir in $$list; do \ echo "Making $$target in $$subdir"; \ if test "$$subdir" = "."; then \ dot_seen=yes; \ @@ -359,57 +536,12 @@ $(RECURSIVE_TARGETS): $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ fi; test -z "$$fail" -$(RECURSIVE_CLEAN_TARGETS): - @fail= failcom='exit 1'; \ - for f in x $$MAKEFLAGS; do \ - case $$f in \ - *=* | --[!k]*);; \ - *k*) failcom='fail=yes';; \ - esac; \ - done; \ - dot_seen=no; \ - case "$@" in \ - distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ - *) list='$(SUBDIRS)' ;; \ - esac; \ - rev=''; for subdir in $$list; do \ - if test "$$subdir" = "."; then :; else \ - rev="$$subdir $$rev"; \ - fi; \ - done; \ - rev="$$rev ."; \ - target=`echo $@ | sed s/-recursive//`; \ - for subdir in $$rev; do \ - echo "Making $$target in $$subdir"; \ - if test "$$subdir" = "."; then \ - local_target="$$target-am"; \ - else \ - local_target="$$target"; \ - fi; \ - ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ - || eval $$failcom; \ - done && test -z "$$fail" -tags-recursive: - list='$(SUBDIRS)'; for subdir in $$list; do \ - test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ - done -ctags-recursive: - list='$(SUBDIRS)'; for subdir in $$list; do \ - test "$$subdir" = . || ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) ctags); \ - done +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-recursive +TAGS: tags -ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ - mkid -fID $$unique -tags: TAGS - -TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \ @@ -425,12 +557,7 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \ fi; \ done; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ + $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ @@ -442,15 +569,11 @@ TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $$unique; \ fi; \ fi -ctags: CTAGS -CTAGS: ctags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ +ctags: ctags-recursive + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique @@ -459,11 +582,29 @@ GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-recursive + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -495,13 +636,10 @@ distdir: $(DISTFILES) done @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ if test "$$subdir" = .; then :; else \ - test -d "$(distdir)/$$subdir" \ - || $(MKDIR_P) "$(distdir)/$$subdir" \ - || exit 1; \ - fi; \ - done - @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \ - if test "$$subdir" = .; then :; else \ + $(am__make_dryrun) \ + || test -d "$(distdir)/$$subdir" \ + || $(MKDIR_P) "$(distdir)/$$subdir" \ + || exit 1; \ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \ $(am__relativize); \ new_distdir=$$reldir; \ @@ -539,21 +677,29 @@ install-am: all-am installcheck: installcheck-recursive install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -test -z "$(DISTCLEANFILES)" || rm -f $(DISTCLEANFILES) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-recursive clean-am: clean-generic clean-libtool mostlyclean-am @@ -620,12 +766,11 @@ ps-am: uninstall-am: -.MAKE: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) ctags-recursive \ - install-am install-strip tags-recursive +.MAKE: $(am__recursive_targets) install-am install-strip -.PHONY: $(RECURSIVE_CLEAN_TARGETS) $(RECURSIVE_TARGETS) CTAGS GTAGS \ - all all-am check check-am clean clean-generic clean-libtool \ - ctags ctags-recursive dist-hook distclean distclean-generic \ +.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am check \ + check-am clean clean-generic clean-libtool cscopelist-am ctags \ + ctags-am dist-hook distclean distclean-generic \ distclean-libtool distclean-tags distdir dvi dvi-am html \ html-am info info-am install install-am install-data \ install-data-am install-dvi install-dvi-am install-exec \ @@ -634,80 +779,86 @@ uninstall-am: install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs installdirs-am maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-generic \ - mostlyclean-libtool pdf pdf-am ps ps-am tags tags-recursive \ - uninstall uninstall-am + mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \ + uninstall-am +.PRECIOUS: Makefile -all: nut-scanner-deps +# We do not clean away these files, some are even tracked in Git: +#CLEANFILES += $(GENERATED_USB_OS_FILES) -nut-scanner-deps: - @if python -c 1; then \ - echo "Regenerating the SNMP helper files."; \ - $(top_srcdir)/tools/nut-snmpinfo.py; \ +all: nut-scanner-deps $(GENERATED_USB_OS_FILES) + +# This target is called from the making of nut-scanner to ensure its bits +nut-scanner-deps: $(GENERATED_SNMP_FILES) $(GENERATED_USB_FILES) + +# Aliases for particular files, if someone has a need: +nut-scanner-deps-snmpinfo: $(GENERATED_SNMP_FILES) +nut-scanner-deps-usb: $(GENERATED_USB_FILES) + +# The distributed nut-snmpinfo.py.in template is assumed to only differ from +# a generated nut-snmpinfo.py by the @PYTHON@ shebang. +$(GENERATED_SNMP_FILES): $(top_srcdir)/drivers/*-mib.c + @if [ -n "$(PYTHON)" ] && $(PYTHON) -c 1; then \ + echo "Regenerating the SNMP helper files in SRC dir with '$(PYTHON)'."; \ + TOP_SRCDIR="$(top_srcdir)" ; export TOP_SRCDIR; \ + TOP_BUILDDIR="$(top_builddir)" ; export TOP_BUILDDIR; \ + cd $(builddir) && $(PYTHON) $(top_srcdir)/tools/nut-snmpinfo.py.in; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Python is not available."; \ - echo "Skipping the SNMP helper files regeneration."; \ + echo "Skipping the SNMP helper files regeneration in SRC dir."; \ echo "----------------------------------------------------------------------"; \ fi +$(GENERATED_USB_FILES): $(top_srcdir)/drivers/*-hid.c $(top_srcdir)/drivers/*usb*.c $(top_srcdir)/drivers/nutdrv_qx.c @if perl -e 1; then \ - echo "Regenerating the USB helper files."; \ - $(top_srcdir)/tools/nut-usbinfo.pl; \ + echo "Regenerating the USB helper files in SRC dir."; \ + TOP_SRCDIR="$(top_srcdir)" ; export TOP_SRCDIR; \ + TOP_BUILDDIR="$(top_builddir)" ; export TOP_BUILDDIR; \ + cd $(builddir) && $(top_srcdir)/tools/nut-usbinfo.pl; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Perl is not available."; \ - echo "Skipping the USB helper files regeneration."; \ - echo "----------------------------------------------------------------------"; \ - fi - -website: - @if python -c "import json,simplejson,lxml"; then \ - echo "Regenerating the HTML and JSON formated HCL tables."; \ - $(top_srcdir)/tools/nut-hclinfo.py; \ - else \ - echo "----------------------------------------------------------------------"; \ - echo "Warning: either Python, or a required module (json, simplejson, lxml) "; \ - echo "is not available."; \ - echo "Skipping the HTML and JSON formated HCL tables regeneration."; \ + echo "Skipping the USB helper files regeneration in SRC dir."; \ echo "----------------------------------------------------------------------"; \ fi # call the USB info script upon "make dist", and if Perl is present # call the SNMP info script upon "make dist", and if Python is present # and call both for building nut-scanner -# also generate HCL data files +# Also ensure that data/driver.list is well formatted +# NOTE: Beware that current working directory for the script should be builddir +# so it may write the files in "dist" case (read-only sources), but the script +# is called from the distdir where its copy is present. +# The distributed nut-snmpinfo.py.in template is assumed to only differ from +# a generated nut-snmpinfo.py by the @PYTHON@ shebang. dist-hook: - @if python -c 1; then \ - echo "Regenerating the SNMP helper files."; \ - $(distdir)/nut-snmpinfo.py; \ + @if [ -n "$(PYTHON)" ] && $(PYTHON) -c 1; then \ + echo "Regenerating the SNMP helper files in DIST dir with '$(PYTHON)'."; \ + TOP_SRCDIR="$(top_srcdir)" ; export TOP_SRCDIR; \ + TOP_BUILDDIR="$(top_builddir)" ; export TOP_BUILDDIR; \ + $(PYTHON) $(distdir)/nut-snmpinfo.py.in; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Python is not available."; \ - echo "Skipping the SNMP helper files regeneration."; \ + echo "Skipping the SNMP helper files regeneration in DIST dir."; \ echo "----------------------------------------------------------------------"; \ fi @if perl -e 1; then \ - echo "Regenerating the USB helper files."; \ + echo "Regenerating the USB helper files in DIST dir."; \ + TOP_SRCDIR="$(top_srcdir)" ; export TOP_SRCDIR; \ + TOP_BUILDDIR="$(top_builddir)" ; export TOP_BUILDDIR; \ $(distdir)/nut-usbinfo.pl; \ else \ echo "----------------------------------------------------------------------"; \ echo "Warning: Perl is not available."; \ - echo "Skipping the USB helper files regeneration."; \ + echo "Skipping the USB helper files regeneration in DIST dir."; \ echo "----------------------------------------------------------------------"; \ fi - @if python -c "import json,simplejson,lxml"; then \ - echo "Regenerating the HTML and JSON formated HCL tables."; \ - $(distdir)/nut-hclinfo.py; \ - else \ - echo "----------------------------------------------------------------------"; \ - echo "Warning: either Python, or a required module (json, simplejson, lxml) "; \ - echo "is not available."; \ - echo "Skipping the HTML and JSON formated HCL tables regeneration."; \ - echo "----------------------------------------------------------------------"; \ - fi + @$(distdir)/driver-list-format.sh; .PHONY: nut-scanner-deps nut-scanner-snmp-deps nut-scanner-usb-deps diff --git a/tools/driver-list-format.sh b/tools/driver-list-format.sh new file mode 100755 index 0000000..cf9f291 --- /dev/null +++ b/tools/driver-list-format.sh @@ -0,0 +1,32 @@ +#!/bin/sh +################################################################################ +# +# Ensure that driver.list and driver.list.in are properly formatted (with tabs) +# +################################################################################ + +# Adapt path for either dist target or manual call +CURRENT_PATH="`dirname $0`" +DRVLIST_PATH="" + +if [ -f "${CURRENT_PATH}/data/driver.list.in" ]; then + DRVLIST_PATH="${CURRENT_PATH}" +elif [ -f "${CURRENT_PATH}/../data/driver.list.in" ]; then + DRVLIST_PATH="${CURRENT_PATH}/.." +else + echo "Can't find driver.list in . or .." + exit 1 +fi + +echo "Checking whether driver.list[.in] are well formatted" +for drvfile in driver.list.in driver.list +do + if [ -f "${DRVLIST_PATH}/data/${drvfile}" ]; then + sed -e '/^#/!s/\" \+\"/\"\t\"/g' -e "/^#/!s/[[:blank:]]*$//" < "${DRVLIST_PATH}/data/${drvfile}" > "${DRVLIST_PATH}/data/${drvfile}.tabbed" + mv -f "${DRVLIST_PATH}/data/${drvfile}.tabbed" "${DRVLIST_PATH}/data/${drvfile}" + echo "Processed ${DRVLIST_PATH}/data/${drvfile}" + else + echo "Skipping ${drvfile} as it is missing..." + fi +done +echo "done" diff --git a/tools/gitlog2changelog.py.in b/tools/gitlog2changelog.py.in new file mode 100755 index 0000000..5d882cb --- /dev/null +++ b/tools/gitlog2changelog.py.in @@ -0,0 +1,133 @@ +#!@PYTHON@ +# Copyright 2008 Marcus D. Hanwell +# Minor changes for NUT by Charles Lepple +# Distributed under the terms of the GNU General Public License v2 or later + +import string, re, os +from textwrap import TextWrapper +import sys + +rev_range = '' + +if len(sys.argv) > 1: + base = sys.argv[1] + rev_range = '%s..HEAD' % base + +# Execute git log with the desired command line options. +fin = os.popen('git log --summary --stat --no-merges --date=short %s' % rev_range, 'r') +# Create a ChangeLog file in the current directory. +fout = open('ChangeLog', 'w') + +# Set up the loop variables in order to locate the blocks we want +authorFound = False +dateFound = False +messageFound = False +filesFound = False +message = "" +messageNL = False +files = "" +prevAuthorLine = "" + +wrapper = TextWrapper(initial_indent="\t", subsequent_indent="\t ") + +# The main part of the loop +for line in fin: + # The commit line marks the start of a new commit object. + if line.startswith('commit'): + # Start all over again... + authorFound = False + dateFound = False + messageFound = False + messageNL = False + message = "" + filesFound = False + files = "" + continue + # Match the author line and extract the part we want + # (Don't use startswith to allow Author override inside commit message.) + elif 'Author:' in line: + authorList = re.split(': ', line, 1) + try: + author = authorList[1] + author = author[0:len(author)-1] + authorFound = True + except: + print ("Could not parse authorList = '%s'" % (line)) + + # Match the date line + elif line.startswith('Date:'): + dateList = re.split(': ', line, 1) + try: + date = dateList[1] + date = date[0:len(date)-1] + dateFound = True + except: + print ("Could not parse dateList = '%s'" % (line)) + # The Fossil-IDs are ignored: + elif line.startswith(' Fossil-ID:') or line.startswith(' [[SVN:'): + continue + # The svn-id lines are ignored + elif ' git-svn-id:' in line: + continue + # The sign off line is ignored too + elif 'Signed-off-by' in line: + continue + # Extract the actual commit message for this commit + elif authorFound & dateFound & messageFound == False: + # Find the commit message if we can + if len(line) == 1: + if messageNL: + messageFound = True + else: + messageNL = True + elif len(line) == 4: + messageFound = True + else: + if len(message) == 0: + message = message + line.strip() + else: + message = message + " " + line.strip() + # If this line is hit all of the files have been stored for this commit + elif re.search('files? changed', line): + filesFound = True + continue + # Collect the files for this commit. FIXME: Still need to add +/- to files + elif authorFound & dateFound & messageFound: + fileList = re.split(' \| ', line, 2) + if len(fileList) > 1: + if len(files) > 0: + files = files + ", " + fileList[0].strip() + else: + files = fileList[0].strip() + # All of the parts of the commit have been found - write out the entry + if authorFound & dateFound & messageFound & filesFound: + # First the author line, only outputted if it is the first for that + # author on this day + authorLine = date + " " + author + if len(prevAuthorLine) == 0: + fout.write(authorLine + "\n\n") + elif authorLine == prevAuthorLine: + pass + else: + fout.write("\n" + authorLine + "\n\n") + + # Assemble the actual commit message line(s) and limit the line length + # to 80 characters. + commitLine = "* " + files + ": " + message + + # Write out the commit line + fout.write(wrapper.fill(commitLine) + "\n") + + #Now reset all the variables ready for a new commit block. + authorFound = False + dateFound = False + messageFound = False + messageNL = False + message = "" + filesFound = False + files = "" + prevAuthorLine = authorLine + +# Close the input and output lines now that we are finished. +fin.close() +fout.close() diff --git a/tools/nut-ddl-dump.sh b/tools/nut-ddl-dump.sh new file mode 100755 index 0000000..326a06d --- /dev/null +++ b/tools/nut-ddl-dump.sh @@ -0,0 +1,65 @@ +#!/bin/sh +################################################################################ +# A script to ease the generation of NUT device dumps for NUT Devices Dumps Library +################################################################################ +# Author: (C) Arnaud Quette +# License: GPL v2+ +################################################################################ +# FIXME: +# - check if a previous report exists, and increase report number +#  - we currently use the .dev format ; but also consider the NDS format +# http://www.networkupstools.org/ddl/ +################################################################################ + +strUsage="Usage: $0 " + +# Check command line parameter () +if [ -z "$1" ]; then + echo "$strUsage" + exit +else + DDL_DEVICE_NAME=$1 +fi + +# Test communication with the device +upscResult="`upsc ${DDL_DEVICE_NAME} 2> /dev/null`" +if [ $? -gt 0 ]; then + echo "Can't communicate with ${DDL_DEVICE_NAME}" + exit +fi + +# Collect more information +dumpDate="`date -R`" +upsrwResult="`upsrw ${DDL_DEVICE_NAME} 2> /dev/null`" +upscmdResult="`upscmd -l ${DDL_DEVICE_NAME} 2> /dev/null`" + +# Build the filename +# ________. +# Process the Manufacturer name +RAW_DDL_MFR="`upsc ${DDL_DEVICE_NAME} device.mfr 2>/dev/null`" +if [ "${RAW_DDL_MFR}" = "EATON" ]; then + RAW_DDL_MFR="Eaton" +fi +# Replace spaces with underscores +DDL_MFR="`echo ${RAW_DDL_MFR} | sed s/\ /_/g`" +# Process the Model name +# Replace spaces with underscores +RAW_DDL_MODEL="`upsc ${DDL_DEVICE_NAME} device.model 2>/dev/null`" +DDL_MODEL="`echo ${RAW_DDL_MODEL} | sed s/\ /_/g`" +# Process the driver name and NUT version +DDL_DRIVER_NAME="`upsc ${DDL_DEVICE_NAME} driver.name 2>/dev/null`" +DDL_NUT_VERSION="`upsc ${DDL_DEVICE_NAME} driver.version 2>/dev/null`" +# TODO: check if a similar file exists, to update Report nb +DDL_REPORT_NUMBER="01" +DDL_FILENAME="${DDL_MFR}__${DDL_MODEL}__${DDL_DRIVER_NAME}__${DDL_NUT_VERSION}__${DDL_REPORT_NUMBER}.dev" + +# Dump device data into the file +echo "# Device dump generated by $0 on $dumpDate" > ${DDL_FILENAME} +echo "# upsrw output:" >> ${DDL_FILENAME} +echo "${upsrwResult}" | sed -e 's/^/# /' >> ${DDL_FILENAME} +echo "# upscmd output:" >> ${DDL_FILENAME} +echo "${upscmdResult}" | sed -e 's/^/# /' >> ${DDL_FILENAME} +echo "" >> ${DDL_FILENAME} +echo "# upsc output:" >> ${DDL_FILENAME} +echo "${upscResult}" >> ${DDL_FILENAME} +echo "${DDL_FILENAME} generated using ${DDL_DEVICE_NAME} " diff --git a/tools/nut-dumpdiff.sh b/tools/nut-dumpdiff.sh new file mode 100755 index 0000000..0dcb470 --- /dev/null +++ b/tools/nut-dumpdiff.sh @@ -0,0 +1,35 @@ +#!/usr/bin/env bash + +# This script is intended to simplify comparison of NUT data dumps, +# such as those collected by drivers with `-d 1` argument, or by +# the `upsc` client, ignoring irrelevant variations (e.g. numbers). +# +# TODO: Make more portable than bash and GNU toolkits +# +# Subject to same license as the NUT Project. +# +# Copyright (C) +# 2022 Jim Klimov +# + +if [ $# = 2 ] && [ -s "$1" ] && [ -s "$2" ]; then + echo "=== $0: comparing '$1' (-) vs '$2' (+)" >&2 +else + echo "=== $0: aborting: requires two filenames to compare as arguments" >&2 + exit 1 +fi + +# Pre-sort just in case: +DATA1="`sort -n < "$1"`" +DATA2="`sort -n < "$2"`" + +# Strip away same-context lines, +# and lines with measurements that are either decimal numbers +# or multi-digit numbers without a decimal point (assuming +# differences in shorter numbers or counters may be important) +diff -bu <(echo "$DATA1") <(echo "$DATA2") \ +| grep -E '^[+-][^+-]' \ +| grep -vE '^[^:]*(power|load|voltage|current|frequency|temperature|humidity): ([0-9][0-9]*|[0-9][0-9]*\.[0-9][0-9]*)$' + +# Note: up to user to post-filter, "^driver.version.*:" +# may be deemed irrelevant as well diff --git a/tools/nut-hclinfo.py b/tools/nut-hclinfo.py deleted file mode 100755 index 8a7aa4e..0000000 --- a/tools/nut-hclinfo.py +++ /dev/null @@ -1,277 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -# -# Copyright (c) 2009 - Arnaud Quette -# Copyright (c) 2010 - Sébastien Volle -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -# This script convert the driver.list into HTML and JSON formated tables -# These tables are then used by the AsciiDoc generated website and -# documentation - -try: - import json -except ImportError: - import simplejson as json # Required for Python < 2.6 - -import re -import sys -import os, errno - -# HCL file location and name -rawHCL="../data/driver.list"; - -# Website output -webJsonHCL = "../docs/website/scripts/ups_data.js"; -webStaticHCL = "../docs/ups-html.txt"; - -# from http://wiki.python.org/moin/EscapingHtml - -html_escape_table = { - "&": "&", - '"': """, - "'": "'", - ">": ">", - "<": "<", - } - -def html_escape(text): - """Produce entities within text.""" - return "".join(html_escape_table.get(c,c) for c in text) - -# # # - -class WrongFieldNumberException(Exception): - pass - -def buildData(deviceDataFile): - """ - Read and parse data file under provided path. - Return a bi-dimensional list representing parsed data. - """ - - deviceData = [] - numFields = 6 # Manufacturer, type, support level, model comment, driver - - try: - file = open(deviceDataFile, "r") - except IOError: - print "Cannot open", deviceDataFile - exit(1) - - for line in file: - # Ignore empty lines or comments - if re.match(r"^$|^\s*#", line): - continue - - # Strip all trailing whitespace chars - line = re.sub(r"\s+$", "", line) - - # Replace all tabs by commas - line = re.sub(r"\t", ",", line) - - # Remove trailing comma - line = re.sub(r",$", "", line) - - # Split fields and append result to device data list - # We suppose there are no double-quotes in fields - row = re.findall(r'"([^"]*)",?', line) - - if len(row) != numFields: - print "Warning: Unexpected number of fields in line: %s" % row - print "\tLine will be skipped." - else: - deviceData.append(re.findall(r'"([^"]*)",?', line)) - - return deviceData - -def buildHTMLTable(deviceData): - """ - Convert provided device data into an HTML table. - Return string representation of the HTML table. - - Identical cells are merged vertically with rowspan attribute. - The driver column is color-coded on support level. - - A support level column is also provided. It should be hidden in a graphic - browser but should be visible from a console based browser (w3m). - """ - - from lxml import etree, html - from lxml.builder import E - - if not type(deviceData).__name__ == "list" or len(deviceData) == 0: - raise Exception("Incorrect data was provided") - - # HTML table columns definition - columns = [ - { - "name": "manufacturer", "id": "manufacturer-col", - "text": "Manufacturer", "fields": ["manufacturer"] - }, - { - "name": "model", "id": "model-col", - "text": "Model", "fields": ["model", "comment"] - }, - { - "name": "driver", "id": "driver-col", - "text": "Driver", "fields": ["driver"] - }, - { - "name": "support-level", "id": "support-level-col", - "text": "Support Level", "fields": ["support-level"] - }, - ] - # Device data fields definition - dataFields = [ - "manufacturer", "device-type", "support-level", - "model", "comment", "driver" - ] - - # FIXME: CSS classes should be defined in script global settings - supportLevelClasses = { - "0": "", "1": "red", "2": "orange", - "3": "yellow", "4": "blue", "5": "green" - } - hiddenClass = "hidden" - - # Build table header - table = E.table(id="ups_list", border="1") - header = E.tr() - - for column in columns: - td = E.td(column.get("text"), id=column.get("id")) - if column["id"] == "support-level-col": - td.set("class", hiddenClass) - header.append(td) - - table.append(E.thead(header)) - - # Build table body - tbody = E.tbody(id="ups_list_body") - - cellHistory = [] - rowHistory = deviceData[0][0] - rows = [] - classes = ("even", "odd") - currentClass = 0 - manufIndex = dataFields.index("manufacturer") - - # Build table rows - for device in deviceData: - - # Devices are expected to have a specified number of fields - if len(device) < len(dataFields): - print "Unexpected number of fields in device: %s" % device - print "Device will not be included in result set." - continue - - # Alternate CSS class if current manufacturer is different from the last - if device[manufIndex] != rowHistory : - currentClass = (currentClass + 1) % 2 - rowHistory = device[manufIndex] - - cells = [] - - colIndex = 0 - for column in columns: - cellContent = [] - for field in column["fields"]: - fieldIndex = dataFields.index(field) - fieldContent = device[fieldIndex] - cellContent.append(html_escape(fieldContent)) - cellContent = "
".join(cellContent) - - try: - cH = cellHistory[colIndex] - except: - cH = False - - if cH and cH.get("text") == cellContent: - cH["rowspan"] = cH.get("rowspan", 1) + 1 - else: - cell = { "text": cellContent, "rowspan": 1 } - if column["name"] == "driver": - cell["class"] = supportLevelClasses[device[dataFields.index("support-level")]] - else: - cell["class"] = classes[currentClass] - if column["name"] == "support-level": - cell["class"] = hiddenClass - - cells.append(cell) - try: - cellHistory[colIndex] = cell - except: - cellHistory.append(cell) - - colIndex += 1 - - rows.append(cells) - - for row in rows: - r = E.tr() - for cell in row: - attr = "" - innerHTML = "" - for key, value in cell.iteritems(): - val = unicode(str(value), "utf-8") - if key != "text": - attr += " %s='%s'" % (key, val) - else: - innerHTML = val - - r.append(html.fromstring("%s" % (attr, innerHTML))) - - tbody.append(r) - - table.append(tbody) - - return etree.tostring(table, pretty_print=True) - -# main program -deviceData = buildData(rawHCL) - -# Dump device data as JSON -jsonData = "var UPSData = %s" % json.dumps(deviceData, encoding="utf-8") - -# First, check if target directory exists (which is not the case for 'dist') -dir = os.path.dirname(webJsonHCL) -try: - os.makedirs(dir) -except OSError: - pass - -try: - file = open(webJsonHCL, "w") - file.write(jsonData) - file.close() - print "JSON HCL written" -except IOError: - print "Unable to write JSON device data to %s" % webJsonHCL - exit(1) - -# Create HTML table from device data -table = buildHTMLTable(deviceData) -try: - file = open(webStaticHCL, "w") - file.write("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n") - file.write(table) - file.write("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n") - print "HTML HCL written" -except IOError: - print "Unable to write HTML device table to %s" % webStaticHCL - exit(1) - diff --git a/tools/device-recorder.sh b/tools/nut-recorder.sh similarity index 78% rename from tools/device-recorder.sh rename to tools/nut-recorder.sh index 5b55a99..2329c2d 100755 --- a/tools/device-recorder.sh +++ b/tools/nut-recorder.sh @@ -1,16 +1,20 @@ #!/bin/sh ################################################################################ # -# device-recorder.sh -# A script to record device running sequence and dump it in a .seq format -# The .seq file can then be used by dummy-ups to replay the sequence. +# nut-recorder +# An utility to record device running sequence (Ie power failures or any +# other change) and dump it in a .seq format +# The .seq file can then be used by the 'dummy-ups driver to replay the +# sequence. # ################################################################################ # FIXME: -# - implement PAUSE / RESUME (do not increment TIMER) on pressing space +# - implement PAUSE / RESUME (do not increment TIMER) on pressing space (?) +# - implement support for creating either .dev (static dump) or .seq +# - implement dump of instcmd and upsrw ################################################################################ -strUsage="Usage: dummy-recorder.sh [output-file] [interval]" +strUsage="Usage: nut-recorder [output-file] [interval]" # log data each 5 seconds DEFAULT_INTERVAL=5 @@ -18,7 +22,7 @@ DEFAULT_INTERVAL=5 # temporary files location TEMP_DIR="/tmp" -# output to dummy-device.dev by default +# output this file by default DEFAULT_OUTPUT="dummy-device.seq" # Process command line parameters @@ -67,7 +71,7 @@ do sleep $pollInterval # update the TIMER value - curTimer=`expr $curTimer + $pollInterval` + curTimer=`expr $curTimer + $pollInterval` # dump the current data testResult="`upsc $devName > ${TEMP_DIR}/curDump.tmp`" diff --git a/tools/nut-scanner/Makefile.am b/tools/nut-scanner/Makefile.am index 0656e49..667b40b 100644 --- a/tools/nut-scanner/Makefile.am +++ b/tools/nut-scanner/Makefile.am @@ -1,7 +1,47 @@ -BUILT_SOURCES = nutscan-usb.h nutscan-snmp.h +# Generally, list headers and/or sources which are re-generated +# for nut-scanner in the parent dir +NUT_SCANNER_DEPS_H = nutscan-usb.h nutscan-snmp.h +NUT_SCANNER_DEPS_C = -nutscan-usb.h nutscan-snmp.h: - cd ..; $(MAKE) $(AM_MAKEFLAGS) nut-scanner-deps +# General set of nut-scanner dependencies generated in the parent dir +NUT_SCANNER_DEPS = $(NUT_SCANNER_DEPS_H) $(NUT_SCANNER_DEPS_C) + +BUILT_SOURCES = $(NUT_SCANNER_DEPS) +CLEANFILES = $(BUILT_SOURCES) + +# Make sure we have the freshest files (no-op if built earlier and then +# no driver sources and other dependencies were edited by a developer) +$(NUT_SCANNER_DEPS): dummy + @cd .. && $(MAKE) $(AM_MAKEFLAGS) nut-scanner-deps + +# Make sure out-of-dir dependencies exist (especially when dev-building parts): +$(top_builddir)/common/libcommon.la: dummy + @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +# do not hard depend on '../include/nut_version.h', since it blocks +# 'dist', and is only required for actual build, in which case +# BUILT_SOURCES (in ../include) will ensure nut_version.h will +# be built before anything else +nut-scanner.c: $(top_builddir)/include/nut_version.h + +LINKED_SOURCE_FILES = + +# Separate the .deps of other dirs from this one +# NOTE: Not using "$<" due to a legacy Sun/illumos dmake bug with resolver +# of dynamic vars, see e.g. https://man.omnios.org/man1/make#BUGS +LINKED_SOURCE_FILES += serial.c +serial.c: $(top_srcdir)/drivers/serial.c + test -s "$@" || ln -s -f "$(top_srcdir)/drivers/serial.c" "$@" + +LINKED_SOURCE_FILES += bcmxcp_ser.c +bcmxcp_ser.c: $(top_srcdir)/drivers/bcmxcp_ser.c + test -s "$@" || ln -s -f "$(top_srcdir)/drivers/bcmxcp_ser.c" "$@" + +CLEANFILES += $(LINKED_SOURCE_FILES) +BUILT_SOURCES += $(LINKED_SOURCE_FILES) + +$(top_builddir)/include/nut_version.h: + @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) # Only build nut-scanner, and its library, if libltdl was found (required!) if WITH_LIBLTDL @@ -9,11 +49,36 @@ if WITH_LIBLTDL lib_LTLIBRARIES = libnutscan.la endif libnutscan_la_SOURCES = scan_nut.c scan_ipmi.c \ - nutscan-device.c nutscan-ip.c nutscan-display.c nutscan-init.c \ - scan_usb.c scan_snmp.c scan_xml_http.c scan_avahi.c -libnutscan_la_LIBADD = ../../clients/libupsclient.la $(NETLIBS) $(LIBLTDL_LIBS) -libnutscan_la_LDFLAGS = -version-info 1:0:0 -libnutscan_la_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include $(LIBLTDL_CFLAGS) + nutscan-device.c nutscan-ip.c nutscan-display.c \ + nutscan-init.c scan_usb.c scan_snmp.c scan_xml_http.c \ + scan_avahi.c scan_eaton_serial.c nutscan-serial.c +nodist_libnutscan_la_SOURCES = $(LINKED_SOURCE_FILES) +libnutscan_la_LIBADD = $(NETLIBS) +if WITH_LIBLTDL +libnutscan_la_LIBADD += $(LIBLTDL_LIBS) +endif +libnutscan_la_LIBADD += $(top_builddir)/common/libcommonclient.la +# +# Below we set API versions of public libraries +# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +# Note that changes here may have to be reflected in packaging (the shared +# object .so names would differ) +# +# libnutscan version information +libnutscan_la_LDFLAGS = $(SERLIBS) -version-info 2:0:0 + +# libnutscan exported symbols regex +# WARNING: Since the library includes parts of libcommon (as much as needed +# e.g. for logging or string manipulations), we export a few symbols from +# those too. This may cause issues for (third-party) code development that +# would use both libnutscan and libcommon or similar libs. Here we did have +# a problem with nut-scanner using two copies of common.c and conflicting +# copies of "nut_debug_level" making fun of our debug-logging attempts. +# One solution to tackle if needed for those cases would be to make some +# dynamic/shared libnutcommon (etc.) +libnutscan_la_LDFLAGS += -export-symbols-regex '^(nutscan_|nut_debug_level|s_upsdebugx|max_threads|curr_threads)' +libnutscan_la_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include \ + $(LIBLTDL_CFLAGS) -I$(top_srcdir)/drivers nut_scanner_SOURCES = nut-scanner.c nut_scanner_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include @@ -39,13 +104,21 @@ if WITH_IPMI libnutscan_la_CFLAGS += $(LIBIPMI_CFLAGS) endif -dist_noinst_HEADERS = nutscan-usb.h nutscan-snmp.h +# C is not a header, but there is no dist_noinst_SOURCES +dist_noinst_HEADERS = $(NUT_SCANNER_DEPS_H) $(NUT_SCANNER_DEPS_C) if WITH_DEV - include_HEADERS = nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h + include_HEADERS = nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h nutscan-serial.h else - dist_noinst_HEADERS += nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h + dist_noinst_HEADERS += nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h nutscan-serial.h endif -CLEANFILES = nutscan-usb.h nutscan-snmp.h +dummy: +CLEANFILES += *-spellchecked +MAINTAINERCLEANFILES = Makefile.in .dirstamp + +# NOTE: Do not clean ".deps" in SUBDIRS of the main project, +# the root Makefile.am takes care of that! +#clean-local: +# rm -rf $(builddir)/.deps diff --git a/tools/nut-scanner/Makefile.in b/tools/nut-scanner/Makefile.in index cf53410..068ee30 100644 --- a/tools/nut-scanner/Makefile.in +++ b/tools/nut-scanner/Makefile.in @@ -1,9 +1,8 @@ -# Makefile.in generated by automake 1.11.1 from Makefile.am. +# Makefile.in generated by automake 1.16.3 from Makefile.am. # @configure_input@ -# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, -# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, -# Inc. +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + # This Makefile.in is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -18,6 +17,61 @@ VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) pkgdatadir = $(datadir)/@PACKAGE@ pkgincludedir = $(includedir)/@PACKAGE@ pkglibdir = $(libdir)/@PACKAGE@ @@ -38,47 +92,61 @@ build_triplet = @build@ host_triplet = @host@ target_triplet = @target@ @WITH_LIBLTDL_TRUE@bin_PROGRAMS = nut-scanner$(EXEEXT) -@WITH_SSL_TRUE@am__append_1 = $(LIBSSL_CFLAGS) -@WITH_SSL_TRUE@am__append_2 = $(LIBSSL_LIBS) -@WITH_USB_TRUE@am__append_3 = $(LIBUSB_CFLAGS) -@WITH_SNMP_TRUE@am__append_4 = $(LIBNETSNMP_CFLAGS) -@WITH_NEON_TRUE@am__append_5 = $(LIBNEON_CFLAGS) -@WITH_AVAHI_TRUE@am__append_6 = $(LIBAVAHI_CFLAGS) -@WITH_IPMI_TRUE@am__append_7 = $(LIBIPMI_CFLAGS) -@WITH_DEV_FALSE@am__append_8 = nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h +@WITH_LIBLTDL_TRUE@am__append_1 = $(LIBLTDL_LIBS) +@WITH_SSL_TRUE@am__append_2 = $(LIBSSL_CFLAGS) +@WITH_SSL_TRUE@am__append_3 = $(LIBSSL_LIBS) +@WITH_USB_TRUE@am__append_4 = $(LIBUSB_CFLAGS) +@WITH_SNMP_TRUE@am__append_5 = $(LIBNETSNMP_CFLAGS) +@WITH_NEON_TRUE@am__append_6 = $(LIBNEON_CFLAGS) +@WITH_AVAHI_TRUE@am__append_7 = $(LIBAVAHI_CFLAGS) +@WITH_IPMI_TRUE@am__append_8 = $(LIBIPMI_CFLAGS) +@WITH_DEV_FALSE@am__append_9 = nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h nutscan-serial.h subdir = tools/nut-scanner -DIST_COMMON = README $(am__dist_noinst_HEADERS_DIST) \ - $(am__include_HEADERS_DIST) $(srcdir)/Makefile.am \ - $(srcdir)/Makefile.in ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 -am__aclocal_m4_deps = $(top_srcdir)/m4/ax_compare_version.m4 \ +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_c___attribute__.m4 \ + $(top_srcdir)/m4/ax_c_pragmas.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_compare_version.m4 \ + $(top_srcdir)/m4/ax_run_or_link_ifelse.m4 \ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \ $(top_srcdir)/m4/lt~obsolete.m4 \ $(top_srcdir)/m4/nut_arg_with.m4 \ $(top_srcdir)/m4/nut_check_asciidoc.m4 \ + $(top_srcdir)/m4/nut_check_cppcheck.m4 \ + $(top_srcdir)/m4/nut_check_headers_windows.m4 \ $(top_srcdir)/m4/nut_check_libavahi.m4 \ $(top_srcdir)/m4/nut_check_libfreeipmi.m4 \ $(top_srcdir)/m4/nut_check_libgd.m4 \ - $(top_srcdir)/m4/nut_check_libhal.m4 \ $(top_srcdir)/m4/nut_check_libltdl.m4 \ + $(top_srcdir)/m4/nut_check_libmodbus.m4 \ $(top_srcdir)/m4/nut_check_libneon.m4 \ $(top_srcdir)/m4/nut_check_libnetsnmp.m4 \ + $(top_srcdir)/m4/nut_check_libnss.m4 \ + $(top_srcdir)/m4/nut_check_libopenssl.m4 \ $(top_srcdir)/m4/nut_check_libpowerman.m4 \ - $(top_srcdir)/m4/nut_check_libssl.m4 \ $(top_srcdir)/m4/nut_check_libusb.m4 \ $(top_srcdir)/m4/nut_check_libwrap.m4 \ $(top_srcdir)/m4/nut_check_os.m4 \ - $(top_srcdir)/m4/nut_config_libhal.m4 \ + $(top_srcdir)/m4/nut_check_pkgconfig.m4 \ + $(top_srcdir)/m4/nut_check_python.m4 \ + $(top_srcdir)/m4/nut_compiler_family.m4 \ + $(top_srcdir)/m4/nut_func_getnameinfo_argtypes.m4 \ $(top_srcdir)/m4/nut_report_feature.m4 \ + $(top_srcdir)/m4/nut_stash_warnings.m4 \ $(top_srcdir)/m4/nut_type_socklen_t.m4 \ - $(top_srcdir)/configure.in + $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(am__dist_noinst_HEADERS_DIST) \ + $(am__include_HEADERS_DIST) $(am__DIST_COMMON) mkinstalldirs = $(install_sh) -d CONFIG_HEADER = $(top_builddir)/include/config.h CONFIG_CLEAN_FILES = CONFIG_CLEAN_VPATH_FILES = +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \ + "$(DESTDIR)$(includedir)" +PROGRAMS = $(bin_PROGRAMS) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; am__vpath_adj = case $$p in \ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ @@ -100,60 +168,136 @@ am__nobase_list = $(am__nobase_strip_setup); \ am__base_list = \ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' -am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ - "$(DESTDIR)$(includedir)" +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } LTLIBRARIES = $(lib_LTLIBRARIES) am__DEPENDENCIES_1 = -@WITH_SSL_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) -libnutscan_la_DEPENDENCIES = ../../clients/libupsclient.la \ - $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ - $(am__DEPENDENCIES_2) +@WITH_LIBLTDL_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) +@WITH_SSL_TRUE@am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) +libnutscan_la_DEPENDENCIES = $(am__DEPENDENCIES_1) \ + $(am__DEPENDENCIES_2) \ + $(top_builddir)/common/libcommonclient.la \ + $(am__DEPENDENCIES_3) am_libnutscan_la_OBJECTS = libnutscan_la-scan_nut.lo \ libnutscan_la-scan_ipmi.lo libnutscan_la-nutscan-device.lo \ libnutscan_la-nutscan-ip.lo libnutscan_la-nutscan-display.lo \ libnutscan_la-nutscan-init.lo libnutscan_la-scan_usb.lo \ libnutscan_la-scan_snmp.lo libnutscan_la-scan_xml_http.lo \ - libnutscan_la-scan_avahi.lo -libnutscan_la_OBJECTS = $(am_libnutscan_la_OBJECTS) -libnutscan_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ + libnutscan_la-scan_avahi.lo libnutscan_la-scan_eaton_serial.lo \ + libnutscan_la-nutscan-serial.lo +am__objects_1 = libnutscan_la-serial.lo libnutscan_la-bcmxcp_ser.lo +nodist_libnutscan_la_OBJECTS = $(am__objects_1) +libnutscan_la_OBJECTS = $(am_libnutscan_la_OBJECTS) \ + $(nodist_libnutscan_la_OBJECTS) +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +libnutscan_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(libnutscan_la_CFLAGS) \ $(CFLAGS) $(libnutscan_la_LDFLAGS) $(LDFLAGS) -o $@ @WITH_LIBLTDL_TRUE@am_libnutscan_la_rpath = -rpath $(libdir) -PROGRAMS = $(bin_PROGRAMS) am_nut_scanner_OBJECTS = nut_scanner-nut-scanner.$(OBJEXT) nut_scanner_OBJECTS = $(am_nut_scanner_OBJECTS) nut_scanner_DEPENDENCIES = libnutscan.la -nut_scanner_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ +nut_scanner_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(nut_scanner_CFLAGS) \ $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/include depcomp = $(SHELL) $(top_srcdir)/depcomp -am__depfiles_maybe = depfiles +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = ./$(DEPDIR)/libnutscan_la-bcmxcp_ser.Plo \ + ./$(DEPDIR)/libnutscan_la-nutscan-device.Plo \ + ./$(DEPDIR)/libnutscan_la-nutscan-display.Plo \ + ./$(DEPDIR)/libnutscan_la-nutscan-init.Plo \ + ./$(DEPDIR)/libnutscan_la-nutscan-ip.Plo \ + ./$(DEPDIR)/libnutscan_la-nutscan-serial.Plo \ + ./$(DEPDIR)/libnutscan_la-scan_avahi.Plo \ + ./$(DEPDIR)/libnutscan_la-scan_eaton_serial.Plo \ + ./$(DEPDIR)/libnutscan_la-scan_ipmi.Plo \ + ./$(DEPDIR)/libnutscan_la-scan_nut.Plo \ + ./$(DEPDIR)/libnutscan_la-scan_snmp.Plo \ + ./$(DEPDIR)/libnutscan_la-scan_usb.Plo \ + ./$(DEPDIR)/libnutscan_la-scan_xml_http.Plo \ + ./$(DEPDIR)/libnutscan_la-serial.Plo \ + ./$(DEPDIR)/nut_scanner-nut-scanner.Po am__mv = mv -f COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -LTCOMPILE = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ - $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \ + $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \ + $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = CCLD = $(CC) -LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \ - --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \ - $(LDFLAGS) -o $@ -SOURCES = $(libnutscan_la_SOURCES) $(nut_scanner_SOURCES) +LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +SOURCES = $(libnutscan_la_SOURCES) $(nodist_libnutscan_la_SOURCES) \ + $(nut_scanner_SOURCES) DIST_SOURCES = $(libnutscan_la_SOURCES) $(nut_scanner_SOURCES) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac am__dist_noinst_HEADERS_DIST = nutscan-usb.h nutscan-snmp.h nut-scan.h \ - nutscan-device.h nutscan-ip.h nutscan-init.h + nutscan-device.h nutscan-ip.h nutscan-init.h nutscan-serial.h am__include_HEADERS_DIST = nut-scan.h nutscan-device.h nutscan-ip.h \ - nutscan-init.h + nutscan-init.h nutscan-serial.h HEADERS = $(dist_noinst_HEADERS) $(include_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` ETAGS = etags CTAGS = ctags +am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp README DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) A2X = @A2X@ ACLOCAL = @ACLOCAL@ AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ AR = @AR@ ASCIIDOC = @ASCIIDOC@ +ASPELL = @ASPELL@ +AUGPARSE = @AUGPARSE@ AUTOCONF = @AUTOCONF@ AUTOHEADER = @AUTOHEADER@ AUTOMAKE = @AUTOMAKE@ @@ -164,16 +308,25 @@ CCDEPMODE = @CCDEPMODE@ CFLAGS = @CFLAGS@ CONFPATH = @CONFPATH@ CPP = @CPP@ +CPPCHECK = @CPPCHECK@ CPPFLAGS = @CPPFLAGS@ +CPPUNIT_CFLAGS = @CPPUNIT_CFLAGS@ +CPPUNIT_LIBS = @CPPUNIT_LIBS@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ CYGPATH_W = @CYGPATH_W@ DBLATEX = @DBLATEX@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ DLLTOOL = @DLLTOOL@ DOC_BUILD_LIST = @DOC_BUILD_LIST@ +DOC_CHECK_LIST = @DOC_CHECK_LIST@ DRIVER_BUILD_LIST = @DRIVER_BUILD_LIST@ DRIVER_INSTALL_TARGET = @DRIVER_INSTALL_TARGET@ DRIVER_MAN_LIST = @DRIVER_MAN_LIST@ +DRVPATH = @DRVPATH@ DSYMUTIL = @DSYMUTIL@ DUMPBIN = @DUMPBIN@ ECHO_C = @ECHO_C@ @@ -182,11 +335,8 @@ ECHO_T = @ECHO_T@ EGREP = @EGREP@ EXEEXT = @EXEEXT@ FGREP = @FGREP@ +GDLIB_CONFIG = @GDLIB_CONFIG@ GREP = @GREP@ -HAL_CALLOUTS_PATH = @HAL_CALLOUTS_PATH@ -HAL_DEVICE_MATCH_KEY = @HAL_DEVICE_MATCH_KEY@ -HAL_FDI_PATH = @HAL_FDI_PATH@ -HAL_USER = @HAL_USER@ INSTALL = @INSTALL@ INSTALL_DATA = @INSTALL_DATA@ INSTALL_PROGRAM = @INSTALL_PROGRAM@ @@ -196,14 +346,15 @@ LD = @LD@ LDFLAGS = @LDFLAGS@ LIBAVAHI_CFLAGS = @LIBAVAHI_CFLAGS@ LIBAVAHI_LIBS = @LIBAVAHI_LIBS@ +LIBDIR = @LIBDIR@ LIBGD_CFLAGS = @LIBGD_CFLAGS@ LIBGD_LDFLAGS = @LIBGD_LDFLAGS@ -LIBHAL_CFLAGS = @LIBHAL_CFLAGS@ -LIBHAL_LIBS = @LIBHAL_LIBS@ LIBIPMI_CFLAGS = @LIBIPMI_CFLAGS@ LIBIPMI_LIBS = @LIBIPMI_LIBS@ LIBLTDL_CFLAGS = @LIBLTDL_CFLAGS@ LIBLTDL_LIBS = @LIBLTDL_LIBS@ +LIBMODBUS_CFLAGS = @LIBMODBUS_CFLAGS@ +LIBMODBUS_LIBS = @LIBMODBUS_LIBS@ LIBNEON_CFLAGS = @LIBNEON_CFLAGS@ LIBNEON_LIBS = @LIBNEON_LIBS@ LIBNETSNMP_CFLAGS = @LIBNETSNMP_CFLAGS@ @@ -214,21 +365,30 @@ LIBPOWERMAN_LIBS = @LIBPOWERMAN_LIBS@ LIBS = @LIBS@ LIBSSL_CFLAGS = @LIBSSL_CFLAGS@ LIBSSL_LIBS = @LIBSSL_LIBS@ +LIBSSL_REQUIRES = @LIBSSL_REQUIRES@ LIBTOOL = @LIBTOOL@ +LIBTOOL_DEPS = @LIBTOOL_DEPS@ LIBUSB_CFLAGS = @LIBUSB_CFLAGS@ +LIBUSB_CONFIG = @LIBUSB_CONFIG@ LIBUSB_LIBS = @LIBUSB_LIBS@ LIBWRAP_CFLAGS = @LIBWRAP_CFLAGS@ LIBWRAP_LIBS = @LIBWRAP_LIBS@ LIPO = @LIPO@ LN_S = @LN_S@ +LN_S_R = @LN_S_R@ LTLIBOBJS = @LTLIBOBJS@ +LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@ MAINT = @MAINT@ MAKEINFO = @MAKEINFO@ MANIFEST_TOOL = @MANIFEST_TOOL@ MKDIR_P = @MKDIR_P@ NETLIBS = @NETLIBS@ +NET_SNMP_CONFIG = @NET_SNMP_CONFIG@ NM = @NM@ NMEDIT = @NMEDIT@ +NUT_DATADIR = @NUT_DATADIR@ +NUT_LIBEXECDIR = @NUT_LIBEXECDIR@ +NUT_NETVERSION = @NUT_NETVERSION@ OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OS_NAME = @OS_NAME@ @@ -242,35 +402,46 @@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_URL = @PACKAGE_URL@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ +PIDPATH = @PIDPATH@ PKG_CONFIG = @PKG_CONFIG@ PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@ PKG_CONFIG_PATH = @PKG_CONFIG_PATH@ PORT = @PORT@ +PYTHON = @PYTHON@ +PYTHON2 = @PYTHON2@ +PYTHON3 = @PYTHON3@ RANLIB = @RANLIB@ RUN_AS_GROUP = @RUN_AS_GROUP@ RUN_AS_USER = @RUN_AS_USER@ +SBINDIR = @SBINDIR@ SED = @SED@ SERLIBS = @SERLIBS@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ +SOURCE_HIGHLIGHT = @SOURCE_HIGHLIGHT@ STATEPATH = @STATEPATH@ STRIP = @STRIP@ SUN_LIBUSB = @SUN_LIBUSB@ TREE_VERSION = @TREE_VERSION@ +VALGRIND = @VALGRIND@ VERSION = @VERSION@ WORDS_BIGENDIAN = @WORDS_BIGENDIAN@ +XMLLINT = @XMLLINT@ +XSLTPROC = @XSLTPROC@ abs_builddir = @abs_builddir@ abs_srcdir = @abs_srcdir@ abs_top_builddir = @abs_top_builddir@ abs_top_srcdir = @abs_top_srcdir@ ac_ct_AR = @ac_ct_AR@ ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ am__include = @am__include@ am__leading_dot = @am__leading_dot@ am__quote = @am__quote@ am__tar = @am__tar@ am__untar = @am__untar@ +auglensdir = @auglensdir@ bindir = @bindir@ build = @build@ build_alias = @build_alias@ @@ -281,8 +452,12 @@ builddir = @builddir@ cgiexecdir = @cgiexecdir@ datadir = @datadir@ datarootdir = @datarootdir@ +devddir = @devddir@ docdir = @docdir@ driverexecdir = @driverexecdir@ +dummy_PKG_CONFIG = @dummy_PKG_CONFIG@ +dummy_PKG_CONFIG_CFLAGS = @dummy_PKG_CONFIG_CFLAGS@ +dummy_PKG_CONFIG_LIBS = @dummy_PKG_CONFIG_LIBS@ dvidir = @dvidir@ exec_prefix = @exec_prefix@ host = @host@ @@ -301,18 +476,21 @@ localedir = @localedir@ localstatedir = @localstatedir@ mandir = @mandir@ mkdir_p = @mkdir_p@ +now = @now@ oldincludedir = @oldincludedir@ pdfdir = @pdfdir@ pkgconfigdir = @pkgconfigdir@ prefix = @prefix@ program_transform_name = @program_transform_name@ psdir = @psdir@ +runstatedir = @runstatedir@ sbindir = @sbindir@ sharedstatedir = @sharedstatedir@ srcdir = @srcdir@ sysconfdir = @sysconfdir@ -systemdsystemshutdowndir = @systemdsystemshutdowndir@ +systemdshutdowndir = @systemdshutdowndir@ systemdsystemunitdir = @systemdsystemunitdir@ +systemdtmpfilesdir = @systemdtmpfilesdir@ target = @target@ target_alias = @target_alias@ target_cpu = @target_cpu@ @@ -322,25 +500,63 @@ top_build_prefix = @top_build_prefix@ top_builddir = @top_builddir@ top_srcdir = @top_srcdir@ udevdir = @udevdir@ -BUILT_SOURCES = nutscan-usb.h nutscan-snmp.h + +# Generally, list headers and/or sources which are re-generated +# for nut-scanner in the parent dir +NUT_SCANNER_DEPS_H = nutscan-usb.h nutscan-snmp.h +NUT_SCANNER_DEPS_C = + +# General set of nut-scanner dependencies generated in the parent dir +NUT_SCANNER_DEPS = $(NUT_SCANNER_DEPS_H) $(NUT_SCANNER_DEPS_C) +BUILT_SOURCES = $(NUT_SCANNER_DEPS) $(LINKED_SOURCE_FILES) +CLEANFILES = $(BUILT_SOURCES) $(LINKED_SOURCE_FILES) *-spellchecked + +# Separate the .deps of other dirs from this one +# NOTE: Not using "$<" due to a legacy Sun/illumos dmake bug with resolver +# of dynamic vars, see e.g. https://man.omnios.org/man1/make#BUGS +LINKED_SOURCE_FILES = serial.c bcmxcp_ser.c @WITH_LIBLTDL_TRUE@lib_LTLIBRARIES = libnutscan.la libnutscan_la_SOURCES = scan_nut.c scan_ipmi.c \ - nutscan-device.c nutscan-ip.c nutscan-display.c nutscan-init.c \ - scan_usb.c scan_snmp.c scan_xml_http.c scan_avahi.c + nutscan-device.c nutscan-ip.c nutscan-display.c \ + nutscan-init.c scan_usb.c scan_snmp.c scan_xml_http.c \ + scan_avahi.c scan_eaton_serial.c nutscan-serial.c -libnutscan_la_LIBADD = ../../clients/libupsclient.la $(NETLIBS) \ - $(LIBLTDL_LIBS) $(am__append_2) -libnutscan_la_LDFLAGS = -version-info 1:0:0 +nodist_libnutscan_la_SOURCES = $(LINKED_SOURCE_FILES) +libnutscan_la_LIBADD = $(NETLIBS) $(am__append_1) \ + $(top_builddir)/common/libcommonclient.la $(am__append_3) +# +# Below we set API versions of public libraries +# http://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html +# Note that changes here may have to be reflected in packaging (the shared +# object .so names would differ) +# +# libnutscan version information + +# libnutscan exported symbols regex +# WARNING: Since the library includes parts of libcommon (as much as needed +# e.g. for logging or string manipulations), we export a few symbols from +# those too. This may cause issues for (third-party) code development that +# would use both libnutscan and libcommon or similar libs. Here we did have +# a problem with nut-scanner using two copies of common.c and conflicting +# copies of "nut_debug_level" making fun of our debug-logging attempts. +# One solution to tackle if needed for those cases would be to make some +# dynamic/shared libnutcommon (etc.) +libnutscan_la_LDFLAGS = $(SERLIBS) -version-info 2:0:0 \ + -export-symbols-regex \ + '^(nutscan_|nut_debug_level|s_upsdebugx|max_threads|curr_threads)' libnutscan_la_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include \ - $(LIBLTDL_CFLAGS) $(am__append_1) $(am__append_3) \ + $(LIBLTDL_CFLAGS) -I$(top_srcdir)/drivers $(am__append_2) \ $(am__append_4) $(am__append_5) $(am__append_6) \ - $(am__append_7) + $(am__append_7) $(am__append_8) nut_scanner_SOURCES = nut-scanner.c nut_scanner_CFLAGS = -I$(top_srcdir)/clients -I$(top_srcdir)/include nut_scanner_LDADD = libnutscan.la -dist_noinst_HEADERS = nutscan-usb.h nutscan-snmp.h $(am__append_8) -@WITH_DEV_TRUE@include_HEADERS = nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h -CLEANFILES = nutscan-usb.h nutscan-snmp.h + +# C is not a header, but there is no dist_noinst_SOURCES +dist_noinst_HEADERS = $(NUT_SCANNER_DEPS_H) $(NUT_SCANNER_DEPS_C) \ + $(am__append_9) +@WITH_DEV_TRUE@include_HEADERS = nut-scan.h nutscan-device.h nutscan-ip.h nutscan-init.h nutscan-serial.h +MAINTAINERCLEANFILES = Makefile.in .dirstamp all: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) all-am @@ -358,14 +574,13 @@ $(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__confi echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu tools/nut-scanner/Makefile'; \ $(am__cd) $(top_srcdir) && \ $(AUTOMAKE) --gnu tools/nut-scanner/Makefile -.PRECIOUS: Makefile Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status @case '$?' in \ *config.status*) \ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ *) \ - echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ - cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \ esac; $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) @@ -376,49 +591,21 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) $(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh $(am__aclocal_m4_deps): -install-libLTLIBRARIES: $(lib_LTLIBRARIES) - @$(NORMAL_INSTALL) - test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)" - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ - list2=; for p in $$list; do \ - if test -f $$p; then \ - list2="$$list2 $$p"; \ - else :; fi; \ - done; \ - test -z "$$list2" || { \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ - } - -uninstall-libLTLIBRARIES: - @$(NORMAL_UNINSTALL) - @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ - for p in $$list; do \ - $(am__strip_dir) \ - echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ - $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ - done - -clean-libLTLIBRARIES: - -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) - @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ - dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ - test "$$dir" != "$$p" || dir=.; \ - echo "rm -f \"$${dir}/so_locations\""; \ - rm -f "$${dir}/so_locations"; \ - done -libnutscan.la: $(libnutscan_la_OBJECTS) $(libnutscan_la_DEPENDENCIES) - $(libnutscan_la_LINK) $(am_libnutscan_la_rpath) $(libnutscan_la_OBJECTS) $(libnutscan_la_LIBADD) $(LIBS) install-binPROGRAMS: $(bin_PROGRAMS) @$(NORMAL_INSTALL) - test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)" @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ for p in $$list; do echo "$$p $$p"; done | \ sed 's/$(EXEEXT)$$//' | \ - while read p p1; do if test -f $$p || test -f $$p1; \ - then echo "$$p"; echo "$$p"; else :; fi; \ + while read p p1; do if test -f $$p \ + || test -f $$p1 \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ - sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ @@ -439,7 +626,8 @@ uninstall-binPROGRAMS: @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ files=`for p in $$list; do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ - -e 's/$$/$(EXEEXT)/' `; \ + -e 's/$$/$(EXEEXT)/' \ + `; \ test -n "$$list" || exit 0; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files @@ -452,9 +640,48 @@ clean-binPROGRAMS: list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \ echo " rm -f" $$list; \ rm -f $$list -nut-scanner$(EXEEXT): $(nut_scanner_OBJECTS) $(nut_scanner_DEPENDENCIES) + +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \ + } + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + $(am__strip_dir) \ + echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \ + $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; \ + locs=`for p in $$list; do echo $$p; done | \ + sed 's|^[^/]*$$|.|; s|/[^/]*$$||; s|$$|/so_locations|' | \ + sort -u`; \ + test -z "$$locs" || { \ + echo rm -f $${locs}; \ + rm -f $${locs}; \ + } + +libnutscan.la: $(libnutscan_la_OBJECTS) $(libnutscan_la_DEPENDENCIES) $(EXTRA_libnutscan_la_DEPENDENCIES) + $(AM_V_CCLD)$(libnutscan_la_LINK) $(am_libnutscan_la_rpath) $(libnutscan_la_OBJECTS) $(libnutscan_la_LIBADD) $(LIBS) + +nut-scanner$(EXEEXT): $(nut_scanner_OBJECTS) $(nut_scanner_DEPENDENCIES) $(EXTRA_nut_scanner_DEPENDENCIES) @rm -f nut-scanner$(EXEEXT) - $(nut_scanner_LINK) $(nut_scanner_OBJECTS) $(nut_scanner_LDADD) $(LIBS) + $(AM_V_CCLD)$(nut_scanner_LINK) $(nut_scanner_OBJECTS) $(nut_scanner_LDADD) $(LIBS) mostlyclean-compile: -rm -f *.$(OBJEXT) @@ -462,122 +689,163 @@ mostlyclean-compile: distclean-compile: -rm -f *.tab.c -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-device.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-display.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-init.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-ip.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_avahi.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_ipmi.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_nut.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_snmp.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_usb.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_xml_http.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nut_scanner-nut-scanner.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-bcmxcp_ser.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-device.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-display.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-init.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-ip.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-nutscan-serial.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_avahi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_eaton_serial.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_ipmi.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_nut.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_snmp.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_usb.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-scan_xml_http.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libnutscan_la-serial.Plo@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nut_scanner-nut-scanner.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) .c.o: -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< .c.obj: -@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` .c.lo: -@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $< -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.lo$$||'`;\ +@am__fastdepCC_TRUE@ $(LTCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $< +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $< libnutscan_la-scan_nut.lo: scan_nut.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_nut.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_nut.Tpo -c -o libnutscan_la-scan_nut.lo `test -f 'scan_nut.c' || echo '$(srcdir)/'`scan_nut.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnutscan_la-scan_nut.Tpo $(DEPDIR)/libnutscan_la-scan_nut.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='scan_nut.c' object='libnutscan_la-scan_nut.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_nut.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_nut.Tpo -c -o libnutscan_la-scan_nut.lo `test -f 'scan_nut.c' || echo '$(srcdir)/'`scan_nut.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_nut.Tpo $(DEPDIR)/libnutscan_la-scan_nut.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_nut.c' object='libnutscan_la-scan_nut.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_nut.lo `test -f 'scan_nut.c' || echo '$(srcdir)/'`scan_nut.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_nut.lo `test -f 'scan_nut.c' || echo '$(srcdir)/'`scan_nut.c libnutscan_la-scan_ipmi.lo: scan_ipmi.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_ipmi.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_ipmi.Tpo -c -o libnutscan_la-scan_ipmi.lo `test -f 'scan_ipmi.c' || echo '$(srcdir)/'`scan_ipmi.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnutscan_la-scan_ipmi.Tpo $(DEPDIR)/libnutscan_la-scan_ipmi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='scan_ipmi.c' object='libnutscan_la-scan_ipmi.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_ipmi.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_ipmi.Tpo -c -o libnutscan_la-scan_ipmi.lo `test -f 'scan_ipmi.c' || echo '$(srcdir)/'`scan_ipmi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_ipmi.Tpo $(DEPDIR)/libnutscan_la-scan_ipmi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_ipmi.c' object='libnutscan_la-scan_ipmi.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_ipmi.lo `test -f 'scan_ipmi.c' || echo '$(srcdir)/'`scan_ipmi.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_ipmi.lo `test -f 'scan_ipmi.c' || echo '$(srcdir)/'`scan_ipmi.c libnutscan_la-nutscan-device.lo: nutscan-device.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-device.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-device.Tpo -c -o libnutscan_la-nutscan-device.lo `test -f 'nutscan-device.c' || echo '$(srcdir)/'`nutscan-device.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnutscan_la-nutscan-device.Tpo $(DEPDIR)/libnutscan_la-nutscan-device.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nutscan-device.c' object='libnutscan_la-nutscan-device.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-device.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-device.Tpo -c -o libnutscan_la-nutscan-device.lo `test -f 'nutscan-device.c' || echo '$(srcdir)/'`nutscan-device.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-nutscan-device.Tpo $(DEPDIR)/libnutscan_la-nutscan-device.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutscan-device.c' object='libnutscan_la-nutscan-device.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-device.lo `test -f 'nutscan-device.c' || echo '$(srcdir)/'`nutscan-device.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-device.lo `test -f 'nutscan-device.c' || echo '$(srcdir)/'`nutscan-device.c libnutscan_la-nutscan-ip.lo: nutscan-ip.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-ip.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-ip.Tpo -c -o libnutscan_la-nutscan-ip.lo `test -f 'nutscan-ip.c' || echo '$(srcdir)/'`nutscan-ip.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnutscan_la-nutscan-ip.Tpo $(DEPDIR)/libnutscan_la-nutscan-ip.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nutscan-ip.c' object='libnutscan_la-nutscan-ip.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-ip.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-ip.Tpo -c -o libnutscan_la-nutscan-ip.lo `test -f 'nutscan-ip.c' || echo '$(srcdir)/'`nutscan-ip.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-nutscan-ip.Tpo $(DEPDIR)/libnutscan_la-nutscan-ip.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutscan-ip.c' object='libnutscan_la-nutscan-ip.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-ip.lo `test -f 'nutscan-ip.c' || echo '$(srcdir)/'`nutscan-ip.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-ip.lo `test -f 'nutscan-ip.c' || echo '$(srcdir)/'`nutscan-ip.c libnutscan_la-nutscan-display.lo: nutscan-display.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-display.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-display.Tpo -c -o libnutscan_la-nutscan-display.lo `test -f 'nutscan-display.c' || echo '$(srcdir)/'`nutscan-display.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnutscan_la-nutscan-display.Tpo $(DEPDIR)/libnutscan_la-nutscan-display.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nutscan-display.c' object='libnutscan_la-nutscan-display.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-display.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-display.Tpo -c -o libnutscan_la-nutscan-display.lo `test -f 'nutscan-display.c' || echo '$(srcdir)/'`nutscan-display.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-nutscan-display.Tpo $(DEPDIR)/libnutscan_la-nutscan-display.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutscan-display.c' object='libnutscan_la-nutscan-display.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-display.lo `test -f 'nutscan-display.c' || echo '$(srcdir)/'`nutscan-display.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-display.lo `test -f 'nutscan-display.c' || echo '$(srcdir)/'`nutscan-display.c libnutscan_la-nutscan-init.lo: nutscan-init.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-init.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-init.Tpo -c -o libnutscan_la-nutscan-init.lo `test -f 'nutscan-init.c' || echo '$(srcdir)/'`nutscan-init.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnutscan_la-nutscan-init.Tpo $(DEPDIR)/libnutscan_la-nutscan-init.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nutscan-init.c' object='libnutscan_la-nutscan-init.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-init.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-init.Tpo -c -o libnutscan_la-nutscan-init.lo `test -f 'nutscan-init.c' || echo '$(srcdir)/'`nutscan-init.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-nutscan-init.Tpo $(DEPDIR)/libnutscan_la-nutscan-init.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutscan-init.c' object='libnutscan_la-nutscan-init.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-init.lo `test -f 'nutscan-init.c' || echo '$(srcdir)/'`nutscan-init.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-init.lo `test -f 'nutscan-init.c' || echo '$(srcdir)/'`nutscan-init.c libnutscan_la-scan_usb.lo: scan_usb.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_usb.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_usb.Tpo -c -o libnutscan_la-scan_usb.lo `test -f 'scan_usb.c' || echo '$(srcdir)/'`scan_usb.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnutscan_la-scan_usb.Tpo $(DEPDIR)/libnutscan_la-scan_usb.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='scan_usb.c' object='libnutscan_la-scan_usb.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_usb.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_usb.Tpo -c -o libnutscan_la-scan_usb.lo `test -f 'scan_usb.c' || echo '$(srcdir)/'`scan_usb.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_usb.Tpo $(DEPDIR)/libnutscan_la-scan_usb.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_usb.c' object='libnutscan_la-scan_usb.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_usb.lo `test -f 'scan_usb.c' || echo '$(srcdir)/'`scan_usb.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_usb.lo `test -f 'scan_usb.c' || echo '$(srcdir)/'`scan_usb.c libnutscan_la-scan_snmp.lo: scan_snmp.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_snmp.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_snmp.Tpo -c -o libnutscan_la-scan_snmp.lo `test -f 'scan_snmp.c' || echo '$(srcdir)/'`scan_snmp.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnutscan_la-scan_snmp.Tpo $(DEPDIR)/libnutscan_la-scan_snmp.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='scan_snmp.c' object='libnutscan_la-scan_snmp.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_snmp.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_snmp.Tpo -c -o libnutscan_la-scan_snmp.lo `test -f 'scan_snmp.c' || echo '$(srcdir)/'`scan_snmp.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_snmp.Tpo $(DEPDIR)/libnutscan_la-scan_snmp.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_snmp.c' object='libnutscan_la-scan_snmp.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_snmp.lo `test -f 'scan_snmp.c' || echo '$(srcdir)/'`scan_snmp.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_snmp.lo `test -f 'scan_snmp.c' || echo '$(srcdir)/'`scan_snmp.c libnutscan_la-scan_xml_http.lo: scan_xml_http.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_xml_http.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_xml_http.Tpo -c -o libnutscan_la-scan_xml_http.lo `test -f 'scan_xml_http.c' || echo '$(srcdir)/'`scan_xml_http.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnutscan_la-scan_xml_http.Tpo $(DEPDIR)/libnutscan_la-scan_xml_http.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='scan_xml_http.c' object='libnutscan_la-scan_xml_http.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_xml_http.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_xml_http.Tpo -c -o libnutscan_la-scan_xml_http.lo `test -f 'scan_xml_http.c' || echo '$(srcdir)/'`scan_xml_http.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_xml_http.Tpo $(DEPDIR)/libnutscan_la-scan_xml_http.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_xml_http.c' object='libnutscan_la-scan_xml_http.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_xml_http.lo `test -f 'scan_xml_http.c' || echo '$(srcdir)/'`scan_xml_http.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_xml_http.lo `test -f 'scan_xml_http.c' || echo '$(srcdir)/'`scan_xml_http.c libnutscan_la-scan_avahi.lo: scan_avahi.c -@am__fastdepCC_TRUE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_avahi.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_avahi.Tpo -c -o libnutscan_la-scan_avahi.lo `test -f 'scan_avahi.c' || echo '$(srcdir)/'`scan_avahi.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libnutscan_la-scan_avahi.Tpo $(DEPDIR)/libnutscan_la-scan_avahi.Plo -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='scan_avahi.c' object='libnutscan_la-scan_avahi.lo' libtool=yes @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_avahi.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_avahi.Tpo -c -o libnutscan_la-scan_avahi.lo `test -f 'scan_avahi.c' || echo '$(srcdir)/'`scan_avahi.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_avahi.Tpo $(DEPDIR)/libnutscan_la-scan_avahi.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_avahi.c' object='libnutscan_la-scan_avahi.lo' libtool=yes @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_avahi.lo `test -f 'scan_avahi.c' || echo '$(srcdir)/'`scan_avahi.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_avahi.lo `test -f 'scan_avahi.c' || echo '$(srcdir)/'`scan_avahi.c + +libnutscan_la-scan_eaton_serial.lo: scan_eaton_serial.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-scan_eaton_serial.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-scan_eaton_serial.Tpo -c -o libnutscan_la-scan_eaton_serial.lo `test -f 'scan_eaton_serial.c' || echo '$(srcdir)/'`scan_eaton_serial.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-scan_eaton_serial.Tpo $(DEPDIR)/libnutscan_la-scan_eaton_serial.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='scan_eaton_serial.c' object='libnutscan_la-scan_eaton_serial.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-scan_eaton_serial.lo `test -f 'scan_eaton_serial.c' || echo '$(srcdir)/'`scan_eaton_serial.c + +libnutscan_la-nutscan-serial.lo: nutscan-serial.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-nutscan-serial.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-nutscan-serial.Tpo -c -o libnutscan_la-nutscan-serial.lo `test -f 'nutscan-serial.c' || echo '$(srcdir)/'`nutscan-serial.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-nutscan-serial.Tpo $(DEPDIR)/libnutscan_la-nutscan-serial.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nutscan-serial.c' object='libnutscan_la-nutscan-serial.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-nutscan-serial.lo `test -f 'nutscan-serial.c' || echo '$(srcdir)/'`nutscan-serial.c + +libnutscan_la-serial.lo: serial.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-serial.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-serial.Tpo -c -o libnutscan_la-serial.lo `test -f 'serial.c' || echo '$(srcdir)/'`serial.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-serial.Tpo $(DEPDIR)/libnutscan_la-serial.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='serial.c' object='libnutscan_la-serial.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-serial.lo `test -f 'serial.c' || echo '$(srcdir)/'`serial.c + +libnutscan_la-bcmxcp_ser.lo: bcmxcp_ser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -MT libnutscan_la-bcmxcp_ser.lo -MD -MP -MF $(DEPDIR)/libnutscan_la-bcmxcp_ser.Tpo -c -o libnutscan_la-bcmxcp_ser.lo `test -f 'bcmxcp_ser.c' || echo '$(srcdir)/'`bcmxcp_ser.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libnutscan_la-bcmxcp_ser.Tpo $(DEPDIR)/libnutscan_la-bcmxcp_ser.Plo +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='bcmxcp_ser.c' object='libnutscan_la-bcmxcp_ser.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libnutscan_la_CFLAGS) $(CFLAGS) -c -o libnutscan_la-bcmxcp_ser.lo `test -f 'bcmxcp_ser.c' || echo '$(srcdir)/'`bcmxcp_ser.c nut_scanner-nut-scanner.o: nut-scanner.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nut_scanner_CFLAGS) $(CFLAGS) -MT nut_scanner-nut-scanner.o -MD -MP -MF $(DEPDIR)/nut_scanner-nut-scanner.Tpo -c -o nut_scanner-nut-scanner.o `test -f 'nut-scanner.c' || echo '$(srcdir)/'`nut-scanner.c -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/nut_scanner-nut-scanner.Tpo $(DEPDIR)/nut_scanner-nut-scanner.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nut-scanner.c' object='nut_scanner-nut-scanner.o' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nut_scanner_CFLAGS) $(CFLAGS) -MT nut_scanner-nut-scanner.o -MD -MP -MF $(DEPDIR)/nut_scanner-nut-scanner.Tpo -c -o nut_scanner-nut-scanner.o `test -f 'nut-scanner.c' || echo '$(srcdir)/'`nut-scanner.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nut_scanner-nut-scanner.Tpo $(DEPDIR)/nut_scanner-nut-scanner.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nut-scanner.c' object='nut_scanner-nut-scanner.o' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nut_scanner_CFLAGS) $(CFLAGS) -c -o nut_scanner-nut-scanner.o `test -f 'nut-scanner.c' || echo '$(srcdir)/'`nut-scanner.c +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nut_scanner_CFLAGS) $(CFLAGS) -c -o nut_scanner-nut-scanner.o `test -f 'nut-scanner.c' || echo '$(srcdir)/'`nut-scanner.c nut_scanner-nut-scanner.obj: nut-scanner.c -@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nut_scanner_CFLAGS) $(CFLAGS) -MT nut_scanner-nut-scanner.obj -MD -MP -MF $(DEPDIR)/nut_scanner-nut-scanner.Tpo -c -o nut_scanner-nut-scanner.obj `if test -f 'nut-scanner.c'; then $(CYGPATH_W) 'nut-scanner.c'; else $(CYGPATH_W) '$(srcdir)/nut-scanner.c'; fi` -@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/nut_scanner-nut-scanner.Tpo $(DEPDIR)/nut_scanner-nut-scanner.Po -@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='nut-scanner.c' object='nut_scanner-nut-scanner.obj' libtool=no @AMDEPBACKSLASH@ +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nut_scanner_CFLAGS) $(CFLAGS) -MT nut_scanner-nut-scanner.obj -MD -MP -MF $(DEPDIR)/nut_scanner-nut-scanner.Tpo -c -o nut_scanner-nut-scanner.obj `if test -f 'nut-scanner.c'; then $(CYGPATH_W) 'nut-scanner.c'; else $(CYGPATH_W) '$(srcdir)/nut-scanner.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/nut_scanner-nut-scanner.Tpo $(DEPDIR)/nut_scanner-nut-scanner.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='nut-scanner.c' object='nut_scanner-nut-scanner.obj' libtool=no @AMDEPBACKSLASH@ @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ -@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nut_scanner_CFLAGS) $(CFLAGS) -c -o nut_scanner-nut-scanner.obj `if test -f 'nut-scanner.c'; then $(CYGPATH_W) 'nut-scanner.c'; else $(CYGPATH_W) '$(srcdir)/nut-scanner.c'; fi` +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(nut_scanner_CFLAGS) $(CFLAGS) -c -o nut_scanner-nut-scanner.obj `if test -f 'nut-scanner.c'; then $(CYGPATH_W) 'nut-scanner.c'; else $(CYGPATH_W) '$(srcdir)/nut-scanner.c'; fi` mostlyclean-libtool: -rm -f *.lo @@ -586,8 +854,11 @@ clean-libtool: -rm -rf .libs _libs install-includeHEADERS: $(include_HEADERS) @$(NORMAL_INSTALL) - test -z "$(includedir)" || $(MKDIR_P) "$(DESTDIR)$(includedir)" @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includedir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includedir)" || exit 1; \ + fi; \ for p in $$list; do \ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ echo "$$d$$p"; \ @@ -601,30 +872,17 @@ uninstall-includeHEADERS: @$(NORMAL_UNINSTALL) @list='$(include_HEADERS)'; test -n "$(includedir)" || list=; \ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ - test -n "$$files" || exit 0; \ - echo " ( cd '$(DESTDIR)$(includedir)' && rm -f" $$files ")"; \ - cd "$(DESTDIR)$(includedir)" && rm -f $$files + dir='$(DESTDIR)$(includedir)'; $(am__uninstall_files_from_dir) -ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ - mkid -fID $$unique -tags: TAGS +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags -TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) set x; \ here=`pwd`; \ - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ + $(am__define_uniq_tagged_files); \ shift; \ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ test -n "$$unique" || unique=$$empty_fix; \ @@ -636,15 +894,11 @@ TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ $$unique; \ fi; \ fi -ctags: CTAGS -CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ - $(TAGS_FILES) $(LISP) - list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ - unique=`for i in $$list; do \ - if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ - done | \ - $(AWK) '{ files[$$0] = 1; nonempty = 1; } \ - END { if (nonempty) { for (i in files) print i; }; }'`; \ +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ test -z "$(CTAGS_ARGS)$$unique" \ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ $$unique @@ -653,11 +907,29 @@ GTAGS: here=`$(am__cd) $(top_builddir) && pwd` \ && $(am__cd) $(top_srcdir) \ && gtags -i $(GTAGS_ARGS) "$$here" +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files distclean-tags: -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags -distdir: $(DISTFILES) +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ list='$(DISTFILES)'; \ @@ -690,16 +962,17 @@ distdir: $(DISTFILES) check-am: all-am check: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) check-am -all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(HEADERS) +all-am: Makefile $(PROGRAMS) $(LTLIBRARIES) $(HEADERS) install-binPROGRAMS: install-libLTLIBRARIES installdirs: - for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(includedir)"; do \ + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(includedir)"; do \ test -z "$$dir" || $(MKDIR_P) "$$dir"; \ done install: $(BUILT_SOURCES) $(MAKE) $(AM_MAKEFLAGS) install-am -install-exec: install-exec-am +install-exec: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data: install-data-am uninstall: uninstall-am @@ -708,10 +981,15 @@ install-am: all-am installcheck: installcheck-am install-strip: - $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ - install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ - `test -z '$(STRIP)' || \ - echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi mostlyclean-generic: clean-generic: @@ -725,13 +1003,28 @@ maintainer-clean-generic: @echo "This command is intended for maintainers to use" @echo "it deletes files that may require special tools to rebuild." -test -z "$(BUILT_SOURCES)" || rm -f $(BUILT_SOURCES) + -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES) clean: clean-am clean-am: clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ clean-libtool mostlyclean-am distclean: distclean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/libnutscan_la-bcmxcp_ser.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-nutscan-device.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-nutscan-display.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-nutscan-init.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-nutscan-ip.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-nutscan-serial.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_avahi.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_eaton_serial.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_ipmi.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_nut.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_snmp.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_usb.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_xml_http.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-serial.Plo + -rm -f ./$(DEPDIR)/nut_scanner-nut-scanner.Po -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-tags @@ -777,7 +1070,21 @@ install-ps-am: installcheck-am: maintainer-clean: maintainer-clean-am - -rm -rf ./$(DEPDIR) + -rm -f ./$(DEPDIR)/libnutscan_la-bcmxcp_ser.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-nutscan-device.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-nutscan-display.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-nutscan-init.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-nutscan-ip.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-nutscan-serial.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_avahi.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_eaton_serial.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_ipmi.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_nut.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_snmp.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_usb.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-scan_xml_http.Plo + -rm -f ./$(DEPDIR)/libnutscan_la-serial.Plo + -rm -f ./$(DEPDIR)/nut_scanner-nut-scanner.Po -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic @@ -797,27 +1104,56 @@ ps-am: uninstall-am: uninstall-binPROGRAMS uninstall-includeHEADERS \ uninstall-libLTLIBRARIES -.MAKE: all check install install-am install-strip +.MAKE: all check install install-am install-exec install-strip -.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \ - clean-generic clean-libLTLIBRARIES clean-libtool ctags \ - distclean distclean-compile distclean-generic \ - distclean-libtool distclean-tags distdir dvi dvi-am html \ - html-am info info-am install install-am install-binPROGRAMS \ - install-data install-data-am install-dvi install-dvi-am \ - install-exec install-exec-am install-html install-html-am \ +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \ + clean-binPROGRAMS clean-generic clean-libLTLIBRARIES \ + clean-libtool cscopelist-am ctags ctags-am distclean \ + distclean-compile distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am html html-am info info-am \ + install install-am install-binPROGRAMS install-data \ + install-data-am install-dvi install-dvi-am install-exec \ + install-exec-am install-html install-html-am \ install-includeHEADERS install-info install-info-am \ install-libLTLIBRARIES install-man install-pdf install-pdf-am \ install-ps install-ps-am install-strip installcheck \ installcheck-am installdirs maintainer-clean \ maintainer-clean-generic mostlyclean mostlyclean-compile \ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ - tags uninstall uninstall-am uninstall-binPROGRAMS \ + tags tags-am uninstall uninstall-am uninstall-binPROGRAMS \ uninstall-includeHEADERS uninstall-libLTLIBRARIES +.PRECIOUS: Makefile -nutscan-usb.h nutscan-snmp.h: - cd ..; $(MAKE) $(AM_MAKEFLAGS) nut-scanner-deps + +# Make sure we have the freshest files (no-op if built earlier and then +# no driver sources and other dependencies were edited by a developer) +$(NUT_SCANNER_DEPS): dummy + @cd .. && $(MAKE) $(AM_MAKEFLAGS) nut-scanner-deps + +# Make sure out-of-dir dependencies exist (especially when dev-building parts): +$(top_builddir)/common/libcommon.la: dummy + @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +# do not hard depend on '../include/nut_version.h', since it blocks +# 'dist', and is only required for actual build, in which case +# BUILT_SOURCES (in ../include) will ensure nut_version.h will +# be built before anything else +nut-scanner.c: $(top_builddir)/include/nut_version.h +serial.c: $(top_srcdir)/drivers/serial.c + test -s "$@" || ln -s -f "$(top_srcdir)/drivers/serial.c" "$@" +bcmxcp_ser.c: $(top_srcdir)/drivers/bcmxcp_ser.c + test -s "$@" || ln -s -f "$(top_srcdir)/drivers/bcmxcp_ser.c" "$@" + +$(top_builddir)/include/nut_version.h: + @cd $(@D) && $(MAKE) $(AM_MAKEFLAGS) $(@F) + +dummy: + +# NOTE: Do not clean ".deps" in SUBDIRS of the main project, +# the root Makefile.am takes care of that! +#clean-local: +# rm -rf $(builddir)/.deps # Tell versions [3.59,3.63) of GNU make to not export all variables. # Otherwise a system limit (for SysV at least) may be exceeded. diff --git a/tools/nut-scanner/README b/tools/nut-scanner/README index d6e9d59..1889237 100644 --- a/tools/nut-scanner/README +++ b/tools/nut-scanner/README @@ -38,6 +38,8 @@ iteration function to display results: nutscan_options_t * opt; nutscan_device_t *device; + nutscan-init(); + if ((device = nutscan_scan_usb()) == NULL) { printf("No device found\n"); exit(EXIT_FAILURE); @@ -75,7 +77,7 @@ iteration function to display results: This library file and the associated header files are not installed by -default. You must `./configure --with-lib` to enable building and +default. You must `./configure --with-dev` to enable building and installing these files. The libraries can then be built and installed with `make` and `make install` as usual. This must be done before building other (non-NUT) programs which depend on them. diff --git a/tools/nut-scanner/nut-scan.h b/tools/nut-scanner/nut-scan.h index affcc77..dee6739 100644 --- a/tools/nut-scanner/nut-scan.h +++ b/tools/nut-scanner/nut-scan.h @@ -1,6 +1,9 @@ -/* nut-scan.h: detect NUT services - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) + * 2011 - EATON + * 2012 - Arnaud Quette + * 2016 - EATON - IP addressed XML scan + * 2016-2021 - EATON - Various threads-related improvements * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,12 +19,57 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/*! \file nut-scan.h + \brief general header for nut-scanner + \author Frederic Bohe + \author Arnaud Quette + \author Michal Vyskocil + \author Jim Klimov +*/ + #ifndef NUT_SCAN_H #define NUT_SCAN_H +#include "config.h" +#include +#include "nut_stdint.h" + #include #include #include +#include + +#ifdef WITH_IPMI +#include +#endif + +#ifdef HAVE_PTHREAD +# include + +# ifdef HAVE_SEMAPHORE +# include +# endif + +# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) +extern size_t max_threads, curr_threads, max_threads_netxml, max_threads_oldnut, max_threads_netsnmp; +# endif + +# ifdef HAVE_PTHREAD_TRYJOIN +extern pthread_mutex_t threadcount_mutex; +# endif + +typedef struct nutscan_thread { + pthread_t thread; + int active; /* true if the thread was created, false if joined (to not join twice) */ +} nutscan_thread_t; +#endif /* HAVE_PTHREAD */ + +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif /* SNMP structure */ typedef struct nutscan_snmp { @@ -36,21 +84,76 @@ typedef struct nutscan_snmp { void * handle; } nutscan_snmp_t; +/* IPMI structure */ +/* Settings for OutofBand (remote) connection */ +typedef struct nutscan_ipmi { + char* username; /* IPMI 1.5 and 2.0 */ + char* password; /* IPMI 1.5 and 2.0 */ + int authentication_type; /* IPMI 1.5 */ + int cipher_suite_id; /* IPMI 2.0 */ + char* K_g_BMC_key; /* IPMI 2.0, optional key for 2 key auth. */ + int privilege_level; /* for both */ + unsigned int workaround_flags; /* for both */ + int ipmi_version; /* IPMI 1.5 or 2.0? */ +} nutscan_ipmi_t; + +/* IPMI auth defines, simply using FreeIPMI defines */ +#ifndef IPMI_AUTHENTICATION_TYPE_NONE + #define IPMI_AUTHENTICATION_TYPE_NONE 0x00 + #define IPMI_AUTHENTICATION_TYPE_MD2 0x01 + #define IPMI_AUTHENTICATION_TYPE_MD5 0x02 + #define IPMI_AUTHENTICATION_TYPE_STRAIGHT_PASSWORD_KEY 0x04 + #define IPMI_AUTHENTICATION_TYPE_OEM_PROP 0x05 + #define IPMI_AUTHENTICATION_TYPE_RMCPPLUS 0x06 +#endif +#ifndef IPMI_PRIVILEGE_LEVEL_ADMIN + #define IPMI_PRIVILEGE_LEVEL_ADMIN 0x04 +#endif + +#define IPMI_1_5 1 +#define IPMI_2_0 0 + +/* XML HTTP structure */ +typedef struct nutscan_xml { + uint16_t port_http; /* Port for xml http (tcp) */ + uint16_t port_udp; /* Port for xml udp */ + useconds_t usec_timeout; /* Wait this long for a response */ + char *peername; /* Hostname or NULL for broadcast mode */ +} nutscan_xml_t; + /* Scanning */ -nutscan_device_t * nutscan_scan_snmp(const char * start_ip,const char * stop_ip,long usec_timeout, nutscan_snmp_t * sec); +nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip, useconds_t usec_timeout, nutscan_snmp_t * sec); -nutscan_device_t * nutscan_scan_usb(); +nutscan_device_t * nutscan_scan_usb(void); -nutscan_device_t * nutscan_scan_xml_http(long usec_timeout); +/* If "ip" == NULL, do a broadcast scan */ +/* If sec->usec_timeout <= 0 then the common usec_timeout arg overrides it */ +nutscan_device_t * nutscan_scan_xml_http_range(const char *start_ip, const char *end_ip, useconds_t usec_timeout, nutscan_xml_t * sec); -nutscan_device_t * nutscan_scan_nut(const char * startIP, const char * stopIP, const char * port, long usec_timeout); +nutscan_device_t * nutscan_scan_nut(const char * startIP, const char * stopIP, const char * port, useconds_t usec_timeout); -nutscan_device_t * nutscan_scan_avahi(long usec_timeout); +nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout); -nutscan_device_t * nutscan_scan_ipmi(void); +nutscan_device_t * nutscan_scan_ipmi(const char * startIP, const char * stopIP, nutscan_ipmi_t * sec); + +nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_list); + +#ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE +/* Expose shared libnutscanner semaphore for overall thread count + * limited across different scanning methods (protocols/media): */ +sem_t * nutscan_semaphore(void); +# endif +#endif /* Display functions */ void nutscan_display_ups_conf(nutscan_device_t * device); void nutscan_display_parsable(nutscan_device_t * device); +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + #endif diff --git a/tools/nut-scanner/nut-scanner.c b/tools/nut-scanner/nut-scanner.c index 2114674..30a7206 100644 --- a/tools/nut-scanner/nut-scanner.c +++ b/tools/nut-scanner/nut-scanner.c @@ -1,6 +1,7 @@ -/* nut-scanner.c: a tool to detect NUT supported devices - * - * Copyright (C) 2011 - Arnaud Quette +/* + * Copyright (C) 2011 - 2012 Arnaud Quette + * Copyright (C) 2016 Michal Vyskocil + * Copyright (C) 2016 - 2021 Jim Klimov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,16 +18,42 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/*! \file nut-scanner.c + \brief A tool to detect NUT supported devices + \author Arnaud Quette + \author Michal Vyskocil + \author Jim Klimov +*/ + +#include "common.h" /* Must be first include to pull "config.h" */ + #include #include #include -#include "common.h" #include "nut_version.h" #include #include + #ifdef HAVE_PTHREAD -#include -#endif +# include +# ifdef HAVE_SEMAPHORE +# include +# endif +# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) +# include "nut_stdint.h" +# ifdef HAVE_SYS_RESOURCE_H +# include /* for getrlimit() and struct rlimit */ +# include + +/* 3 is reserved for known overhead (for NetXML at least) + * following practical investigation summarized at + * https://github.com/networkupstools/nut/pull/1158 + * and probably means the usual stdin/stdout/stderr triplet + */ +# define RESERVE_FD_COUNT 3 +# endif /* HAVE_SYS_RESOURCE_H */ +# endif /* HAVE_PTHREAD_TRYJOIN || HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ #include "nut-scan.h" @@ -34,104 +61,288 @@ #define ERR_BAD_OPTION (-1) -const char optstring[] = "?ht:s:e:c:l:u:W:X:w:x:p:CUSMOAm:NPqIVa"; +static const char optstring[] = "?ht:T:s:e:E:c:l:u:W:X:w:x:p:b:B:d:L:CUSMOAm:NPqIVaD"; #ifdef HAVE_GETOPT_LONG -const struct option longopts[] = - {{ "timeout",required_argument,NULL,'t' }, - { "start_ip",required_argument,NULL,'s' }, - { "end_ip",required_argument,NULL,'e' }, - { "mask_cidr",required_argument,NULL,'m' }, - { "community",required_argument,NULL,'c' }, - { "secLevel",required_argument,NULL,'l' }, - { "secName",required_argument,NULL,'u' }, - { "authPassword",required_argument,NULL,'W' }, - { "privPassword",required_argument,NULL,'X' }, - { "authProtocol",required_argument,NULL,'w' }, - { "privProtocol",required_argument,NULL,'x' }, - { "port",required_argument,NULL,'p' }, - { "complete_scan",no_argument,NULL,'C' }, - { "usb_scan",no_argument,NULL,'U' }, - { "snmp_scan",no_argument,NULL,'S' }, - { "xml_scan",no_argument,NULL,'M' }, - { "oldnut_scan",no_argument,NULL,'O' }, - { "avahi_scan",no_argument,NULL,'A' }, - { "ipmi_scan",no_argument,NULL,'I' }, - { "disp_nut_conf",no_argument,NULL,'N' }, - { "disp_parsable",no_argument,NULL,'P' }, - { "quiet",no_argument,NULL,'q' }, - { "help",no_argument,NULL,'h' }, - { "version",no_argument,NULL,'V' }, - { "available",no_argument,NULL,'a' }, - {NULL,0,NULL,0}}; +static const struct option longopts[] = { + { "timeout", required_argument, NULL, 't' }, + { "thread", required_argument, NULL, 'T' }, + { "start_ip", required_argument, NULL, 's' }, + { "end_ip", required_argument, NULL, 'e' }, + { "eaton_serial", required_argument, NULL, 'E' }, + { "mask_cidr", required_argument, NULL, 'm' }, + { "community", required_argument, NULL, 'c' }, + { "secLevel", required_argument, NULL, 'l' }, + { "secName", required_argument, NULL, 'u' }, + { "authPassword", required_argument, NULL, 'W' }, + { "privPassword", required_argument, NULL, 'X' }, + { "authProtocol", required_argument, NULL, 'w' }, + { "privProtocol", required_argument, NULL, 'x' }, + { "username", required_argument, NULL, 'b' }, + { "password", required_argument, NULL, 'B' }, + { "authType", required_argument, NULL, 'd' }, + { "cipher_suite_id", required_argument, NULL, 'L' }, + { "port", required_argument, NULL, 'p' }, + { "complete_scan", no_argument, NULL, 'C' }, + { "usb_scan", no_argument, NULL, 'U' }, + { "snmp_scan", no_argument, NULL, 'S' }, + { "xml_scan", no_argument, NULL, 'M' }, + { "oldnut_scan", no_argument, NULL, 'O' }, + { "avahi_scan", no_argument, NULL, 'A' }, + { "ipmi_scan", no_argument, NULL, 'I' }, + { "disp_nut_conf", no_argument, NULL, 'N' }, + { "disp_parsable", no_argument, NULL, 'P' }, + { "quiet", no_argument, NULL, 'q' }, + { "help", no_argument, NULL, 'h' }, + { "version", no_argument, NULL, 'V' }, + { "available", no_argument, NULL, 'a' }, + { "nut_debug_level", no_argument, NULL, 'D' }, + { NULL, 0, NULL, 0 } +}; #else -#define getopt_long(a,b,c,d,e) getopt(a,b,c) +#define getopt_long(a,b,c,d,e) getopt(a,b,c) #endif /* HAVE_GETOPT_LONG */ static nutscan_device_t *dev[TYPE_END]; -static long timeout = DEFAULT_TIMEOUT*1000*1000; /* in usec */ -static char * start_ip = NULL; -static char * end_ip = NULL; +static useconds_t timeout = DEFAULT_NETWORK_TIMEOUT * 1000 * 1000; /* in usec */ +static char * start_ip = NULL; +static char * end_ip = NULL; static char * port = NULL; +static char * serial_ports = NULL; #ifdef HAVE_PTHREAD static pthread_t thread[TYPE_END]; -static void * run_usb(void * arg) +static void * run_usb(void *arg) { + NUT_UNUSED_VARIABLE(arg); + dev[TYPE_USB] = nutscan_scan_usb(); return NULL; } + static void * run_snmp(void * arg) { nutscan_snmp_t * sec = (nutscan_snmp_t *)arg; - dev[TYPE_SNMP] = nutscan_scan_snmp(start_ip,end_ip,timeout,sec); + dev[TYPE_SNMP] = nutscan_scan_snmp(start_ip, end_ip, timeout, sec); return NULL; } + static void * run_xml(void * arg) { - dev[TYPE_XML] = nutscan_scan_xml_http(timeout); + nutscan_xml_t * sec = (nutscan_xml_t *)arg; + + dev[TYPE_XML] = nutscan_scan_xml_http_range(start_ip, end_ip, timeout, sec); return NULL; } -static void * run_nut_old(void * arg) +static void * run_nut_old(void *arg) { - dev[TYPE_NUT] = nutscan_scan_nut(start_ip,end_ip,port,timeout); + NUT_UNUSED_VARIABLE(arg); + + dev[TYPE_NUT] = nutscan_scan_nut(start_ip, end_ip, port, timeout); return NULL; } -static void * run_avahi(void * arg) +static void * run_avahi(void *arg) { + NUT_UNUSED_VARIABLE(arg); + dev[TYPE_AVAHI] = nutscan_scan_avahi(timeout); return NULL; } + static void * run_ipmi(void * arg) { - dev[TYPE_IPMI] = nutscan_scan_ipmi(); + nutscan_ipmi_t * sec = (nutscan_ipmi_t *)arg; + + dev[TYPE_IPMI] = nutscan_scan_ipmi(start_ip, end_ip, sec); return NULL; } -#endif /* HAVE_PTHREAD */ -static int printq(int quiet,const char *fmt, ...) -{ - va_list ap; - int ret; - if(quiet) { - return 0; +static void * run_eaton_serial(void *arg) +{ + NUT_UNUSED_VARIABLE(arg); + + dev[TYPE_EATON_SERIAL] = nutscan_scan_eaton_serial(serial_ports); + return NULL; +} + +#endif /* HAVE_PTHREAD */ + +static void show_usage() +{ +/* NOTE: This code uses `nutscan_avail_*` global vars from nutscan-init.c */ + puts("nut-scanner : utility for detection of available power devices.\n"); + puts("OPTIONS:"); + printf(" -C, --complete_scan: Scan all available devices except serial ports (default).\n"); + if (nutscan_avail_usb) { + printf(" -U, --usb_scan: Scan USB devices.\n"); + } else { + printf("* Options for USB devices scan not enabled: library not detected.\n"); + } + if (nutscan_avail_snmp) { + printf(" -S, --snmp_scan: Scan SNMP devices using built-in mapping definitions.\n"); + } else { + printf("* Options for SNMP devices scan not enabled: library not detected.\n"); + } + if (nutscan_avail_xml_http) { + printf(" -M, --xml_scan: Scan XML/HTTP devices.\n"); + } else { + printf("* Options for XML/HTTP devices scan not enabled: library not detected.\n"); + } + printf(" -O, --oldnut_scan: Scan NUT devices (old method).\n"); + if (nutscan_avail_avahi) { + printf(" -A, --avahi_scan: Scan NUT devices (avahi method).\n"); + } else { + printf("* Options for NUT devices (avahi method) scan not enabled: library not detected.\n"); + } + if (nutscan_avail_ipmi) { + printf(" -I, --ipmi_scan: Scan IPMI devices.\n"); + } else { + printf("* Options for IPMI devices scan not enabled: library not detected.\n"); } - va_start(ap, fmt); - ret = vprintf(fmt, ap); - va_end(ap); + printf(" -E, --eaton_serial : Scan serial Eaton devices (XCP, SHUT and Q1).\n"); - return ret; +#if (defined HAVE_PTHREAD) && (defined HAVE_PTHREAD_TRYJOIN) + printf(" -T, --thread : Limit the amount of scanning threads running simultaneously (default: %zu).\n", max_threads); +#else + printf(" -T, --thread : Limit the amount of scanning threads running simultaneously (not implemented in this build: no pthread support)"); +#endif + + printf("\nNetwork specific options:\n"); + printf(" -t, --timeout : network operation timeout (default %d).\n", DEFAULT_NETWORK_TIMEOUT); + printf(" -s, --start_ip : First IP address to scan.\n"); + printf(" -e, --end_ip : Last IP address to scan.\n"); + printf(" -m, --mask_cidr : Give a range of IP using CIDR notation.\n"); + + if (nutscan_avail_snmp) { + printf("\nSNMP v1 specific options:\n"); + printf(" -c, --community : Set SNMP v1 community name (default = public)\n"); + + printf("\nSNMP v3 specific options:\n"); + printf(" -l, --secLevel : Set the securityLevel used for SNMPv3 messages (allowed values: noAuthNoPriv, authNoPriv, authPriv)\n"); + printf(" -u, --secName : Set the securityName used for authenticated SNMPv3 messages (mandatory if you set secLevel. No default)\n"); + + /* Construct help for AUTHPROTO */ + { int comma = 0; + NUT_UNUSED_VARIABLE(comma); /* potentially, if no protocols are available */ + printf(" -w, --authProtocol : Set the authentication protocol ("); +#if (defined WITH_SNMP) && (defined NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol) +/* Note: NUT_HAVE_LIBNETSNMP_* macros are not AC_DEFINE'd when libsnmp was + * completely not detected at configure time, so "#if" is not a pedantically + * correct test (unknown macro may default to "0" but is not guaranteed to). + */ +# if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol + printf("%s%s", + (comma++ ? ", " : ""), + "MD5" + ); +# endif +# if NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol + printf("%s%s", + (comma++ ? ", " : ""), + "SHA" + ); +# endif +# if NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol + printf("%s%s", + (comma++ ? ", " : ""), + "SHA256" + ); +# endif +# if NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol + printf("%s%s", + (comma++ ? ", " : ""), + "SHA384" + ); +# endif +# if NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol + printf("%s%s", + (comma++ ? ", " : ""), + "SHA512" + ); +# endif + printf("%s%s", + (comma ? "" : "none supported"), + ") used for authenticated SNMPv3 messages (default=MD5 if available)\n" + ); + } /* Construct help for AUTHPROTO */ + + printf(" -W, --authPassword : Set the authentication pass phrase used for authenticated SNMPv3 messages (mandatory if you set secLevel to authNoPriv or authPriv)\n"); + + /* Construct help for PRIVPROTO */ + { int comma = 0; + NUT_UNUSED_VARIABLE(comma); /* potentially, if no protocols are available */ + printf(" -x, --privProtocol : Set the privacy protocol ("); +# if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol + printf("%s%s", + (comma++ ? ", " : ""), + "DES" + ); +# endif +# if NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol + printf("%s%s", + (comma++ ? ", " : ""), + "AES" + ); +# endif +# if NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 +# if NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol + printf("%s%s", + (comma++ ? ", " : ""), + "AES192" + ); +# endif +# if NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol + printf("%s%s", + (comma++ ? ", " : ""), + "AES256" + ); +# endif +# endif /* NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 */ +#endif /* built WITH_SNMP */ + printf("%s%s", + (comma ? "" : "none supported"), + ") used for encrypted SNMPv3 messages (default=DES if available)\n" + ); + } /* Construct help for PRIVPROTO */ + + printf(" -X, --privPassword : Set the privacy pass phrase used for encrypted SNMPv3 messages (mandatory if you set secLevel to authPriv)\n"); + } + + if (nutscan_avail_ipmi) { + printf("\nIPMI over LAN specific options:\n"); + printf(" -b, --username : Set the username used for authenticating IPMI over LAN connections (mandatory for IPMI over LAN. No default)\n"); + /* Specify the username to use when authenticating with the remote host. If not specified, a null (i.e. anonymous) username is assumed. The user must have + * at least ADMIN privileges in order for this tool to operate fully. */ + printf(" -B, --password : Specify the password to use when authenticationg with the remote host (mandatory for IPMI over LAN. No default)\n"); + /* Specify the password to use when authenticationg with the remote host. If not specified, a null password is assumed. Maximum password length is 16 for IPMI + * 1.5 and 20 for IPMI 2.0. */ + printf(" -d, --authType : Specify the IPMI 1.5 authentication type to use (NONE, STRAIGHT_PASSWORD_KEY, MD2, and MD5) with the remote host (default=MD5)\n"); + printf(" -L, --cipher_suite_id : Specify the IPMI 2.0 cipher suite ID to use, for authentication, integrity, and confidentiality (default=3)\n"); + } + + printf("\nNUT specific options:\n"); + printf(" -p, --port : Port number of remote NUT upsd\n"); + printf("\ndisplay specific options:\n"); + printf(" -N, --disp_nut_conf: Display result in the ups.conf format\n"); + printf(" -P, --disp_parsable: Display result in a parsable format\n"); + printf("\nMiscellaneous options:\n"); + printf(" -V, --version: Display NUT version\n"); + printf(" -a, --available: Display available bus that can be scanned\n"); + printf(" -q, --quiet: Display only scan result. No information on currently scanned bus is displayed.\n"); + printf(" -D, --nut_debug_level: Raise the debugging level. Use this multiple times to see more details.\n"); } int main(int argc, char *argv[]) { - nutscan_snmp_t sec; + nutscan_snmp_t snmp_sec; + nutscan_ipmi_t ipmi_sec; + nutscan_xml_t xml_sec; int opt_ret; char * cidr = NULL; int allow_all = 0; @@ -141,98 +352,266 @@ int main(int argc, char *argv[]) int allow_oldnut = 0; int allow_avahi = 0; int allow_ipmi = 0; - int quiet = 0; + int allow_eaton_serial = 0; /* MUST be requested explicitly! */ + int quiet = 0; /* The debugging level for certain upsdebugx() progress messages; 0 = print always, quiet==1 is to require at least one -D */ void (*display_func)(nutscan_device_t * device); int ret_code = EXIT_SUCCESS; +#if (defined HAVE_PTHREAD) && ( (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) ) && (defined HAVE_SYS_RESOURCE_H) + struct rlimit nofile_limit; - memset(&sec,0,sizeof(sec)); + /* Limit the max scanning thread count by the amount of allowed open + * file descriptors (which caller can change with `ulimit -n NUM`), + * following practical investigation summarized at + * https://github.com/networkupstools/nut/pull/1158 + * Resource-Limit code inspired by example from: + * https://stackoverflow.com/questions/4076848/how-to-do-the-equivalent-of-ulimit-n-400-from-within-c/4077000#4077000 + */ + + /* Get max number of files. */ + if (getrlimit(RLIMIT_NOFILE, &nofile_limit) != 0) { + /* Report error, keep hardcoded default */ + fprintf(stderr, "getrlimit() failed with errno=%d, keeping default job limits\n", errno); + nofile_limit.rlim_cur = 0; + nofile_limit.rlim_max = 0; + } else { + if (nofile_limit.rlim_cur > 0 + && nofile_limit.rlim_cur > RESERVE_FD_COUNT + && (uintmax_t)max_threads > (uintmax_t)(nofile_limit.rlim_cur - RESERVE_FD_COUNT) + && (uintmax_t)(nofile_limit.rlim_cur) < (uintmax_t)SIZE_MAX + ) { + max_threads = (size_t)nofile_limit.rlim_cur; + if (max_threads > (RESERVE_FD_COUNT + 1)) { + max_threads -= RESERVE_FD_COUNT; + } + } + } +#endif /* HAVE_PTHREAD && ( HAVE_PTHREAD_TRYJOIN || HAVE_SEMAPHORE ) && HAVE_SYS_RESOURCE_H */ + + memset(&snmp_sec, 0, sizeof(snmp_sec)); + memset(&ipmi_sec, 0, sizeof(ipmi_sec)); + memset(&xml_sec, 0, sizeof(xml_sec)); + + /* Set the default values for IPMI */ + ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_MD5; + ipmi_sec.ipmi_version = IPMI_1_5; /* default to IPMI 1.5, if not otherwise specified */ + ipmi_sec.cipher_suite_id = 3; /* default to HMAC-SHA1; HMAC-SHA1-96; AES-CBC-128 */ + ipmi_sec.privilege_level = IPMI_PRIVILEGE_LEVEL_ADMIN; /* should be sufficient */ + + /* Set the default values for XML HTTP (run_xml()) */ + xml_sec.port_http = 80; + xml_sec.port_udp = 4679; + xml_sec.usec_timeout = 0; /* Override with the "timeout" common setting later */ + xml_sec.peername = NULL; + + /* Parse command line options -- First loop: only get debug level */ + /* Suppress error messages, for now -- leave them to the second loop. */ + opterr = 0; + while ((opt_ret = getopt_long(argc, argv, optstring, longopts, NULL)) != -1) { + if (opt_ret == 'D') + nut_debug_level++; + } nutscan_init(); display_func = nutscan_display_ups_conf; - while((opt_ret = getopt_long(argc, argv, optstring, longopts, NULL))!=-1) { + /* Parse command line options -- Second loop: everything else */ + /* Restore error messages... */ + opterr = 1; + /* ...and index of the item to be processed by getopt(). */ + optind = 1; + /* Note: the getopts print an error message about unknown arguments + * or arguments which need a second token and that is missing now */ + while ((opt_ret = getopt_long(argc, argv, optstring, longopts, NULL)) != -1) { switch(opt_ret) { case 't': - timeout = atol(optarg)*1000*1000; /*in usec*/ - if( timeout == 0 ) { - fprintf(stderr,"Illegal timeout value, using default %ds\n", DEFAULT_TIMEOUT); - timeout = DEFAULT_TIMEOUT*1000*1000; + timeout = (useconds_t)atol(optarg) * 1000 * 1000; /*in usec*/ + if (timeout <= 0) { + fprintf(stderr, + "Illegal timeout value, using default %ds\n", + DEFAULT_NETWORK_TIMEOUT); + timeout = DEFAULT_NETWORK_TIMEOUT * 1000 * 1000; } break; case 's': start_ip = strdup(optarg); - end_ip = start_ip; + if (end_ip == NULL) + end_ip = start_ip; break; case 'e': end_ip = strdup(optarg); + if (start_ip == NULL) + start_ip = end_ip; + break; + case 'E': + serial_ports = strdup(optarg); + allow_eaton_serial = 1; break; case 'm': cidr = strdup(optarg); + upsdebugx(5, "Got CIDR net/mask: %s", cidr); + break; + case 'D': + /* nothing to do, here */ break; case 'c': - if(!nutscan_avail_snmp) { + if (!nutscan_avail_snmp) { goto display_help; } - sec.community = strdup(optarg); + snmp_sec.community = strdup(optarg); break; case 'l': - if(!nutscan_avail_snmp) { + if (!nutscan_avail_snmp) { goto display_help; } - sec.secLevel = strdup(optarg); + snmp_sec.secLevel = strdup(optarg); break; case 'u': - if(!nutscan_avail_snmp) { + if (!nutscan_avail_snmp) { goto display_help; } - sec.secName = strdup(optarg); + snmp_sec.secName = strdup(optarg); break; case 'W': - if(!nutscan_avail_snmp) { + if (!nutscan_avail_snmp) { goto display_help; } - sec.authPassword = strdup(optarg); + snmp_sec.authPassword = strdup(optarg); break; case 'X': - if(!nutscan_avail_snmp) { + if (!nutscan_avail_snmp) { goto display_help; } - sec.privPassword = strdup(optarg); + snmp_sec.privPassword = strdup(optarg); break; case 'w': - if(!nutscan_avail_snmp) { + if (!nutscan_avail_snmp) { goto display_help; } - sec.authProtocol = strdup(optarg); + snmp_sec.authProtocol = strdup(optarg); break; case 'x': - if(!nutscan_avail_snmp) { + if (!nutscan_avail_snmp) { goto display_help; } - sec.privProtocol = strdup(optarg); + snmp_sec.privProtocol = strdup(optarg); break; case 'S': - if(!nutscan_avail_snmp) { + if (!nutscan_avail_snmp) { goto display_help; } allow_snmp = 1; break; + case 'b': + if (!nutscan_avail_ipmi) { + goto display_help; + } + ipmi_sec.username = strdup(optarg); + break; + case 'B': + if (!nutscan_avail_ipmi) { + goto display_help; + } + ipmi_sec.password = strdup(optarg); + break; + case 'd': + if (!nutscan_avail_ipmi) { + goto display_help; + } + if (!strcmp(optarg, "NONE")) { + ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_NONE; + } + else if (!strcmp(optarg, "STRAIGHT_PASSWORD_KEY")) { + ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_STRAIGHT_PASSWORD_KEY; + } + else if (!strcmp(optarg, "MD2")) { + ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_MD2; + } + else if (!strcmp(optarg, "MD5")) { + ipmi_sec.authentication_type = IPMI_AUTHENTICATION_TYPE_MD5; + } + else { + fprintf(stderr, + "Unknown authentication type (%s). Defaulting to MD5\n", + optarg); + } + break; + case 'L': + if (!nutscan_avail_ipmi) { + goto display_help; + } + ipmi_sec.cipher_suite_id = atoi(optarg); + /* Force IPMI 2.0! */ + ipmi_sec.ipmi_version = IPMI_2_0; + break; case 'p': port = strdup(optarg); break; + case 'T': { +#if (defined HAVE_PTHREAD) && ( (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) ) + char* endptr; + long val = strtol(optarg, &endptr, 10); + /* With endptr we check that no chars were left in optarg + * (that is, pointed-to char -- if reported -- is '\0') + */ + if ((!endptr || !*endptr) + && val > 0 + && (uintmax_t)val < (uintmax_t)SIZE_MAX + ) { +# ifdef HAVE_SYS_RESOURCE_H + if (nofile_limit.rlim_cur > 0 + && nofile_limit.rlim_cur > RESERVE_FD_COUNT + && (uintmax_t)nofile_limit.rlim_cur < (uintmax_t)SIZE_MAX + && (uintmax_t)val > (uintmax_t)(nofile_limit.rlim_cur - RESERVE_FD_COUNT) + ) { + upsdebugx(1, "Detected soft limit for " + "file descriptor count is %ju", + (uintmax_t)nofile_limit.rlim_cur); + upsdebugx(1, "Detected hard limit for " + "file descriptor count is %ju", + (uintmax_t)nofile_limit.rlim_max); + + max_threads = (size_t)nofile_limit.rlim_cur; + if (max_threads > (RESERVE_FD_COUNT + 1)) { + max_threads -= RESERVE_FD_COUNT; + } + + fprintf(stderr, + "WARNING: Requested max scanning " + "thread count %s (%ld) exceeds the " + "current file descriptor count limit " + "(minus reservation), constraining " + "to %zu\n", + optarg, val, max_threads); + } else +# endif /* HAVE_SYS_RESOURCE_H */ + max_threads = (size_t)val; + } else { + fprintf(stderr, + "WARNING: Requested max scanning " + "thread count %s (%ld) is out of range, " + "using default %zu\n", + optarg, val, max_threads); + } +#else + fprintf(stderr, + "WARNING: Max scanning thread count option " + "is not supported in this build, ignored\n"); +#endif /* HAVE_PTHREAD && ways to limit the thread count */ + } + break; case 'C': allow_all = 1; break; case 'U': - if(!nutscan_avail_usb) { + if (!nutscan_avail_usb) { goto display_help; } allow_usb = 1; break; case 'M': - if(!nutscan_avail_xml_http) { + if (!nutscan_avail_xml_http) { goto display_help; } allow_xml = 1; @@ -241,13 +620,13 @@ int main(int argc, char *argv[]) allow_oldnut = 1; break; case 'A': - if(!nutscan_avail_avahi) { + if (!nutscan_avail_avahi) { goto display_help; } allow_avahi = 1; break; case 'I': - if(!nutscan_avail_ipmi) { + if (!nutscan_avail_ipmi) { goto display_help; } allow_ipmi = 1; @@ -266,209 +645,299 @@ int main(int argc, char *argv[]) exit(EXIT_SUCCESS); case 'a': printf("OLDNUT\n"); - if(nutscan_avail_usb) { + if (nutscan_avail_usb) { printf("USB\n"); } - if(nutscan_avail_snmp) { + if (nutscan_avail_snmp) { printf("SNMP\n"); } - if(nutscan_avail_xml_http) { + if (nutscan_avail_xml_http) { printf("XML\n"); } - if(nutscan_avail_avahi) { + if (nutscan_avail_avahi) { printf("AVAHI\n"); } - if(nutscan_avail_ipmi) { - printf("IPMI\n"); + if (nutscan_avail_ipmi) { + printf("IPMI\n"); } + printf("EATON_SERIAL\n"); exit(EXIT_SUCCESS); case '?': ret_code = ERR_BAD_OPTION; + goto display_help; + /* Fall through to usage and error exit */ case 'h': default: display_help: - puts("nut-scanner : detecting available power devices.\n"); - puts("OPTIONS:"); - printf(" -C, --complete_scan: Scan all available devices (default).\n"); - if( nutscan_avail_usb ) { - printf(" -U, --usb_scan: Scan USB devices.\n"); - } - if( nutscan_avail_snmp ) { - printf(" -S, --snmp_scan: Scan SNMP devices.\n"); - } - if( nutscan_avail_xml_http ) { - printf(" -M, --xml_scan: Scan XML/HTTP devices.\n"); - } - printf(" -O, --oldnut_scan: Scan NUT devices (old method).\n"); - if( nutscan_avail_avahi ) { - printf(" -A, --avahi_scan: Scan NUT devices (avahi method).\n"); - } - if( nutscan_avail_ipmi ) { - printf(" -I, --ipmi_scan: Scan IPMI devices.\n"); - } - printf(" -t, --timeout : network operation timeout (default %d).\n",DEFAULT_TIMEOUT); - printf(" -s, --start_ip : First IP address to scan.\n"); - printf(" -e, --end_ip : Last IP address to scan.\n"); - printf(" -m, --mask_cidr : Give a range of IP using CIDR notation.\n"); - - if( nutscan_avail_snmp ) { - printf("\nSNMP v1 specific options:\n"); - printf(" -c, --community : Set SNMP v1 community name (default = public)\n"); - - printf("\nSNMP v3 specific options:\n"); - printf(" -l, --secLevel : Set the securityLevel used for SNMPv3 messages (allowed values: noAuthNoPriv,authNoPriv,authPriv)\n"); - printf(" -u, --secName : Set the securityName used for authenticated SNMPv3 messages (mandatory if you set secLevel. No default)\n"); - printf(" -w, --authProtocol : Set the authentication protocol (MD5 or SHA) used for authenticated SNMPv3 messages (default=MD5)\n"); - printf(" -W, --authPassword : Set the authentication pass phrase used for authenticated SNMPv3 messages (mandatory if you set secLevel to authNoPriv or authPriv)\n"); - printf(" -x, --privProtocol : Set the privacy protocol (DES or AES) used for encrypted SNMPv3 messages (default=DES)\n"); - printf(" -X, --privPassword : Set the privacy pass phrase used for encrypted SNMPv3 messages (mandatory if you set secLevel to authPriv)\n"); - } - - printf("\nNUT specific options:\n"); - printf(" -p, --port : Port number of remote NUT upsd\n"); - printf("\ndisplay specific options:\n"); - printf(" -N, --disp_nut_conf: Display result in the ups.conf format\n"); - printf(" -P, --disp_parsable: Display result in a parsable format\n"); - printf("\nMiscellaneous options:\n"); - printf(" -V, --version: Display NUT version\n"); - printf(" -a, --available: Display available bus that can be scanned\n"); - printf(" -q, --quiet: Display only scan result. No information on currently scanned bus is displayed.\n"); + show_usage(); + if ((opt_ret != 'h') || (ret_code != EXIT_SUCCESS)) + fprintf(stderr, "\n\n" + "WARNING: Some error has occurred while processing 'nut-scanner' command-line\n" + "arguments, see more details above the usage help text.\n\n"); return ret_code; } - } - if( cidr ) { +#ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE + /* FIXME: Currently sem_init already done on nutscan-init for lib need. + We need to destroy it before re-init. We currently can't change "sem value" + on lib (need to be thread safe). */ + sem_t *current_sem = nutscan_semaphore(); + sem_destroy(current_sem); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + /* Different platforms, different sizes, none fits all... */ + if (SIZE_MAX > UINT_MAX && max_threads > UINT_MAX) { +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic pop +#endif + fprintf(stderr, "\n\n" + "WARNING: Limiting max_threads to range acceptable for sem_init()\n\n"); + max_threads = UINT_MAX - 1; + } + sem_init(current_sem, 0, (unsigned int)max_threads); +# endif +#endif /* HAVE_PTHREAD */ + + if (cidr) { + upsdebugx(1, "Processing CIDR net/mask: %s", cidr); nutscan_cidr_to_ip(cidr, &start_ip, &end_ip); + upsdebugx(1, "Extracted IP address range from CIDR net/mask: %s => %s", start_ip, end_ip); } - if( !allow_usb && !allow_snmp && !allow_xml && !allow_oldnut && - !allow_avahi && !allow_ipmi ) { + if (!allow_usb && !allow_snmp && !allow_xml && !allow_oldnut && + !allow_avahi && !allow_ipmi && !allow_eaton_serial + ) { allow_all = 1; } - if( allow_all ) { + if (allow_all) { allow_usb = 1; allow_snmp = 1; allow_xml = 1; allow_oldnut = 1; allow_avahi = 1; allow_ipmi = 1; + /* BEWARE: allow_all does not include allow_eaton_serial! */ } - if( allow_usb && nutscan_avail_usb ) { - printq(quiet,"Scanning USB bus.\n"); +/* TODO/discuss : Should the #else...#endif code below for lack of pthreads + * during build also serve as a fallback for pthread failure at runtime? + */ + if (allow_usb && nutscan_avail_usb) { + upsdebugx(quiet, "Scanning USB bus."); #ifdef HAVE_PTHREAD - if(pthread_create(&thread[TYPE_USB],NULL,run_usb,NULL)) { + if (pthread_create(&thread[TYPE_USB], NULL, run_usb, NULL)) { + upsdebugx(1, "pthread_create returned an error; disabling this scan mode"); nutscan_avail_usb = 0; } #else + upsdebugx(1, "USB SCAN: no pthread support, starting nutscan_scan_usb..."); dev[TYPE_USB] = nutscan_scan_usb(); #endif /* HAVE_PTHREAD */ + } else { + upsdebugx(1, "USB SCAN: not requested, SKIPPED"); } - if( allow_snmp && nutscan_avail_snmp ) { - if( start_ip == NULL ) { - printq(quiet,"No start IP, skipping SNMP\n"); + if (allow_snmp && nutscan_avail_snmp) { + if (start_ip == NULL) { + upsdebugx(quiet, "No start IP, skipping SNMP"); + nutscan_avail_snmp = 0; } else { - printq(quiet,"Scanning SNMP bus.\n"); + upsdebugx(quiet, "Scanning SNMP bus."); #ifdef HAVE_PTHREAD - if( pthread_create(&thread[TYPE_SNMP],NULL,run_snmp,&sec)) { + upsdebugx(1, "SNMP SCAN: starting pthread_create with run_snmp..."); + if (pthread_create(&thread[TYPE_SNMP], NULL, run_snmp, &snmp_sec)) { + upsdebugx(1, "pthread_create returned an error; disabling this scan mode"); nutscan_avail_snmp = 0; } #else - dev[TYPE_SNMP] = nutscan_scan_snmp(start_ip,end_ip,timeout,&sec); + upsdebugx(1, "SNMP SCAN: no pthread support, starting nutscan_scan_snmp..."); + dev[TYPE_SNMP] = nutscan_scan_snmp(start_ip, end_ip, timeout, &snmp_sec); #endif /* HAVE_PTHREAD */ } + } else { + upsdebugx(1, "SNMP SCAN: not requested, SKIPPED"); } - if( allow_xml && nutscan_avail_xml_http) { - printq(quiet,"Scanning XML/HTTP bus.\n"); + if (allow_xml && nutscan_avail_xml_http) { + upsdebugx(quiet, "Scanning XML/HTTP bus."); + xml_sec.usec_timeout = timeout; #ifdef HAVE_PTHREAD - if(pthread_create(&thread[TYPE_XML],NULL,run_xml,NULL)) { + upsdebugx(1, "XML/HTTP SCAN: starting pthread_create with run_xml..."); + if (pthread_create(&thread[TYPE_XML], NULL, run_xml, &xml_sec)) { + upsdebugx(1, "pthread_create returned an error; disabling this scan mode"); nutscan_avail_xml_http = 0; } #else - dev[TYPE_XML] = nutscan_scan_xml_http(timeout); + upsdebugx(1, "XML/HTTP SCAN: no pthread support, starting nutscan_scan_xml_http_range()..."); + dev[TYPE_XML] = nutscan_scan_xml_http_range(start_ip, end_ip, timeout, &xml_sec); #endif /* HAVE_PTHREAD */ + } else { + upsdebugx(1, "XML/HTTP SCAN: not requested, SKIPPED"); } - if( allow_oldnut && nutscan_avail_nut) { - if( start_ip == NULL ) { - printq(quiet,"No start IP, skipping NUT bus (old connect method)\n"); + if (allow_oldnut && nutscan_avail_nut) { + if (start_ip == NULL) { + upsdebugx(quiet, "No start IP, skipping NUT bus (old connect method)"); + nutscan_avail_nut = 0; } else { - printq(quiet,"Scanning NUT bus (old connect method).\n"); + upsdebugx(quiet, "Scanning NUT bus (old connect method)."); #ifdef HAVE_PTHREAD - if(pthread_create(&thread[TYPE_NUT],NULL,run_nut_old,NULL)) { + upsdebugx(1, "NUT bus (old) SCAN: starting pthread_create with run_nut_old..."); + if (pthread_create(&thread[TYPE_NUT], NULL, run_nut_old, NULL)) { + upsdebugx(1, "pthread_create returned an error; disabling this scan mode"); nutscan_avail_nut = 0; } #else - dev[TYPE_NUT] = nutscan_scan_nut(start_ip,end_ip,port,timeout); + upsdebugx(1, "NUT bus (old) SCAN: no pthread support, starting nutscan_scan_nut..."); + dev[TYPE_NUT] = nutscan_scan_nut(start_ip, end_ip, port, timeout); #endif /* HAVE_PTHREAD */ } + } else { + upsdebugx(1, "NUT bus (old) SCAN: not requested, SKIPPED"); } - if( allow_avahi && nutscan_avail_avahi) { - printq(quiet,"Scanning NUT bus (avahi method).\n"); + if (allow_avahi && nutscan_avail_avahi) { + upsdebugx(quiet, "Scanning NUT bus (avahi method)."); #ifdef HAVE_PTHREAD - if(pthread_create(&thread[TYPE_AVAHI],NULL,run_avahi,NULL)) { + upsdebugx(1, "NUT bus (avahi) SCAN: starting pthread_create with run_avahi..."); + if (pthread_create(&thread[TYPE_AVAHI], NULL, run_avahi, NULL)) { + upsdebugx(1, "pthread_create returned an error; disabling this scan mode"); nutscan_avail_avahi = 0; } #else + upsdebugx(1, "NUT bus (avahi) SCAN: no pthread support, starting nutscan_scan_avahi..."); dev[TYPE_AVAHI] = nutscan_scan_avahi(timeout); #endif /* HAVE_PTHREAD */ + } else { + upsdebugx(1, "NUT bus (avahi) SCAN: not requested, SKIPPED"); } - if( allow_ipmi && nutscan_avail_ipmi) { - printq(quiet,"Scanning IPMI bus.\n"); + if (allow_ipmi && nutscan_avail_ipmi) { + upsdebugx(quiet, "Scanning IPMI bus."); #ifdef HAVE_PTHREAD - if(pthread_create(&thread[TYPE_IPMI],NULL,run_ipmi,NULL)) { + upsdebugx(1, "IPMI SCAN: starting pthread_create with run_ipmi..."); + if (pthread_create(&thread[TYPE_IPMI], NULL, run_ipmi, &ipmi_sec)) { + upsdebugx(1, "pthread_create returned an error; disabling this scan mode"); nutscan_avail_ipmi = 0; } #else - dev[TYPE_IPMI] = nutscan_scan_ipmi(); + upsdebugx(1, "IPMI SCAN: no pthread support, starting nutscan_scan_ipmi..."); + dev[TYPE_IPMI] = nutscan_scan_ipmi(start_ip, end_ip, &ipmi_sec); #endif /* HAVE_PTHREAD */ + } else { + upsdebugx(1, "IPMI SCAN: not requested, SKIPPED"); + } + + /* Eaton serial scan */ + if (allow_eaton_serial) { + upsdebugx(quiet, "Scanning serial bus for Eaton devices."); +#ifdef HAVE_PTHREAD + upsdebugx(1, "SERIAL SCAN: starting pthread_create with run_eaton_serial (return not checked!)..."); + pthread_create(&thread[TYPE_EATON_SERIAL], NULL, run_eaton_serial, serial_ports); + /* FIXME: check return code */ + /* upsdebugx(1, "pthread_create returned an error; disabling this scan mode"); */ + /* nutscan_avail_eaton_serial(?) = 0; */ +#else + upsdebugx(1, "SERIAL SCAN: no pthread support, starting nutscan_scan_eaton_serial..."); + dev[TYPE_EATON_SERIAL] = nutscan_scan_eaton_serial (serial_ports); +#endif /* HAVE_PTHREAD */ + } else { + upsdebugx(1, "SERIAL SCAN: not requested, SKIPPED"); } #ifdef HAVE_PTHREAD - if( allow_usb && nutscan_avail_usb ) { - pthread_join(thread[TYPE_USB],NULL); + if (allow_usb && nutscan_avail_usb && thread[TYPE_USB]) { + upsdebugx(1, "USB SCAN: join back the pthread"); + pthread_join(thread[TYPE_USB], NULL); } - if( allow_snmp && nutscan_avail_snmp ) { - pthread_join(thread[TYPE_SNMP],NULL); + if (allow_snmp && nutscan_avail_snmp && thread[TYPE_SNMP]) { + upsdebugx(1, "SNMP SCAN: join back the pthread"); + pthread_join(thread[TYPE_SNMP], NULL); } - if( allow_xml && nutscan_avail_xml_http ) { - pthread_join(thread[TYPE_XML],NULL); + if (allow_xml && nutscan_avail_xml_http && thread[TYPE_XML]) { + upsdebugx(1, "XML/HTTP SCAN: join back the pthread"); + pthread_join(thread[TYPE_XML], NULL); } - if( allow_oldnut && nutscan_avail_nut ) { - pthread_join(thread[TYPE_NUT],NULL); + if (allow_oldnut && nutscan_avail_nut && thread[TYPE_NUT]) { + upsdebugx(1, "NUT bus (old) SCAN: join back the pthread"); + pthread_join(thread[TYPE_NUT], NULL); } - if( allow_avahi && nutscan_avail_avahi ) { - pthread_join(thread[TYPE_AVAHI],NULL); + if (allow_avahi && nutscan_avail_avahi && thread[TYPE_AVAHI]) { + upsdebugx(1, "NUT bus (avahi) SCAN: join back the pthread"); + pthread_join(thread[TYPE_AVAHI], NULL); } - if( allow_ipmi && nutscan_avail_ipmi ) { - pthread_join(thread[TYPE_IPMI],NULL); + if (allow_ipmi && nutscan_avail_ipmi && thread[TYPE_IPMI]) { + upsdebugx(1, "IPMI SCAN: join back the pthread"); + pthread_join(thread[TYPE_IPMI], NULL); + } + if (allow_eaton_serial && thread[TYPE_EATON_SERIAL]) { + upsdebugx(1, "SERIAL SCAN: join back the pthread"); + pthread_join(thread[TYPE_EATON_SERIAL], NULL); } #endif /* HAVE_PTHREAD */ + upsdebugx(1, "SCANS DONE: display results"); + + upsdebugx(1, "SCANS DONE: display results: USB"); display_func(dev[TYPE_USB]); + upsdebugx(1, "SCANS DONE: free resources: USB"); nutscan_free_device(dev[TYPE_USB]); + upsdebugx(1, "SCANS DONE: display results: SNMP"); display_func(dev[TYPE_SNMP]); + upsdebugx(1, "SCANS DONE: free resources: SNMP"); nutscan_free_device(dev[TYPE_SNMP]); + upsdebugx(1, "SCANS DONE: display results: XML/HTTP"); display_func(dev[TYPE_XML]); + upsdebugx(1, "SCANS DONE: free resources: XML/HTTP"); nutscan_free_device(dev[TYPE_XML]); + upsdebugx(1, "SCANS DONE: display results: NUT bus (old)"); display_func(dev[TYPE_NUT]); + upsdebugx(1, "SCANS DONE: free resources: NUT bus (old)"); nutscan_free_device(dev[TYPE_NUT]); + upsdebugx(1, "SCANS DONE: display results: NUT bus (avahi)"); display_func(dev[TYPE_AVAHI]); + upsdebugx(1, "SCANS DONE: free resources: NUT bus (avahi)"); nutscan_free_device(dev[TYPE_AVAHI]); + upsdebugx(1, "SCANS DONE: display results: IPMI"); display_func(dev[TYPE_IPMI]); + upsdebugx(1, "SCANS DONE: free resources: IPMI"); nutscan_free_device(dev[TYPE_IPMI]); + + upsdebugx(1, "SCANS DONE: display results: SERIAL"); + display_func(dev[TYPE_EATON_SERIAL]); + upsdebugx(1, "SCANS DONE: free resources: SERIAL"); + nutscan_free_device(dev[TYPE_EATON_SERIAL]); + +#ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE + sem_destroy(nutscan_semaphore()); +# endif +#endif + + upsdebugx(1, "SCANS DONE: free common scanner resources"); + nutscan_free(); + + upsdebugx(1, "SCANS DONE: EXIT_SUCCESS"); return EXIT_SUCCESS; } diff --git a/tools/nut-scanner/nutscan-device.c b/tools/nut-scanner/nutscan-device.c index b536398..bde57fe 100644 --- a/tools/nut-scanner/nutscan-device.c +++ b/tools/nut-scanner/nutscan-device.c @@ -1,6 +1,5 @@ -/* device.c: manipulation of a container describing a NUT device - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) 2011 - EATON * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,20 +15,38 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/*! \file nutscan-device.c + \brief manipulation of a container describing a NUT device + \author Frederic Bohe +*/ +#include "config.h" /* must be the first header */ + #include "nutscan-device.h" #include #include +#include + +const char * nutscan_device_type_strings[TYPE_END - 1] = { + "USB", + "SNMP", + "XML", + "NUT", + "IPMI", + "Avahi", + "serial", +}; nutscan_device_t * nutscan_new_device() { nutscan_device_t * device; device = malloc(sizeof(nutscan_device_t)); - if( device==NULL) { + if (device == NULL) { return NULL; } - memset(device,0,sizeof(nutscan_device_t)); + memset(device, 0, sizeof(nutscan_device_t)); return device; } @@ -37,46 +54,36 @@ nutscan_device_t * nutscan_new_device() static void deep_free_device(nutscan_device_t * device) { nutscan_options_t * current; - nutscan_options_t * old; - if(device==NULL) { + if (device == NULL) { return; } - if(device->driver) { + if (device->driver) { free(device->driver); } - if(device->port) { + if (device->port) { free(device->port); } - current = &device->opt; + while (device->opt != NULL) { + current = device->opt; + device->opt = current->next; - if(current->option != NULL) { - free(current->option); - } - - if(current->value != NULL) { - free(current->value); - } - - current = current->next; - while (current != NULL) { - if(current->option != NULL) { + if (current->option != NULL) { free(current->option); } - if(current->value != NULL) { + if (current->value != NULL) { free(current->value); } - old = current; - current = current->next; - free(old); - }; - if(device->prev) { + free(current); + } + + if (device->prev) { device->prev->next = device->next; } - if(device->next) { + if (device->next) { device->next->prev = device->prev; } @@ -85,87 +92,87 @@ static void deep_free_device(nutscan_device_t * device) void nutscan_free_device(nutscan_device_t * device) { - if(device==NULL) { + if (device == NULL) { return; } - while(device->prev != NULL) { + while (device->prev != NULL) { deep_free_device(device->prev); } - while(device->next != NULL) { + while (device->next != NULL) { deep_free_device(device->next); } - free(device); + deep_free_device(device); } -void nutscan_add_option_to_device(nutscan_device_t * device,char * option, char * value) +void nutscan_add_option_to_device(nutscan_device_t * device, char * option, char * value) { - nutscan_options_t * opt; + nutscan_options_t **opt; - opt = &(device->opt); /* search for last entry */ - if( opt->option != NULL ) { - while( opt->next != NULL ) { - opt = opt->next; - } + opt = &device->opt; - opt->next = malloc(sizeof(nutscan_options_t)); - opt = opt->next; - memset(opt,0,sizeof(nutscan_options_t)); - } + while (NULL != *opt) + opt = &(*opt)->next; - if( option != NULL ) { - opt->option = strdup(option); + *opt = (nutscan_options_t *)malloc(sizeof(nutscan_options_t)); + + /* TBD: A gracefull way to propagate memory failure would be nice */ + assert(NULL != *opt); + + memset(*opt, 0, sizeof(nutscan_options_t)); + + if (option != NULL) { + (*opt)->option = strdup(option); } else { - opt->option = NULL; + (*opt)->option = NULL; } - if( value != NULL ) { - opt->value = strdup(value); + if (value != NULL) { + (*opt)->value = strdup(value); } else { - opt->value = NULL; + (*opt)->value = NULL; } } nutscan_device_t * nutscan_add_device_to_device(nutscan_device_t * first, nutscan_device_t * second) { - nutscan_device_t * dev1=NULL; - nutscan_device_t * dev2=NULL; + nutscan_device_t * dev1 = NULL; + nutscan_device_t * dev2 = NULL; /* Get end of first device */ - if( first != NULL) { + if (first != NULL) { dev1 = first; - while(dev1->next != NULL) { + while (dev1->next != NULL) { dev1 = dev1->next; } } else { - if( second == NULL ) { + if (second == NULL) { return NULL; } /* return end of second */ dev2 = second; - while(dev2->next != NULL) { + while (dev2->next != NULL) { dev2 = dev2->next; } return dev2; } /* Get start of second */ - if( second != NULL ) { + if (second != NULL) { dev2 = second; - while(dev2->prev != NULL) { + while (dev2->prev != NULL) { dev2 = dev2->prev; } } else { /* return end of first */ dev1 = first; - while(dev1->next != NULL) { + while (dev1->next != NULL) { dev1 = dev1->next; - } return dev1; } @@ -175,9 +182,20 @@ nutscan_device_t * nutscan_add_device_to_device(nutscan_device_t * first, nutsca dev2->prev = dev1; /* return end of both */ - while(dev2->next != NULL) { - dev2 = dev2->next; + while (dev2->next != NULL) { + dev2 = dev2->next; } return dev2; } + +nutscan_device_t * nutscan_rewind_device(nutscan_device_t * device) +{ + if (NULL == device) + return NULL; + + while (NULL != device->prev) + device = device->prev; + + return device; +} diff --git a/tools/nut-scanner/nutscan-device.h b/tools/nut-scanner/nutscan-device.h index d96f9f3..9023394 100644 --- a/tools/nut-scanner/nutscan-device.h +++ b/tools/nut-scanner/nutscan-device.h @@ -1,6 +1,5 @@ -/* device.h: definition of a container describing a NUT device - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) 2011 - EATON * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,20 +15,46 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/*! \file nutscan-device.h + \brief definition of a container describing a NUT discovered device + \author Frederic Bohe +*/ + #ifndef SCAN_DEVICE #define SCAN_DEVICE +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + +/** + * \brief Device type string getter + * + * \param type Device type + * + * \return Type string + */ +#define nutscan_device_type_string(type) \ + (assert(0 < (type) && (type) < TYPE_END), nutscan_device_type_strings[type - 1]) + typedef enum nutscan_device_type { - TYPE_NONE=0, + TYPE_NONE = 0, TYPE_USB, TYPE_SNMP, TYPE_XML, TYPE_NUT, TYPE_IPMI, TYPE_AVAHI, + TYPE_EATON_SERIAL, TYPE_END } nutscan_device_type_t; +/** Device type -> string mapping */ +extern const char * nutscan_device_type_strings[TYPE_END - 1]; + typedef struct nutscan_options { char * option; char * value; @@ -40,13 +65,29 @@ typedef struct nutscan_device { nutscan_device_type_t type; char * driver; char * port; - nutscan_options_t opt; + nutscan_options_t * opt; struct nutscan_device * prev; struct nutscan_device * next; } nutscan_device_t; -nutscan_device_t * nutscan_new_device(); +nutscan_device_t * nutscan_new_device(void); void nutscan_free_device(nutscan_device_t * device); -void nutscan_add_option_to_device(nutscan_device_t * device,char * option, char * value); +void nutscan_add_option_to_device(nutscan_device_t * device, char * option, char * value); nutscan_device_t * nutscan_add_device_to_device(nutscan_device_t * first, nutscan_device_t * second); + +/** + * \brief Rewind device list + * + * \param device Device list item + * + * \return Device list head + */ +nutscan_device_t * nutscan_rewind_device(nutscan_device_t * device); + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + #endif diff --git a/tools/nut-scanner/nutscan-display.c b/tools/nut-scanner/nutscan-display.c index 1cbbfc8..71f2248 100644 --- a/tools/nut-scanner/nutscan-display.c +++ b/tools/nut-scanner/nutscan-display.c @@ -1,6 +1,5 @@ -/* display.c: format and display scanned devices - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) 2011 - EATON * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,18 +16,26 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/*! \file nutscan-display.c + \brief format and display scanned devices + \author Frederic Bohe +*/ + #include "common.h" #include #include "nutscan-device.h" +#include "nut-scan.h" -char nutscan_device_type_string[TYPE_END][6] = { - "NONE", - "USB", - "SNMP", - "XML", - "NUT", - "IPMI", - "AVAHI" }; +static char * nutscan_device_type_string[TYPE_END] = { + "NONE", + "USB", + "SNMP", + "XML", + "NUT", + "IPMI", + "AVAHI", + "EATON_SERIAL" +}; void nutscan_display_ups_conf(nutscan_device_t * device) { @@ -36,39 +43,39 @@ void nutscan_display_ups_conf(nutscan_device_t * device) nutscan_options_t * opt; static int nutdev_num = 1; - if(device==NULL) { + if (device == NULL) { return; } /* Find start of the list */ - while(current_dev->prev != NULL) { + while (current_dev->prev != NULL) { current_dev = current_dev->prev; } - /* Display each devices */ + /* Display each device */ do { printf("[nutdev%i]\n\tdriver = \"%s\"\n\tport = \"%s\"\n", nutdev_num, current_dev->driver, current_dev->port); - opt = &(current_dev->opt); + opt = current_dev->opt; - do { - if( opt->option != NULL ) { - printf("\t%s",opt->option); - if( opt->value != NULL ) { + while (NULL != opt) { + if (opt->option != NULL) { + printf("\t%s", opt->option); + if (opt->value != NULL) { printf(" = \"%s\"", opt->value); } printf("\n"); } opt = opt->next; - } while( opt != NULL ); + } nutdev_num++; current_dev = current_dev->next; } - while( current_dev != NULL ); + while (current_dev != NULL); } void nutscan_display_parsable(nutscan_device_t * device) @@ -76,37 +83,39 @@ void nutscan_display_parsable(nutscan_device_t * device) nutscan_device_t * current_dev = device; nutscan_options_t * opt; - if(device==NULL) { + if (device == NULL) { return; } /* Find start of the list */ - while(current_dev->prev != NULL) { + while (current_dev->prev != NULL) { current_dev = current_dev->prev; } - /* Display each devices */ + /* Display each device */ do { + /* Do not separate by whitespace, in case someone already parses this */ printf("%s:driver=\"%s\",port=\"%s\"", nutscan_device_type_string[current_dev->type], current_dev->driver, current_dev->port); - opt = &(current_dev->opt); + opt = current_dev->opt; - do { - if( opt->option != NULL ) { - printf(",%s",opt->option); - if( opt->value != NULL ) { + while (NULL != opt) { + if (opt->option != NULL) { + /* Do not separate by whitespace, in case someone already parses this */ + printf(",%s", opt->option); + if (opt->value != NULL) { printf("=\"%s\"", opt->value); } } opt = opt->next; - } while( opt != NULL ); + } + printf("\n"); current_dev = current_dev->next; } - while( current_dev != NULL ); + while (current_dev != NULL); } - diff --git a/tools/nut-scanner/nutscan-init.c b/tools/nut-scanner/nutscan-init.c index f668546..9ea3f5d 100644 --- a/tools/nut-scanner/nutscan-init.c +++ b/tools/nut-scanner/nutscan-init.c @@ -1,6 +1,5 @@ -/* nutscan-init.c: init functions for nut scanner library - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) 2011-2021 - EATON * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,36 +16,204 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/*! \file nutscan-init.c + \brief init functions for nut scanner library + \author Frederic Bohe + \author Arnaud Quette +*/ + #include "common.h" +#include "nutscan-init.h" +#include +#include +#include +#include +#include "nut-scan.h" int nutscan_avail_avahi = 0; int nutscan_avail_ipmi = 0; -int nutscan_avail_nut = 1; +int nutscan_avail_nut = 0; int nutscan_avail_snmp = 0; int nutscan_avail_usb = 0; int nutscan_avail_xml_http = 0; -int nutscan_load_usb_library(void); -int nutscan_load_snmp_library(void); -int nutscan_load_neon_library(void); -int nutscan_load_avahi_library(void); -int nutscan_load_ipmi_library(void); +int nutscan_load_usb_library(const char *libname_path); +int nutscan_load_snmp_library(const char *libname_path); +int nutscan_load_neon_library(const char *libname_path); +int nutscan_load_avahi_library(const char *libname_path); +int nutscan_load_ipmi_library(const char *libname_path); +int nutscan_load_upsclient_library(const char *libname_path); + +#ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE +/* Shared by library consumers, exposed by nutscan_semaphore() below */ +static sem_t semaphore; + +sem_t * nutscan_semaphore(void) +{ + return &semaphore; +} +# endif /* HAVE_SEMAPHORE */ + +# ifdef HAVE_PTHREAD_TRYJOIN +pthread_mutex_t threadcount_mutex; +# endif + +# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) +/* We have 3 networked scan types: nut, snmp, xml, + * and users typically give their /24 subnet as "-m" arg. + * With some systems having a 1024 default (u)limit to + * file descriptors, this should fit if those are involved. + * On some systems tested, a large amount of not-joined + * pthreads did cause various crashes; also RAM is limited. + * Note that each scan may be time consuming to query an + * IP address and wait for (no) reply, so while these threads + * are usually not resource-intensive (nor computationally), + * they spend much wallclock time each so parallelism helps. + */ +size_t max_threads = DEFAULT_THREAD; +size_t curr_threads = 0; + +size_t max_threads_netxml = 1021; /* experimental finding, see PR#1158 */ +size_t max_threads_oldnut = 1021; +size_t max_threads_netsnmp = 0; /* 10240; */ + /* per reports in PR#1158, some versions of net-snmp could be limited + * to 1024 threads in the past; this was not found in practice. + * Still, some practical limit can be useful (configurable?) + * Here 0 means to not apply any special limit (beside max_threads). + */ + +# endif /* HAVE_PTHREAD_TRYJOIN || HAVE_SEMAPHORE */ + +#endif /* HAVE_PTHREAD */ void nutscan_init(void) { +#ifdef HAVE_PTHREAD +/* TOTHINK: Should semaphores to limit thread count + * and the more naive but portable methods be an + * if-else proposition? At least when initializing? + */ +# ifdef HAVE_SEMAPHORE + /* NOTE: This semaphore may get re-initialized in nut-scanner program + * after parsing command-line arguments. It calls nutscan_init() before + * parsing CLI, to know about available libs and to set defaults below. + */ +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + /* Different platforms, different sizes, none fits all... */ + if (SIZE_MAX > UINT_MAX && max_threads > UINT_MAX) { +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic pop +#endif + upsdebugx(1, + "WARNING: %s: Limiting max_threads to range acceptable for sem_init()", + __func__); + max_threads = UINT_MAX - 1; + } + sem_init(&semaphore, 0, (unsigned int)max_threads); +# endif + +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_init(&threadcount_mutex, NULL); +# endif +#endif /* HAVE_PTHREAD */ + + char *libname = NULL; #ifdef WITH_USB - nutscan_avail_usb = nutscan_load_usb_library(); + #if WITH_LIBUSB_1_0 + libname = get_libname("libusb-1.0.so"); + #else + libname = get_libname("libusb-0.1.so"); + if (!libname) { + /* We can also use libusb-compat from newer libusb-1.0 releases */ + libname = get_libname("libusb.so"); + } + #endif + if (libname) { + nutscan_avail_usb = nutscan_load_usb_library(libname); + free(libname); + } #endif #ifdef WITH_SNMP - nutscan_avail_snmp = nutscan_load_snmp_library(); + libname = get_libname("libnetsnmp.so"); + if (libname) { + nutscan_avail_snmp = nutscan_load_snmp_library(libname); + free(libname); + } #endif #ifdef WITH_NEON - nutscan_avail_xml_http = nutscan_load_neon_library(); + libname = get_libname("libneon.so"); + if (!libname) { + libname = get_libname("libneon-gnutls.so"); + } + if (libname) { + nutscan_avail_xml_http = nutscan_load_neon_library(libname); + free(libname); + } #endif #ifdef WITH_AVAHI - nutscan_avail_avahi = nutscan_load_avahi_library(); + libname = get_libname("libavahi-client.so"); + if (libname) { + nutscan_avail_avahi = nutscan_load_avahi_library(libname); + free(libname); + } #endif #ifdef WITH_FREEIPMI - nutscan_avail_ipmi = nutscan_load_ipmi_library(); + libname = get_libname("libfreeipmi.so"); + if (libname) { + nutscan_avail_ipmi = nutscan_load_ipmi_library(libname); + free(libname); + } #endif + libname = get_libname("libupsclient.so"); + if (libname) { + nutscan_avail_nut = nutscan_load_upsclient_library(libname); + free(libname); + } +} + +void nutscan_free(void) +{ + if (nutscan_avail_usb) { + lt_dlexit(); + } + if (nutscan_avail_snmp) { + lt_dlexit(); + } + if (nutscan_avail_xml_http) { + lt_dlexit(); + } + if (nutscan_avail_avahi) { + lt_dlexit(); + } + if (nutscan_avail_ipmi) { + lt_dlexit(); + } + if (nutscan_avail_nut) { + lt_dlexit(); + } + +#ifdef HAVE_PTHREAD +/* TOTHINK: See comments near mutex/semaphore init code above */ +# ifdef HAVE_SEMAPHORE + sem_destroy(nutscan_semaphore()); +# endif + +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_destroy(&threadcount_mutex); +# endif +#endif + } diff --git a/tools/nut-scanner/nutscan-init.h b/tools/nut-scanner/nutscan-init.h index e952e6c..4ff5f39 100644 --- a/tools/nut-scanner/nutscan-init.h +++ b/tools/nut-scanner/nutscan-init.h @@ -1,6 +1,5 @@ -/* nutscan-init.h: initialisation data - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) 2011 - EATON * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,9 +15,21 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/*! \file nutscan-init.h + \brief initialisation data + \author Frederic Bohe +*/ + #ifndef SCAN_INIT #define SCAN_INIT +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + extern int nutscan_avail_avahi; extern int nutscan_avail_ipmi; extern int nutscan_avail_nut; @@ -27,4 +38,14 @@ extern int nutscan_avail_usb; extern int nutscan_avail_xml_http; void nutscan_init(void); +void nutscan_free(void); + +#define DEFAULT_THREAD 512 + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + #endif diff --git a/tools/nut-scanner/nutscan-ip.c b/tools/nut-scanner/nutscan-ip.c index 8788018..f064534 100644 --- a/tools/nut-scanner/nutscan-ip.c +++ b/tools/nut-scanner/nutscan-ip.c @@ -1,6 +1,5 @@ -/* ip.c: iterator for IPv4 or IPv6 addresses - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) 2011 - EATON * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,6 +16,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/*! \file nutscan-ip.c + \brief iterator for IPv4 or IPv6 addresses + \author Frederic Bohe +*/ + +#include "config.h" /* must be first */ + #include "nutscan-ip.h" #include #include "common.h" @@ -28,9 +34,9 @@ static void increment_IPv6(struct in6_addr * addr) { int i; - for( i=15 ; i>= 0 ; i--) { + for (i = 15 ; i >= 0 ; i--) { addr->s6_addr[i]++; - if( addr->s6_addr[i] != 0) { + if (addr->s6_addr[i] != 0) { break; } } @@ -40,37 +46,39 @@ static void invert_IPv6(struct in6_addr * addr1, struct in6_addr * addr2) { struct in6_addr addr; - memcpy(addr.s6_addr,addr1->s6_addr,sizeof(addr.s6_addr)); - memcpy(addr1->s6_addr,addr2->s6_addr,sizeof(addr.s6_addr)); - memcpy(addr2->s6_addr,addr.s6_addr,sizeof(addr.s6_addr)); + memcpy(addr.s6_addr, addr1->s6_addr, sizeof(addr.s6_addr)); + memcpy(addr1->s6_addr, addr2->s6_addr, sizeof(addr.s6_addr)); + memcpy(addr2->s6_addr, addr.s6_addr, sizeof(addr.s6_addr)); } -static int ntop( struct in_addr * ip, char * host, size_t host_size) +static int ntop(struct in_addr * ip, char * host, GETNAMEINFO_TYPE_ARG46 host_size) { struct sockaddr_in in; - memset(&in,0,sizeof(struct sockaddr_in)); + memset(&in, 0, sizeof(struct sockaddr_in)); in.sin_addr = *ip; in.sin_family = AF_INET; - return getnameinfo((struct sockaddr *)&in, - sizeof(struct sockaddr_in), - host,host_size,NULL,0,NI_NUMERICHOST); + return getnameinfo( + (struct sockaddr *)&in, + sizeof(struct sockaddr_in), + host, host_size, NULL, 0, NI_NUMERICHOST); } -static int ntop6( struct in6_addr * ip, char * host, size_t host_size) +static int ntop6(struct in6_addr * ip, char * host, GETNAMEINFO_TYPE_ARG46 host_size) { struct sockaddr_in6 in6; - memset(&in6,0,sizeof(struct sockaddr_in6)); - memcpy( &in6.sin6_addr, ip, sizeof(struct in6_addr) ); + memset(&in6, 0, sizeof(struct sockaddr_in6)); + memcpy(&in6.sin6_addr, ip, sizeof(struct in6_addr)); in6.sin6_family = AF_INET6; - return getnameinfo((struct sockaddr *)&in6, - sizeof(struct sockaddr_in6), - host,host_size,NULL,0,NI_NUMERICHOST); + return getnameinfo( + (struct sockaddr *)&in6, + sizeof(struct sockaddr_in6), + host, host_size, NULL, 0, NI_NUMERICHOST); } /* Return the first ip or NULL if error */ char * nutscan_ip_iter_init(nutscan_ip_iter_t * ip, const char * startIP, const char * stopIP) { - int addr; + uint32_t addr; /* 32-bit IPv4 address */ int i; struct addrinfo hints; struct addrinfo *res; @@ -78,92 +86,127 @@ char * nutscan_ip_iter_init(nutscan_ip_iter_t * ip, const char * startIP, const struct sockaddr_in6 * s_in6; char host[SMALLBUF]; - if( startIP == NULL ) { + if (startIP == NULL) { return NULL; } - if(stopIP == NULL ) { + if (stopIP == NULL) { stopIP = startIP; } - memset(&hints,0,sizeof(struct addrinfo)); + memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; ip->type = IPv4; /* Detecting IPv4 vs IPv6 */ - if(getaddrinfo(startIP,NULL,&hints,&res) != 0) { + if (getaddrinfo(startIP, NULL, &hints, &res) != 0) { /*Try IPv6 detection */ ip->type = IPv6; hints.ai_family = AF_INET6; - if(getaddrinfo(startIP,NULL,&hints,&res) != 0) { - fprintf(stderr,"Invalid address : %s\n",startIP); + if (getaddrinfo(startIP, NULL, &hints, &res) != 0) { + fprintf(stderr, "Invalid address : %s\n", startIP); return NULL; } - + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-align" +#endif + /* Note: we receive a pointer to res above, so have + * no control about alignment of its further data */ s_in6 = (struct sockaddr_in6 *)res->ai_addr; - memcpy(&ip->start6,&s_in6->sin6_addr,sizeof(struct in6_addr)); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic pop +#endif + memcpy(&ip->start6, &s_in6->sin6_addr, sizeof(struct in6_addr)); freeaddrinfo(res); } else { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-align" +#endif + /* Note: we receive a pointer to res above, so have + * no control about alignment of its further data */ s_in = (struct sockaddr_in *)res->ai_addr; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic pop +#endif ip->start = s_in->sin_addr; freeaddrinfo(res); } /* Compute stop IP */ - if( ip->type == IPv4 ) { + if (ip->type == IPv4) { hints.ai_family = AF_INET; - if(getaddrinfo(stopIP,NULL,&hints,&res) != 0) { - fprintf(stderr,"Invalid address : %s\n",stopIP); - return NULL; - } + if (getaddrinfo(stopIP, NULL, &hints, &res) != 0) { + fprintf(stderr, "Invalid address : %s\n", stopIP); + return NULL; + } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-align" +#endif + /* Note: we receive a pointer to res above, so have + * no control about alignment of its further data */ s_in = (struct sockaddr_in *)res->ai_addr; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic pop +#endif ip->stop = s_in->sin_addr; freeaddrinfo(res); - } - else { + } + else { hints.ai_family = AF_INET6; - if(getaddrinfo(stopIP,NULL,&hints,&res) != 0) { - fprintf(stderr,"Invalid address : %s\n",stopIP); - return NULL; - } + if (getaddrinfo(stopIP, NULL, &hints, &res) != 0) { + fprintf(stderr, "Invalid address : %s\n", stopIP); + return NULL; + } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-align" +#endif + /* Note: we receive a pointer to res above, so have + * no control about alignment of its further data */ s_in6 = (struct sockaddr_in6 *)res->ai_addr; - memcpy(&ip->stop6,&s_in6->sin6_addr,sizeof(struct in6_addr)); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic pop +#endif + memcpy(&ip->stop6, &s_in6->sin6_addr, sizeof(struct in6_addr)); freeaddrinfo(res); - } + } - /* Make sure start IP is lesser than stop IP */ - if( ip->type == IPv4 ) { - if( ntohl(ip->start.s_addr) > ntohl(ip->stop.s_addr) ) { - addr = ip->start.s_addr; - ip->start.s_addr = ip->stop.s_addr; - ip->stop.s_addr = addr; - } + /* Make sure start IP is lesser than stop IP */ + if (ip->type == IPv4) { + if (ntohl(ip->start.s_addr) > ntohl(ip->stop.s_addr)) { + addr = ip->start.s_addr; + ip->start.s_addr = ip->stop.s_addr; + ip->stop.s_addr = addr; + } - if( ntop(&ip->start, host, sizeof(host)) != 0 ) { + if (ntop(&ip->start, host, sizeof(host)) != 0) { return NULL; } return strdup(host); - } - else { /* IPv6 */ - for( i=0; i<16; i++ ) { - if( ip->start6.s6_addr[i] !=ip->stop6.s6_addr[i] ) { - if(ip->start6.s6_addr[i]>ip->stop6.s6_addr[i]){ - invert_IPv6(&ip->start6,&ip->stop6); - } - break; - } - } + } + else { /* IPv6 */ + for (i = 0; i < 16; i++) { + if (ip->start6.s6_addr[i] !=ip->stop6.s6_addr[i]) { + if (ip->start6.s6_addr[i] > ip->stop6.s6_addr[i]) { + invert_IPv6(&ip->start6, &ip->stop6); + } + break; + } + } - if( ntop6(&ip->start6, host, sizeof(host)) != 0 ) { + if (ntop6(&ip->start6, host, sizeof(host)) != 0) { return NULL; } return strdup(host); - } - + } } @@ -174,30 +217,30 @@ char * nutscan_ip_iter_inc(nutscan_ip_iter_t * ip) { char host[SMALLBUF]; - if( ip->type == IPv4 ) { + if (ip->type == IPv4) { /* Check if this is the last address to scan */ - if(ip->start.s_addr == ip->stop.s_addr) { + if (ip->start.s_addr == ip->stop.s_addr) { return NULL; } /* increment the address (need to pass address in host byte order, then pass back in network byte order */ - ip->start.s_addr = htonl((ntohl(ip->start.s_addr)+1)); + ip->start.s_addr = htonl((ntohl(ip->start.s_addr) + 1)); - if( ntop(&ip->start, host, sizeof(host)) != 0 ) { + if (ntop(&ip->start, host, sizeof(host)) != 0) { return NULL; } - + return strdup(host); } else { /* Check if this is the last address to scan */ - if( memcmp(&ip->start6.s6_addr, &ip->stop6.s6_addr, - sizeof(ip->start6.s6_addr)) == 0 ) { + if (memcmp(&ip->start6.s6_addr, &ip->stop6.s6_addr, + sizeof(ip->start6.s6_addr)) == 0) { return NULL; } increment_IPv6(&ip->start6); - if( ntop6(&ip->start6, host, sizeof(host)) != 0 ) { + if (ntop6(&ip->start6, host, sizeof(host)) != 0) { return NULL; } @@ -214,7 +257,7 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) nutscan_ip_iter_t ip; int mask_val; int mask_byte; - unsigned long mask_bit; + uint32_t mask_bit; /* 32-bit IPv4 address bitmask */ char host[SMALLBUF]; struct addrinfo hints; struct addrinfo *res; @@ -225,47 +268,87 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) *stop_ip = NULL; cidr_tok = strdup(cidr); - first_ip = strdup(strtok_r(cidr_tok,"/",&saveptr)); - if( first_ip == NULL) { + first_ip = strdup(strtok_r(cidr_tok, "/", &saveptr)); + if (first_ip == NULL) { + upsdebugx(0, "WARNING: %s failed to parse first_ip from cidr=%s", + __func__, cidr); + free(cidr_tok); return 0; } - mask = strtok_r(NULL,"/",&saveptr); - if( mask == NULL ) { + mask = strtok_r(NULL, "/", &saveptr); + if (mask == NULL) { + upsdebugx(0, "WARNING: %s failed to parse mask from cidr=%s (first_ip=%s)", + __func__, cidr, first_ip); + free (first_ip); + free(cidr_tok); return 0; } - free(cidr_tok); + upsdebugx(5, "%s: parsed cidr=%s into first_ip=%s and mask=%s", + __func__, cidr, first_ip, mask); mask_val = atoi(mask); + upsdebugx(5, "%s: parsed mask value %d", + __func__, mask_val); - /* Detecting IPv4 vs IPv6 */ - memset(&hints,0,sizeof(struct addrinfo)); + /* NOTE: Sanity-wise, some larger number also makes sense + * as the maximum subnet size we would scan. But at least, + * this helps avoid scanning the whole Internet just due + * to string-parsing errors. + */ + if (mask_val < 1) { + fatalx(EXIT_FAILURE, "Bad netmask: %s", mask); + } + + /* Note: this freeing invalidates "mask" and "saveptr" pointer targets */ + free(cidr_tok); + + /* Detecting IPv4 vs IPv6 */ + memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_INET; ip.type = IPv4; /* Detecting IPv4 vs IPv6 */ - if(getaddrinfo(first_ip,NULL,&hints,&res) != 0) { + if (getaddrinfo(first_ip, NULL, &hints, &res) != 0) { /*Try IPv6 detection */ ip.type = IPv6; hints.ai_family = AF_INET6; int ret; - if((ret=getaddrinfo(first_ip,NULL,&hints,&res)) != 0) { + if ((ret = getaddrinfo(first_ip, NULL, &hints, &res)) != 0) { free(first_ip); return 0; } - + +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-align" +#endif + /* Note: we receive a pointer to res above, so have + * no control about alignment of its further data */ s_in6 = (struct sockaddr_in6 *)res->ai_addr; - memcpy(&ip.start6,&s_in6->sin6_addr,sizeof(struct in6_addr)); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic pop +#endif + memcpy(&ip.start6, &s_in6->sin6_addr, sizeof(struct in6_addr)); freeaddrinfo(res); } else { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-align" +#endif + /* Note: we receive a pointer to res above, so have + * no control about alignment of its further data */ s_in = (struct sockaddr_in *)res->ai_addr; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic pop +#endif ip.start = s_in->sin_addr; freeaddrinfo(res); } - if( ip.type == IPv4 ) { + if (ip.type == IPv4) { - if( mask_val > 0 ) { + if (mask_val > 0) { mask_val --; mask_bit = 0x80000000; mask_bit >>= mask_val; @@ -277,14 +360,14 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) ip.stop.s_addr = htonl(ntohl(ip.start.s_addr)|mask_bit); ip.start.s_addr = htonl(ntohl(ip.start.s_addr)&(~mask_bit)); - if( ntop(&ip.start, host, sizeof(host)) != 0 ) { + if (ntop(&ip.start, host, sizeof(host)) != 0) { *start_ip = NULL; *stop_ip = NULL; return 0; } *start_ip = strdup(host); - if( ntop(&ip.stop, host, sizeof(host)) != 0 ) { + if (ntop(&ip.stop, host, sizeof(host)) != 0) { free(*start_ip); *start_ip = NULL; *stop_ip = NULL; @@ -296,31 +379,40 @@ int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip) return 1; } else { - if(getaddrinfo(first_ip,NULL,&hints,&res) != 0) { + if (getaddrinfo(first_ip, NULL, &hints, &res) != 0) { return 0; } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wcast-align" +#endif + /* Note: we receive a pointer to res above, so have + * no control about alignment of its further data */ s_in6 = (struct sockaddr_in6 *)res->ai_addr; - memcpy(&ip.stop6,&s_in6->sin6_addr,sizeof(struct in6_addr)); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_CAST_ALIGN) +# pragma GCC diagnostic pop +#endif + memcpy(&ip.stop6, &s_in6->sin6_addr, sizeof(struct in6_addr)); freeaddrinfo(res); mask_byte = mask_val / 8; - if( mask_byte < 16 ) { - memset( &(ip.stop6.s6_addr[mask_byte+1]), 0xFF, 15 - mask_byte); - memset( &(ip.start6.s6_addr[mask_byte+1]), 0x00, 15 - mask_byte); + if (mask_byte < 16 && mask_byte >= 0) { + memset(&(ip.stop6.s6_addr[mask_byte + 1]), 0xFF, 15 - (uint8_t)mask_byte); + memset(&(ip.start6.s6_addr[mask_byte + 1]), 0x00, 15 - (uint8_t)mask_byte); - mask_bit = (0x100 >> mask_val%8)-1; + mask_bit = (0x100 >> mask_val%8) - 1; ip.stop6.s6_addr[mask_byte] |= mask_bit; ip.start6.s6_addr[mask_byte] &= (~mask_bit); } - if( ntop6(&ip.start6, host, sizeof(host)) != 0 ) { + if (ntop6(&ip.start6, host, sizeof(host)) != 0) { *start_ip = NULL; *stop_ip = NULL; return 0; } *start_ip = strdup(host); - if( ntop6(&ip.stop6, host, sizeof(host)) != 0 ) { + if (ntop6(&ip.stop6, host, sizeof(host)) != 0) { free(*start_ip); *start_ip = NULL; *stop_ip = NULL; diff --git a/tools/nut-scanner/nutscan-ip.h b/tools/nut-scanner/nutscan-ip.h index fbae00b..6fa868d 100644 --- a/tools/nut-scanner/nutscan-ip.h +++ b/tools/nut-scanner/nutscan-ip.h @@ -1,6 +1,5 @@ -/* ip.h: iterator for IPv4 or IPv6 addresses - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) 2011 - EATON * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,26 +15,45 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/*! \file nutscan-ip.h + \brief iterator for IPv4 or IPv6 addresses + \author Frederic Bohe +*/ + #ifndef SCAN_IP #define SCAN_IP #include #include +#ifdef __cplusplus +/* *INDENT-OFF* */ +extern "C" { +/* *INDENT-ON* */ +#endif + enum network_type { - IPv4, - IPv6 + IPv4, + IPv6 }; typedef struct nutscan_ip_iter { enum network_type type; - struct in_addr start; - struct in_addr stop; - struct in6_addr start6; - struct in6_addr stop6; + struct in_addr start; + struct in_addr stop; + struct in6_addr start6; + struct in6_addr stop6; } nutscan_ip_iter_t; char * nutscan_ip_iter_init(nutscan_ip_iter_t *, const char * startIP, const char * stopIP); char * nutscan_ip_iter_inc(nutscan_ip_iter_t *); int nutscan_cidr_to_ip(const char * cidr, char ** start_ip, char ** stop_ip); + +#ifdef __cplusplus +/* *INDENT-OFF* */ +} +/* *INDENT-ON* */ +#endif + #endif diff --git a/tools/nut-scanner/nutscan-serial.c b/tools/nut-scanner/nutscan-serial.c new file mode 100644 index 0000000..bf4ac29 --- /dev/null +++ b/tools/nut-scanner/nutscan-serial.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2011 - EATON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! \file nutscan-serial.c + \brief helper functions to get serial devices name + \author Frederic Bohe + \author Arnaud Quette +*/ + +#include "config.h" /* must be the first header */ + +#include "nutscan-serial.h" +#include +#include +#include +#include "nut_platform.h" +#include "common.h" + +#ifdef WIN32 +/* Windows: all serial port names start with "COM" */ +#define SERIAL_PORT_PREFIX "COM" +#else +/* Unix: all serial port names start with "/dev/tty" */ +#define SERIAL_PORT_PREFIX "/dev/tty" +#endif + +#define ERR_OUT_OF_BOUND "Serial port range out of bound (must be 0 to 9 or a to z depending on your system)\n" + +typedef struct { + char * name; + char auto_start_port; + char auto_stop_port; +} device_portname_t; + +static device_portname_t device_portname[] = { +#ifdef NUT_PLATFORM_HPUX + /* the first number seems to be a card instance, the second number seems + to be a port number */ + { "/dev/tty0p%c", '0', '9' }, + { "/dev/tty1p%c", '0', '9' }, + /* osf/1 and Digital UNIX style */ + { "/dev/tty0%c", '0', '9' }, +#endif +#ifdef NUT_PLATFORM_SOLARIS + { "/dev/tty%c", 'a', 'z' }, +#endif +#ifdef NUT_PLATFORM_AIX + { "/dev/tty%c", '0', '9' }, +#endif +#ifdef NUT_PLATFORM_LINUX + { "/dev/ttyS%c", '0', '9' }, + { "/dev/ttyUSB%c", '0', '9' }, +#endif +#ifdef NUT_PLATFORM_MS_WINDOWS + { "COM%c", '1', '9'}, +#endif + /* SGI IRIX */ + /* { "/dev/ttyd%i", "=" }, */ + /* { "/dev/ttyf%i", "=" }, */ + /* FIXME: Mac OS X has no serial port, but maybe ttyUSB? */ + { NULL, 0, 0 } +}; + +/* Return 1 if port_name is a full path name to a serial port, + * as per SERIAL_PORT_PREFIX */ +static int is_serial_port_path(const char * port_name) +{ + if (!strncmp(port_name, SERIAL_PORT_PREFIX, strlen(SERIAL_PORT_PREFIX))) { + return 1; + } + return 0; +} + +/* Add "port" to "list" */ +static char ** add_port(char ** list, char * port) +{ + char ** res; + size_t count = 0; + + if (list == NULL) { + count = 0; + } + else { + while (list[count] != NULL) { + count++; + } + } + + /*+1 to get the number of port from the index nb_ports*/ + /*+1 for the terminal NULL */ + res = realloc(list, sizeof(char*) * (count + 1 + 1)); + if (res == NULL) { + upsdebugx(1, "%s: Failed to realloc port list", __func__); + return list; + } + res[count] = strdup(port); + res[count + 1] = NULL; + + return res; +} + +/* Return a list of serial ports name, in 'ports_list', according to the OS, + * the provided 'ports_range', and the number of available ports */ +char ** nutscan_get_serial_ports_list(const char *ports_range) +{ + char start_port = 0; + char stop_port = 0; + char current_port = 0; + char * list_sep_ptr = NULL; + char ** ports_list = NULL; + char str_tmp[128]; + char * tok; + device_portname_t *cur_device = NULL; + char * saveptr = NULL; + char * range; + int flag_auto = 0; + + /* 1) check ports_list */ + if ((ports_range == NULL) || (!strncmp(ports_range, "auto", 4))) { + flag_auto = 1; + } + else { + range = strdup(ports_range); + /* we have a list: + * - single element: X (digit) or port name (COM1, /dev/ttyS0, ...) + * - range list: X-Y + * - multiple elements (comma separated): /dev/ttyS0,/dev/ttyUSB0 */ + if ((list_sep_ptr = strchr(range, '-')) != NULL) { + tok = strtok_r(range, "-", &saveptr); + if (tok[1] != 0) { + fprintf(stderr, ERR_OUT_OF_BOUND); + free(range); + return NULL; + } + start_port = tok[0]; + tok = strtok_r(NULL, "-", &saveptr); + if (tok != NULL) { + if (tok[1] != 0) { + fprintf(stderr, ERR_OUT_OF_BOUND); + free(range); + return NULL; + } + stop_port = tok[0]; + } + else { + stop_port = start_port; + } + } + else if (((list_sep_ptr = strchr(ports_range, ',')) != NULL) + && (is_serial_port_path(ports_range)) + ) { + tok = strtok_r(range, ",", &saveptr); + while (tok != NULL) { + ports_list = add_port(ports_list, tok); + tok = strtok_r(NULL, ",", &saveptr); + } + } + else { + /* we have been provided a single port name */ + /* it's a full device name */ + if (ports_range[1] != 0) { + ports_list = add_port(ports_list, range); + } + /* it's device number */ + else { + start_port = stop_port = ports_range[0]; + } + } + free(range); + } + + if (start_port == 0 && !flag_auto) { + return ports_list; + } + + for (cur_device = device_portname; cur_device->name != NULL; cur_device++) { + if (flag_auto) { + start_port = cur_device->auto_start_port; + stop_port = cur_device->auto_stop_port; + } + for (current_port = start_port; current_port <= stop_port; + current_port++) { +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY +#pragma GCC diagnostic ignored "-Wformat-security" +#endif + /* We actually have a format string in the name, + * see the device_portname[] definition above */ + snprintf(str_tmp, sizeof(str_tmp), cur_device->name, + current_port); +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL +#pragma GCC diagnostic pop +#endif + + ports_list = add_port(ports_list, str_tmp); + } + } + return ports_list; +} + diff --git a/tools/nut-scanner/nutscan-serial.h b/tools/nut-scanner/nutscan-serial.h new file mode 100644 index 0000000..2471247 --- /dev/null +++ b/tools/nut-scanner/nutscan-serial.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2011 - EATON + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! \file nutscan-serial.h + \brief helper functions to get serial devices name + \author Frederic Bohe +*/ + +#ifndef SCAN_SERIAL +#define SCAN_SERIAL + +char ** nutscan_get_serial_ports_list(const char *ports_range); + +#endif diff --git a/tools/nut-scanner/nutscan-snmp.h b/tools/nut-scanner/nutscan-snmp.h index 7af6cef..660994b 100644 --- a/tools/nut-scanner/nutscan-snmp.h +++ b/tools/nut-scanner/nutscan-snmp.h @@ -1,5 +1,7 @@ -/* nutscan-snmp - * Copyright (C) 2011 - Frederic Bohe +/* nutscan-snmp.h - fully generated during build of NUT + * Copyright (C) 2011-2019 EATON + * Authors: Frederic Bohe + * Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -20,27 +22,44 @@ #define DEVSCAN_SNMP_H typedef struct { - char * oid; - char * mib; - char * sysoid; + char * oid; + char * mib; + char * sysoid; } snmp_device_id_t; /* SNMP IDs device table */ static snmp_device_id_t snmp_device_table[] = { - { ".1.3.6.1.4.1.2947.1.1.2.0" , "bestpower", NULL}, - { ".1.3.6.1.4.1.13742.1.1.12.0" , "raritan", ".1.3.6.1.4.1.13742"}, - { "1.3.6.1.2.1.33.1.1.1.0" , "ietf", ".1.3.6.1.2.1.33"}, - { ".1.3.6.1.4.1.17373.3.1.1.0" , "aphel_genesisII", ".1.3.6.1.4.1.17373"}, - { ".1.3.6.1.4.1.534.6.6.6.1.1.12.0" , "aphel_revelation", ".1.3.6.1.4.1.534.6.6.6"}, - { ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0" , "eaton_epdu", ".1.3.6.1.4.1.534.6.6.7"}, - { "1.3.6.1.4.1.534.1.1.2.0" , "pw", ".1.3.6.1.4.1.534.1"}, - { ".1.3.6.1.4.1.705.1.1.1.0" , "mge", ".1.3.6.1.4.1.705.1"}, - { ".1.3.6.1.4.1.318.1.1.1.1.1.1.0" , "apcc", NULL}, - { ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1" , "baytech", NULL}, - { ".1.3.6.1.4.1.3808.1.1.1.1.1.1.0" , "cyberpower", ".1.3.6.1.4.1.3808"}, - { "1.3.6.1.4.1.232.165.3.1.1.0" , "cpqpower", NULL}, - { ".1.3.6.1.4.1.4555.1.1.1.1.1.1.0" , "netvision", ".1.3.6.1.4.1.4555.1.1.1"}, - /* Terminating entry */ - { NULL, NULL, NULL} + { ".1.3.6.1.4.1.318.1.1.8.1.5.0", "apc_ats", ".1.3.6.1.4.1.318.1.3.11" }, + { ".1.3.6.1.4.1.318.1.1.1.1.1.1.0", "apcc", ".1.3.6.1.4.1.318.1.1.1.1.1.1.0" }, + { ".1.3.6.1.4.1.318.1.1.4.1.4.0", "apc_pdu", ".1.3.6.1.4.1.318.1.3.4.4.1.3.6.1.4.1.318.1.3.4.5" }, + { ".1.3.6.1.4.1.318.1.1.4.1.4.0", "apc_pdu", ".1.3.6.1.4.1.318.1.3.4.5" }, + { ".1.3.6.1.4.1.318.1.1.4.1.4.0", "apc_pdu", ".1.3.6.1.4.1.318.1.3.4.6" }, + { ".1.3.6.1.4.1.4779.1.3.5.2.1.24.1", "baytech", ".1.3.6.1.4.1.4779" }, + { ".1.3.6.1.4.1.2947.1.1.2.0", "bestpower", ".1.3.6.1.4.1.2947.1.1.2.0" }, + { ".1.3.6.1.4.1.232.165.3.1.1.0", "cpqpower", ".1.3.6.1.4.1.232.165.3" }, + { ".1.3.6.1.4.1.3808.1.1.1.1.1.1.0", "cyberpower", ".1.3.6.1.4.1.3808.1.1.1" }, + { NULL, "delta_ups", ".1.3.6.1.4.1.2254.2.4" }, + { ".1.3.6.1.4.1.534.10.2.1.2.0", "eaton_ats16_nm2", ".1.3.6.1.4.1.534.10.2" }, + { ".1.3.6.1.4.1.534.10.2.1.2.0", "eaton_ats16_nmc", ".1.3.6.1.4.1.705.1" }, + { ".1.3.6.1.4.1.534.10.1.2.1.0", "eaton_ats30", ".1.3.6.1.4.1.534.10.1" }, + { ".1.3.6.1.4.1.17373.3.1.1.0", "aphel_genesisII", ".1.3.6.1.4.1.17373" }, + { ".1.3.6.1.4.1.534.6.6.7.1.2.1.2.0", "eaton_epdu", ".1.3.6.1.4.1.534.6.6.7" }, + { ".1.3.6.1.4.1.20677.1", "pulizzi_switched1", ".1.3.6.1.4.1.20677.1" }, + { ".1.3.6.1.4.1.20677.1", "pulizzi_switched2", ".1.3.6.1.4.1.20677.2" }, + { ".1.3.6.1.4.1.534.6.6.6.1.1.12.0", "aphel_revelation", ".1.3.6.1.4.1.534.6.6.6" }, + { ".1.3.6.1.4.1.10418.17.2.1.2.0", "emerson_avocent_pdu", ".1.3.6.1.4.1.10418.17.1.7" }, + { ".1.3.6.1.4.1.232.165.7.1.2.1.3.0", "hpe_epdu", ".1.3.6.1.4.1.232.165.7" }, + { ".1.3.6.1.4.1.2011.6.174.1.2.100.1.2.1", "huawei", ".1.3.6.1.4.1.8072.3.2.10" }, + { NULL, "tripplite", ".1.3.6.1.4.1.850.1" }, + { "1.3.6.1.2.1.33.1.1.1.0", "ietf", ".1.3.6.1.2.1.33" }, + { ".1.3.6.1.4.1.705.1.1.1.0", "mge", ".1.3.6.1.4.1.705.1" }, + { ".1.3.6.1.4.1.4555.1.1.1.1.1.1.0", "netvision", ".1.3.6.1.4.1.4555.1.1.1" }, + { "1.3.6.1.4.1.534.1.1.2.0", "pw", ".1.3.6.1.4.1.534.1" }, + { "1.3.6.1.4.1.534.1.1.2.0", "pxgx_ups", ".1.3.6.1.4.1.534.2.12" }, + { ".1.3.6.1.4.1.13742.1.1.12.0", "raritan", ".1.3.6.1.4.1.13742" }, + { ".1.3.6.1.4.1.13742.6.3.2.1.1.3.1", "raritan-px2", ".1.3.6.1.4.1.13742.6" }, + { NULL, "xppc", ".1.3.6.1.4.1.935" }, + /* Terminating entry */ + { NULL, NULL, NULL } }; #endif /* DEVSCAN_SNMP_H */ diff --git a/tools/nut-scanner/nutscan-usb.h b/tools/nut-scanner/nutscan-usb.h index 89a39fc..072c6a7 100644 --- a/tools/nut-scanner/nutscan-usb.h +++ b/tools/nut-scanner/nutscan-usb.h @@ -19,9 +19,29 @@ #ifndef DEVSCAN_USB_H #define DEVSCAN_USB_H -#include -#include "nut_stdint.h" /* for uint16_t */ +#include "nut_stdint.h" /* for uint16_t etc. */ +#include /* for PATH_MAX in usb.h etc. */ + +#include /* for MAXPATHLEN etc. */ + +/* libusb header file */ +#if (!WITH_LIBUSB_1_0) && (!WITH_LIBUSB_0_1) +#error "configure script error: Neither WITH_LIBUSB_1_0 nor WITH_LIBUSB_0_1 is set" +#endif + +#if (WITH_LIBUSB_1_0) && (WITH_LIBUSB_0_1) +#error "configure script error: Both WITH_LIBUSB_1_0 and WITH_LIBUSB_0_1 are set" +#endif + +#if WITH_LIBUSB_1_0 + #include +#endif +#if WITH_LIBUSB_0_1 + #include + /* simple remap to avoid bloating structures */ + typedef usb_dev_handle libusb_device_handle; +#endif typedef struct { uint16_t vendorID; uint16_t productID; @@ -31,7 +51,8 @@ typedef struct { /* USB IDs device table */ static usb_device_id_t usb_device_table[] = { - { 0x0001, 0x0000, "blazer_usb" }, + { 0x0001, 0x0000, "nutdrv_qx" }, + { 0x03f0, 0x0001, "usbhid-ups" }, { 0x03f0, 0x1f01, "bcmxcp_usb" }, { 0x03f0, 0x1f02, "bcmxcp_usb" }, { 0x03f0, 0x1f06, "usbhid-ups" }, @@ -40,9 +61,21 @@ static usb_device_id_t usb_device_table[] = { { 0x03f0, 0x1f0a, "usbhid-ups" }, { 0x03f0, 0x1fe0, "usbhid-ups" }, { 0x03f0, 0x1fe1, "usbhid-ups" }, + { 0x03f0, 0x1fe2, "usbhid-ups" }, + { 0x03f0, 0x1fe3, "usbhid-ups" }, + { 0x03f0, 0x1fe5, "usbhid-ups" }, + { 0x03f0, 0x1fe6, "usbhid-ups" }, + { 0x03f0, 0x1fe7, "usbhid-ups" }, + { 0x03f0, 0x1fe8, "usbhid-ups" }, { 0x0463, 0x0001, "usbhid-ups" }, { 0x0463, 0xffff, "usbhid-ups" }, { 0x047c, 0xffff, "usbhid-ups" }, + { 0x0483, 0x0035, "nutdrv_qx" }, + { 0x0483, 0xa113, "usbhid-ups" }, + { 0x04b3, 0x0001, "usbhid-ups" }, + { 0x04b4, 0x5500, "riello_usb" }, + { 0x04d8, 0xd004, "usbhid-ups" }, + { 0x04d8, 0xd005, "usbhid-ups" }, { 0x050d, 0x0375, "usbhid-ups" }, { 0x050d, 0x0551, "usbhid-ups" }, { 0x050d, 0x0750, "usbhid-ups" }, @@ -51,15 +84,23 @@ static usb_device_id_t usb_device_table[] = { { 0x050d, 0x0910, "usbhid-ups" }, { 0x050d, 0x0912, "usbhid-ups" }, { 0x050d, 0x0980, "usbhid-ups" }, + { 0x050d, 0x0f51, "usbhid-ups" }, { 0x050d, 0x1100, "usbhid-ups" }, + { 0x051d, 0x0000, "usbhid-ups" }, { 0x051d, 0x0002, "usbhid-ups" }, { 0x051d, 0x0003, "usbhid-ups" }, { 0x0592, 0x0002, "bcmxcp_usb" }, { 0x0592, 0x0004, "usbhid-ups" }, - { 0x05b8, 0x0000, "blazer_usb" }, - { 0x0665, 0x5161, "blazer_usb" }, - { 0x06da, 0x0002, "bcmxcp_usb" }, - { 0x06da, 0x0003, "blazer_usb" }, + { 0x05b8, 0x0000, "nutdrv_qx" }, + { 0x05dd, 0x041b, "usbhid-ups" }, + { 0x05dd, 0xa011, "usbhid-ups" }, + { 0x0665, 0x5161, "nutdrv_qx" }, + { 0x06da, 0x0002, "nutdrv_qx" }, + { 0x06da, 0x0003, "nutdrv_qx" }, + { 0x06da, 0x0004, "nutdrv_qx" }, + { 0x06da, 0x0005, "nutdrv_qx" }, + { 0x06da, 0x0201, "nutdrv_qx" }, + { 0x06da, 0x0601, "nutdrv_qx" }, { 0x06da, 0xffff, "usbhid-ups" }, { 0x075d, 0x0300, "usbhid-ups" }, { 0x0764, 0x0005, "usbhid-ups" }, @@ -72,6 +113,7 @@ static usb_device_id_t usb_device_table[] = { { 0x09ae, 0x1008, "usbhid-ups" }, { 0x09ae, 0x1009, "usbhid-ups" }, { 0x09ae, 0x1010, "usbhid-ups" }, + { 0x09ae, 0x1330, "usbhid-ups" }, { 0x09ae, 0x2005, "usbhid-ups" }, { 0x09ae, 0x2007, "usbhid-ups" }, { 0x09ae, 0x2008, "usbhid-ups" }, @@ -89,6 +131,8 @@ static usb_device_id_t usb_device_table[] = { { 0x09ae, 0x3013, "usbhid-ups" }, { 0x09ae, 0x3014, "usbhid-ups" }, { 0x09ae, 0x3015, "usbhid-ups" }, + { 0x09ae, 0x3016, "usbhid-ups" }, + { 0x09ae, 0x3024, "usbhid-ups" }, { 0x09ae, 0x4001, "usbhid-ups" }, { 0x09ae, 0x4002, "usbhid-ups" }, { 0x09ae, 0x4003, "usbhid-ups" }, @@ -97,18 +141,39 @@ static usb_device_id_t usb_device_table[] = { { 0x09ae, 0x4006, "usbhid-ups" }, { 0x09ae, 0x4007, "usbhid-ups" }, { 0x09ae, 0x4008, "usbhid-ups" }, + { 0x0d9f, 0x0001, "usbhid-ups" }, { 0x0d9f, 0x0004, "usbhid-ups" }, { 0x0d9f, 0x00a2, "usbhid-ups" }, { 0x0d9f, 0x00a3, "usbhid-ups" }, { 0x0d9f, 0x00a4, "usbhid-ups" }, { 0x0d9f, 0x00a5, "usbhid-ups" }, { 0x0d9f, 0x00a6, "usbhid-ups" }, - { 0x0f03, 0x0001, "blazer_usb" }, + { 0x0f03, 0x0001, "nutdrv_qx" }, { 0x10af, 0x0001, "usbhid-ups" }, - { 0x14f0, 0x00c9, "blazer_usb" }, - { 0xffff, 0x0000, "blazer_usb" }, + { 0x10af, 0x0004, "usbhid-ups" }, + { 0x10af, 0x0008, "usbhid-ups" }, + { 0x14f0, 0x00c9, "nutdrv_qx" }, + { 0x1cb0, 0x0032, "usbhid-ups" }, + { 0x1cb0, 0x0035, "nutdrv_qx" }, + { 0x1cb0, 0x0038, "usbhid-ups" }, + { 0x2341, 0x0036, "usbhid-ups" }, + { 0x2341, 0x8036, "usbhid-ups" }, + { 0x2A03, 0x0036, "usbhid-ups" }, + { 0x2A03, 0x0040, "usbhid-ups" }, + { 0x2A03, 0x8036, "usbhid-ups" }, + { 0x2A03, 0x8040, "usbhid-ups" }, + { 0x2b2d, 0xffff, "usbhid-ups" }, + { 0x2e51, 0x0000, "usbhid-ups" }, + { 0x2e51, 0xffff, "usbhid-ups" }, + { 0x2e66, 0x0201, "usbhid-ups" }, + { 0x2e66, 0x0202, "usbhid-ups" }, + { 0x2e66, 0x0203, "usbhid-ups" }, + { 0x2e66, 0x0300, "usbhid-ups" }, + { 0x4234, 0x0002, "usbhid-ups" }, + { 0xffff, 0x0000, "nutdrv_qx" }, + /* Terminating entry */ - { -1, -1, NULL } + { 0, 0, NULL } }; #endif /* DEVSCAN_USB_H */ diff --git a/tools/nut-scanner/scan_avahi.c b/tools/nut-scanner/scan_avahi.c index 7763b36..67530a7 100644 --- a/tools/nut-scanner/scan_avahi.c +++ b/tools/nut-scanner/scan_avahi.c @@ -1,6 +1,5 @@ -/* scan_avahi.c: detect NUT avahi services - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) 2011 - EATON * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,6 +15,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/*! \file scan_avahi.c + \brief detect NUT through Avahi mDNS / DNS-SD services + \author Frederic Bohe +*/ + #include "common.h" #include "nut-scan.h" @@ -81,132 +86,141 @@ static int (*nut_avahi_service_browser_free)(AvahiServiceBrowser *); static char * (*nut_avahi_address_snprint)(char *ret_s, size_t length, const AvahiAddress *a); static const AvahiPoll* (*nut_avahi_simple_poll_get)(AvahiSimplePoll *s); -/* return 0 on error */ -int nutscan_load_avahi_library() +/* return 0 on error; visible externally */ +int nutscan_load_avahi_library(const char *libname_path); +int nutscan_load_avahi_library(const char *libname_path) { - if( dl_handle != NULL ) { - /* if previous init failed */ - if( dl_handle == (void *)1 ) { - return 0; - } - /* init has already been done */ - return 1; - } + if (dl_handle != NULL) { + /* if previous init failed */ + if (dl_handle == (void *)1) { + return 0; + } + /* init has already been done */ + return 1; + } - if( lt_dlinit() != 0 ) { - fprintf(stderr, "Error initializing lt_init\n"); - return 0; - } + if (libname_path == NULL) { + fprintf(stderr, "AVAHI client library not found. AVAHI search disabled.\n"); + return 0; + } - dl_handle = lt_dlopenext("libavahi-client"); - if (!dl_handle) { - dl_error = lt_dlerror(); - goto err; - } - lt_dlerror(); /* Clear any existing error */ - *(void **) (&nut_avahi_service_browser_get_client) = lt_dlsym(dl_handle, "avahi_service_browser_get_client"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + if (lt_dlinit() != 0) { + fprintf(stderr, "Error initializing lt_init\n"); + return 0; + } - *(void **) (&nut_avahi_simple_poll_loop) = lt_dlsym(dl_handle, "avahi_simple_poll_loop"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + dl_handle = lt_dlopen(libname_path); + if (!dl_handle) { + dl_error = lt_dlerror(); + goto err; + } + lt_dlerror(); /* Clear any existing error */ + *(void **) (&nut_avahi_service_browser_get_client) = lt_dlsym(dl_handle, "avahi_service_browser_get_client"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_client_free) = lt_dlsym(dl_handle, "avahi_client_free"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_simple_poll_loop) = lt_dlsym(dl_handle, "avahi_simple_poll_loop"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_client_errno) = lt_dlsym(dl_handle, "avahi_client_errno"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_client_free) = lt_dlsym(dl_handle, "avahi_client_free"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_free) = lt_dlsym(dl_handle, "avahi_free"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_client_errno) = lt_dlsym(dl_handle, "avahi_client_errno"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_simple_poll_quit) = lt_dlsym(dl_handle, "avahi_simple_poll_quit"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_free) = lt_dlsym(dl_handle, "avahi_free"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_client_new) = lt_dlsym(dl_handle, "avahi_client_new"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_simple_poll_quit) = lt_dlsym(dl_handle, "avahi_simple_poll_quit"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_simple_poll_free) = lt_dlsym(dl_handle, "avahi_simple_poll_free"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_client_new) = lt_dlsym(dl_handle, "avahi_client_new"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_service_resolver_new) = lt_dlsym(dl_handle, "avahi_service_resolver_new"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_simple_poll_free) = lt_dlsym(dl_handle, "avahi_simple_poll_free"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_strerror) = lt_dlsym(dl_handle, "avahi_strerror"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_service_resolver_new) = lt_dlsym(dl_handle, "avahi_service_resolver_new"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_service_resolver_get_client) = lt_dlsym(dl_handle, "avahi_service_resolver_get_client"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_strerror) = lt_dlsym(dl_handle, "avahi_strerror"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_service_browser_new) = lt_dlsym(dl_handle, "avahi_service_browser_new"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_service_resolver_get_client) = lt_dlsym(dl_handle, "avahi_service_resolver_get_client"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_service_resolver_free) = lt_dlsym(dl_handle, "avahi_service_resolver_free"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_service_browser_new) = lt_dlsym(dl_handle, "avahi_service_browser_new"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_simple_poll_new) = lt_dlsym(dl_handle, "avahi_simple_poll_new"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_service_resolver_free) = lt_dlsym(dl_handle, "avahi_service_resolver_free"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_string_list_to_string) = lt_dlsym(dl_handle, "avahi_string_list_to_string"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_simple_poll_new) = lt_dlsym(dl_handle, "avahi_simple_poll_new"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_service_browser_free) = lt_dlsym(dl_handle, "avahi_service_browser_free"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_string_list_to_string) = lt_dlsym(dl_handle, "avahi_string_list_to_string"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_address_snprint) = lt_dlsym(dl_handle, "avahi_address_snprint"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_service_browser_free) = lt_dlsym(dl_handle, "avahi_service_browser_free"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_avahi_simple_poll_get) = lt_dlsym(dl_handle, "avahi_simple_poll_get"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_avahi_address_snprint) = lt_dlsym(dl_handle, "avahi_address_snprint"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - return 1; + *(void **) (&nut_avahi_simple_poll_get) = lt_dlsym(dl_handle, "avahi_simple_poll_get"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + return 1; err: - fprintf(stderr, "%s\n", dl_error); - dl_handle = (void *)1; - return 0; + fprintf(stderr, + "Cannot load AVAHI library (%s) : %s. AVAHI search disabled.\n", + libname_path, dl_error); + dl_handle = (void *)1; + lt_dlexit(); + return 0; } /* end of dynamic link library stuff */ static AvahiSimplePoll *simple_poll = NULL; static nutscan_device_t * dev_ret = NULL; -static long avahi_usec_timeout = 0; +static useconds_t avahi_usec_timeout = 0; -static void update_device(const char * host_name,const char *ip, uint16_t port,char * text, int proto) +static void update_device(const char * host_name, const char *ip, uint16_t port, char * text, int proto) { nutscan_device_t * dev = NULL; @@ -220,113 +234,114 @@ static void update_device(const char * host_name,const char *ip, uint16_t port,c char * device_saveptr = NULL; int device_found = 0; char buf[6]; - int buf_size; + size_t buf_size; - if( text == NULL ) { + if (text == NULL) { return; } t = strdup(text); - phrase = strtok_r(t,"\"",&t_saveptr); - while(phrase != NULL ) { - word = strtok_r(phrase,"=",&phrase_saveptr); - if( word == NULL ) { - phrase = strtok_r(NULL,"\"",&t_saveptr); + phrase = strtok_r(t, "\"", &t_saveptr); + while (phrase != NULL) { + word = strtok_r(phrase, "=", &phrase_saveptr); + if (word == NULL) { + phrase = strtok_r(NULL, "\"", &t_saveptr); continue; } - value = strtok_r(NULL,"=",&phrase_saveptr); - if( value == NULL ) { - phrase = strtok_r(NULL,"\"",&t_saveptr); + value = strtok_r(NULL, "=", &phrase_saveptr); + if (value == NULL) { + phrase = strtok_r(NULL, "\"", &t_saveptr); continue; } - if( strcmp(word,"device_list") != 0 ) { - phrase = strtok_r(NULL,"\"",&t_saveptr); + if (strcmp(word, "device_list") != 0) { + phrase = strtok_r(NULL, "\"", &t_saveptr); continue; } - device = strtok_r(value,";",&device_saveptr); - while( device != NULL ) { + device = strtok_r(value, ";", &device_saveptr); + while (device != NULL) { device_found = 1; dev = nutscan_new_device(); dev->type = TYPE_NUT; dev->driver = strdup("nutclient"); - if( proto == AVAHI_PROTO_INET) { - nutscan_add_option_to_device(dev,"desc","IPv4"); + if (proto == AVAHI_PROTO_INET) { + nutscan_add_option_to_device(dev, "desc", "IPv4"); } - if( proto == AVAHI_PROTO_INET6 ) { - nutscan_add_option_to_device(dev,"desc","IPv6"); + if (proto == AVAHI_PROTO_INET6) { + nutscan_add_option_to_device(dev, "desc", "IPv6"); } - if( port != PORT) { - /* +5+1+1+1 is for : + if (port != PORT) { + /* +5+1+1+1 is for : - port number (max 65535 so 5 characters), - '@' and ':' characters - terminating 0 */ - buf_size = strlen(device)+strlen(host_name)+ - 5+1+1+1; - dev->port=malloc(buf_size); - if(dev->port) { - snprintf(dev->port,buf_size,"%s@%s:%u", - device,host_name,port); + buf_size = strlen(device) + + strlen(host_name) + + 5 + 1 + 1 + 1; + dev->port = malloc(buf_size); + if (dev->port) { + snprintf(dev->port, buf_size, "%s@%s:%u", + device, host_name, port); } } else { /*+1+1 is for '@' character and terminating 0 */ - buf_size = strlen(device)+strlen(host_name)+1+1; - dev->port=malloc(buf_size); - if(dev->port) { - snprintf(dev->port,buf_size,"%s@%s", - device,host_name); + buf_size = strlen(device) + strlen(host_name) + 1 + 1; + dev->port = malloc(buf_size); + if (dev->port) { + snprintf(dev->port, buf_size, "%s@%s", + device, host_name); } } - if( dev->port ) { - dev_ret = nutscan_add_device_to_device(dev_ret,dev); + if (dev->port) { + dev_ret = nutscan_add_device_to_device(dev_ret, dev); } else { nutscan_free_device(dev); } - device = strtok_r(NULL,";",&device_saveptr); - }; + device = strtok_r(NULL, ";", &device_saveptr); + } - phrase = strtok_r(NULL,"\"",&t_saveptr); - }; + phrase = strtok_r(NULL, "\"", &t_saveptr); + } free(t); /* If no device published in avahi data, try to get the device by connecting directly to upsd */ - if( !device_found) { - snprintf(buf,sizeof(buf),"%u",port); - dev = nutscan_scan_nut(ip,ip,buf,avahi_usec_timeout); - if(dev) { - dev_ret = nutscan_add_device_to_device(dev_ret,dev); + if (!device_found) { + snprintf(buf, sizeof(buf), "%u", port); + dev = nutscan_scan_nut(ip, ip, buf, avahi_usec_timeout); + if (dev) { + dev_ret = nutscan_add_device_to_device(dev_ret, dev); } /* add an upsd entry without associated device */ else { dev = nutscan_new_device(); dev->type = TYPE_NUT; dev->driver = strdup("nutclient"); - if( proto == AVAHI_PROTO_INET) { - nutscan_add_option_to_device(dev,"desc","IPv4"); + if (proto == AVAHI_PROTO_INET) { + nutscan_add_option_to_device(dev, "desc", "IPv4"); } - if( proto == AVAHI_PROTO_INET6 ) { - nutscan_add_option_to_device(dev,"desc","IPv6"); + if (proto == AVAHI_PROTO_INET6) { + nutscan_add_option_to_device(dev, "desc", "IPv6"); } - if( port != PORT) { + if (port != PORT) { /*+1+1 is for ':' character and terminating 0 */ /*buf is the string containing the port number*/ - buf_size = strlen(host_name)+strlen(buf)+1+1; - dev->port=malloc(buf_size); - if(dev->port) { - snprintf(dev->port,buf_size,"%s:%s", - host_name,buf); + buf_size = strlen(host_name) + strlen(buf) + 1 + 1; + dev->port = malloc(buf_size); + if (dev->port) { + snprintf(dev->port, buf_size, "%s:%s", + host_name, buf); } } else { - dev->port=strdup(host_name); + dev->port = strdup(host_name); } - if( dev->port ) { - dev_ret = nutscan_add_device_to_device(dev_ret,dev); + if (dev->port) { + dev_ret = nutscan_add_device_to_device(dev_ret, dev); } else { nutscan_free_device(dev); @@ -337,8 +352,8 @@ static void update_device(const char * host_name,const char *ip, uint16_t port,c static void resolve_callback( AvahiServiceResolver *r, - AVAHI_GCC_UNUSED AvahiIfIndex interface, - AVAHI_GCC_UNUSED AvahiProtocol protocol, + AvahiIfIndex interface, + AvahiProtocol protocol, AvahiResolverEvent event, const char *name, const char *type, @@ -348,15 +363,23 @@ static void resolve_callback( uint16_t port, AvahiStringList *txt, AvahiLookupResultFlags flags, - AVAHI_GCC_UNUSED void* userdata) { + void* userdata) +{ assert(r); + NUT_UNUSED_VARIABLE(interface); + NUT_UNUSED_VARIABLE(protocol); + NUT_UNUSED_VARIABLE(userdata); + /* Called whenever a service has been resolved successfully or timed out */ switch (event) { case AVAHI_RESOLVER_FAILURE: - fprintf(stderr, "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", name, type, domain, (*nut_avahi_strerror)((*nut_avahi_client_errno)((*nut_avahi_service_resolver_get_client)(r)))); + fprintf(stderr, + "(Resolver) Failed to resolve service '%s' of type '%s' in domain '%s': %s\n", + name, type, domain, + (*nut_avahi_strerror)((*nut_avahi_client_errno)((*nut_avahi_service_resolver_get_client)(r)))); break; case AVAHI_RESOLVER_FOUND: { @@ -366,6 +389,8 @@ static void resolve_callback( (*nut_avahi_address_snprint)(a, sizeof(a), address); t = (*nut_avahi_string_list_to_string)(txt); + + NUT_UNUSED_VARIABLE(flags); /* fprintf(stderr, "\t%s:%u (%s)\n" @@ -385,7 +410,7 @@ static void resolve_callback( !!(flags & AVAHI_LOOKUP_RESULT_MULTICAST), !!(flags & AVAHI_LOOKUP_RESULT_CACHED)); */ - update_device(host_name,a,port,t,address->proto); + update_device(host_name, a, port, t, address->proto); (*nut_avahi_free)(t); } } @@ -401,18 +426,23 @@ static void browse_callback( const char *name, const char *type, const char *domain, - AVAHI_GCC_UNUSED AvahiLookupResultFlags flags, - void* userdata) { + AvahiLookupResultFlags flags, + void* userdata) +{ AvahiClient *c = userdata; assert(b); + NUT_UNUSED_VARIABLE(flags); + /* Called whenever a new services becomes available on the LAN or is removed from the LAN */ switch (event) { case AVAHI_BROWSER_FAILURE: - fprintf(stderr, "(Browser) %s\n", (*nut_avahi_strerror)((*nut_avahi_client_errno)((*nut_avahi_service_browser_get_client)(b)))); + fprintf(stderr, + "(Browser) %s\n", + (*nut_avahi_strerror)((*nut_avahi_client_errno)((*nut_avahi_service_browser_get_client)(b)))); (*nut_avahi_simple_poll_quit)(simple_poll); return; @@ -424,8 +454,22 @@ static void browse_callback( the callback function is called the server will free the resolver for us. */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ASSIGN_ENUM) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wassign-enum" +#endif + /* It seems that avahi-common/defs.h only defines the flags in a + * manner similar to bitmask flags to request certain features, + * but lacks a value in that enum for lack of flags (unconstrained + * lookup). So we have to silence a warning here... + */ if (!((*nut_avahi_service_resolver_new)(c, interface, protocol, name, type, domain, AVAHI_PROTO_UNSPEC, 0, resolve_callback, c))) - fprintf(stderr, "Failed to resolve service '%s': %s\n", name, (*nut_avahi_strerror)((*nut_avahi_client_errno)(c))); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ASSIGN_ENUM) +# pragma GCC diagnostic pop +#endif + fprintf(stderr, + "Failed to resolve service '%s': %s\n", + name, (*nut_avahi_strerror)((*nut_avahi_client_errno)(c))); break; @@ -435,33 +479,39 @@ static void browse_callback( case AVAHI_BROWSER_ALL_FOR_NOW: (*nut_avahi_simple_poll_quit)(simple_poll); + goto fallthrough_AVAHI_BROWSER_CACHE_EXHAUSTED; /* be explicit */ + case AVAHI_BROWSER_CACHE_EXHAUSTED: + fallthrough_AVAHI_BROWSER_CACHE_EXHAUSTED: /* fprintf(stderr, "(Browser) %s\n", event == AVAHI_BROWSER_CACHE_EXHAUSTED ? "CACHE_EXHAUSTED" : "ALL_FOR_NOW"); */ break; } } -static void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) { +static void client_callback(AvahiClient *c, AvahiClientState state, void * userdata) { assert(c); + NUT_UNUSED_VARIABLE(userdata); /* Called whenever the client or server state changes */ if (state == AVAHI_CLIENT_FAILURE) { - fprintf(stderr, "Server connection failure: %s\n", (*nut_avahi_strerror)((*nut_avahi_client_errno)(c))); + fprintf(stderr, + "Server connection failure: %s\n", + (*nut_avahi_strerror)((*nut_avahi_client_errno)(c))); (*nut_avahi_simple_poll_quit)(simple_poll); } } -nutscan_device_t * nutscan_scan_avahi(long usec_timeout) +nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout) { - /* Example service publication + /* Example service publication * $ avahi-publish -s nut _upsd._tcp 3493 txtvers=1 protovers=1.0.0 device_list="dev1;dev2" */ AvahiClient *client = NULL; AvahiServiceBrowser *sb = NULL; int error; - if( !nutscan_avail_avahi ) { + if (!nutscan_avail_avahi) { return NULL; } @@ -474,17 +524,44 @@ nutscan_device_t * nutscan_scan_avahi(long usec_timeout) } /* Allocate a new client */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ASSIGN_ENUM) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wassign-enum" +#endif + /* It seems that avahi-common/defs.h only defines the flags in a + * manner similar to bitmask flags to request certain features, + * but lacks a value in that enum for lack of flags (unconstrained + * lookup). So we have to silence a warning here... + */ client = (*nut_avahi_client_new)((*nut_avahi_simple_poll_get)(simple_poll), 0, client_callback, NULL, &error); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ASSIGN_ENUM) +# pragma GCC diagnostic pop +#endif /* Check wether creating the client object succeeded */ if (!client) { - fprintf(stderr, "Failed to create client: %s\n", (*nut_avahi_strerror)(error)); + fprintf(stderr, + "Failed to create client: %s\n", + (*nut_avahi_strerror)(error)); goto fail; } /* Create the service browser */ - if (!(sb = (*nut_avahi_service_browser_new)(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_upsd._tcp", NULL, 0, browse_callback, client))) { - fprintf(stderr, "Failed to create service browser: %s\n", (*nut_avahi_strerror)((*nut_avahi_client_errno)(client))); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ASSIGN_ENUM) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wassign-enum" +#endif + /* See comments about flags just a bit above */ + if (!(sb = (*nut_avahi_service_browser_new)( + client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, + "_upsd._tcp", NULL, 0, browse_callback, client)) + ) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_ASSIGN_ENUM) +# pragma GCC diagnostic pop +#endif + fprintf(stderr, + "Failed to create service browser: %s\n", + (*nut_avahi_strerror)((*nut_avahi_client_errno)(client))); goto fail; } @@ -503,12 +580,14 @@ fail: if (simple_poll) (*nut_avahi_simple_poll_free)(simple_poll); - return dev_ret; + return nutscan_rewind_device(dev_ret); } #else /* WITH_AVAHI */ /* stub function */ -nutscan_device_t * nutscan_scan_avahi(long usec_timeout) +nutscan_device_t * nutscan_scan_avahi(useconds_t usec_timeout) { + NUT_UNUSED_VARIABLE(usec_timeout); + return NULL; } #endif /* WITH_AVAHI */ diff --git a/tools/nut-scanner/scan_eaton_serial.c b/tools/nut-scanner/scan_eaton_serial.c new file mode 100644 index 0000000..4b16311 --- /dev/null +++ b/tools/nut-scanner/scan_eaton_serial.c @@ -0,0 +1,657 @@ +/* + * Copyright (C) 2012 - EATON + * Copyright (C) 2016-2021 - EATON - Various threads-related improvements + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/*! \file scan_eaton_serial.c + \brief detect Eaton serial XCP, SHUT and Q1 devices + \author Arnaud Quette + \author Jim Klimov +*/ + +#include "common.h" +#include "nut-scan.h" + +/* Need this on AIX when using xlc to get alloca */ +#ifdef _AIX +#pragma alloca +#endif /* _AIX */ + +#include +#include +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif +#ifdef HAVE_SYS_SIGNAL_H +# include +#endif +#ifdef HAVE_SIGNAL_H +# include +#endif +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic pop +#endif +#include +#include +#include +#include "serial.h" +#include "bcmxcp_io.h" +#include "bcmxcp_ser.h" +#include "bcmxcp.h" +#include "nutscan-serial.h" + +/* SHUT header */ +#define SHUT_SYNC 0x16 +#define MAX_TRY 4 + +/* BCMXCP header defines these externs now: */ +/* +extern unsigned char BCMXCP_AUTHCMD[4]; +extern struct pw_baud_rate { + int rate; + int name; +} pw_baud_rates[]; +*/ + +/* Local list of found devices */ +static nutscan_device_t * dev_ret = NULL; + +/* Remap some functions to avoid undesired behavior (drivers/main.c) */ +char *getval(const char *var) +{ + NUT_UNUSED_VARIABLE(var); + return NULL; +} + +#ifdef HAVE_PTHREAD +static pthread_mutex_t dev_mutex; +#endif + +/* Drivers name */ +#define SHUT_DRIVER_NAME "mge-shut" +#define XCP_DRIVER_NAME "bcmxcp" +#define Q1_DRIVER_NAME "blazer_ser" + +/* Fake driver main, for using serial functions, needed for bcmxcp_ser.c */ +char *device_path; +int upsfd; +int exit_flag = 0; +int do_lock_port; + +/* Functions extracted from drivers/bcmxcp.c, to avoid pulling too many things + * lightweight function to calculate the 8-bit + * two's complement checksum of buf, using XCP data length (including header) + * the result must be 0 for the sequence data to be valid */ +int checksum_test(const unsigned char *buf) +{ + unsigned char checksum = 0; + int i, length; + /* buf[2] is the length of the XCP frame ; add 5 for the header */ + length = (int)(buf[2]) + 5; + + for (i = 0; i < length; i++) { + checksum += buf[i]; + } + /* Compute the 8-bit, Two's Complement checksum now and return it */ + checksum = ((0x100 - checksum) & 0xFF); + return (checksum == 0); +} + + +unsigned char calc_checksum(const unsigned char *buf) +{ + unsigned char c; + int i; + + c = 0; + for (i = 0; i < 2 + buf[1]; i++) + c -= buf[i]; + + return c; +} + +/******************************************************************************* + * SHUT functions (MGE legacy, but Eaton path forward) + ******************************************************************************/ + +/* Light version of of drivers/libshut.c->shut_synchronise() + * return 1 if OK, 0 otherwise */ +static int shut_synchronise(int arg_upsfd) +{ + int try; + unsigned char reply = '\0'; + /* FIXME? Should we save "arg_upsfd" into global "upsfd" variable? + * This was previously shadowed by function argument named "upsfd"... + */ + /* upsfd = arg_upsfd; */ + + /* Sync with the UPS according to notification */ + for (try = 0; try < MAX_TRY; try++) { + if ((ser_send_char(arg_upsfd, SHUT_SYNC)) == -1) { + continue; + } + + ser_get_char(arg_upsfd, &reply, 1, 0); + if (reply == SHUT_SYNC) { + return 1; + } + } + return 0; +} + +/* SHUT scan: + * send SYNC token (0x16) and receive the SYNC token back + * FIXME: maybe try to get device descriptor?! + */ +static nutscan_device_t * nutscan_scan_eaton_serial_shut(const char* port_name) +{ + nutscan_device_t * dev = NULL; + int devfd = -1; + + if ((devfd = ser_open_nf(port_name)) != -1) { + /* set RTS to off and DTR to on to allow correct behavior + * with UPS using PnP feature */ + if (ser_set_dtr(devfd, 1) != -1) { + + ser_set_rts(devfd, 0); + ser_set_speed_nf(devfd, port_name, B2400); + + if (shut_synchronise(devfd)) { + + /* Communication established successfully! */ + dev = nutscan_new_device(); + dev->type = TYPE_EATON_SERIAL; + dev->driver = strdup(SHUT_DRIVER_NAME); + dev->port = strdup(port_name); +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&dev_mutex); +#endif + dev_ret = nutscan_add_device_to_device(dev_ret, dev); +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&dev_mutex); +#endif + } + } + /* Close the device */ + ser_close(devfd, NULL); + } + + return dev; +} + +/******************************************************************************* + * XCP functions (Eaton Powerware legacy) + ******************************************************************************/ + +/* XCP scan: + * baudrate nego (...) + * Send ESC to take it out of menu + * Wait 90ms + * Send auth command (AUTHOR[4] = {0xCF, 0x69, 0xE8, 0xD5};) + * Wait 500ms (or less?) + * Send PW_SET_REQ_ONLY_MODE command (0xA0) and wait for response + * [Get ID Block (PW_ID_BLOCK_REQ) (0x31)] + */ +static nutscan_device_t * nutscan_scan_eaton_serial_xcp(const char* port_name) +{ + nutscan_device_t * dev = NULL; + int i, devfd = -1; + ssize_t ret; + unsigned char answer[256]; + unsigned char sbuf[128]; + + memset(sbuf, 0, 128); + + if ((devfd = ser_open_nf(port_name)) != -1) { +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&dev_mutex); +#endif + upsfd = devfd; +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&dev_mutex); +#endif + + for (i = 0; (pw_baud_rates[i].rate != 0) && (dev == NULL); i++) + { + memset(answer, 0, 256); + + if (ser_set_speed_nf(devfd, port_name, pw_baud_rates[i].rate) == -1) + break; + + ret = ser_send_char(devfd, 0x1d); /* send ESC to take it out of menu */ + if (ret <= 0) + break; + + usleep(90000); + send_write_command(BCMXCP_AUTHCMD, 4); + usleep(500000); + + /* Discovery with Baud Hunting (XCP protocol spec. §4.1.2) + * sending PW_SET_REQ_ONLY_MODE should be enough, since + * the unit should send back Identification block */ + sbuf[0] = PW_COMMAND_START_BYTE; + sbuf[1] = (unsigned char)1; + sbuf[2] = PW_SET_REQ_ONLY_MODE; + sbuf[3] = calc_checksum(sbuf); + ret = ser_send_buf_pace(devfd, 1000, sbuf, 4); + + /* Read PW_COMMAND_START_BYTE byte */ + ret = ser_get_char(devfd, answer, 1, 0); + +#if 0 + /* FIXME: seems not needed, but requires testing with more devices! */ + if (ret <= 0) { + usleep(250000); /* 500000? */ + memset(answer, 0, 256); + ret = command_sequence(&id_command, 1, answer); + } +#endif + + if ((ret > 0) && (answer[0] == PW_COMMAND_START_BYTE)) { + dev = nutscan_new_device(); + dev->type = TYPE_EATON_SERIAL; + dev->driver = strdup(XCP_DRIVER_NAME); + dev->port = strdup(port_name); +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&dev_mutex); +#endif + dev_ret = nutscan_add_device_to_device(dev_ret, dev); +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&dev_mutex); +#endif + break; + } + usleep(100000); + } + /* Close the device */ + ser_close(devfd, NULL); + } + + return dev; +} + +/******************************************************************************* + * Q1 functions (Phoenixtec/Centralion/Santak, still Eaton path forward) + ******************************************************************************/ + +#define SER_WAIT_SEC 1 /* 3 seconds for Best UPS */ +#define MAXTRIES 3 + +/* Q1 scan: + * - open the serial port and set the speed to 2400 baud + * - simply try to get Q1 (status) string + * - check its size and first char. which should be '(' + */ +static nutscan_device_t * nutscan_scan_eaton_serial_q1(const char* port_name) +{ + nutscan_device_t * dev = NULL; + struct termios tio; + ssize_t ret = 0; + int retry; + int devfd = -1; + char buf[128]; + + if ((devfd = ser_open_nf(port_name)) != -1) { + if (ser_set_speed_nf(devfd, port_name, B2400) != -1) { + + if (!tcgetattr(devfd, &tio)) { + + /* Use canonical mode input processing (to read reply line) */ + tio.c_lflag |= ICANON; /* Canonical input (erase and kill processing) */ + + tio.c_cc[VEOF] = _POSIX_VDISABLE; + tio.c_cc[VEOL] = '\r'; + tio.c_cc[VERASE] = _POSIX_VDISABLE; + tio.c_cc[VINTR] = _POSIX_VDISABLE; + tio.c_cc[VKILL] = _POSIX_VDISABLE; + tio.c_cc[VQUIT] = _POSIX_VDISABLE; + tio.c_cc[VSUSP] = _POSIX_VDISABLE; + tio.c_cc[VSTART] = _POSIX_VDISABLE; + tio.c_cc[VSTOP] = _POSIX_VDISABLE; + + if (!tcsetattr(devfd, TCSANOW, &tio)) { + + /* Set the default (normal) cablepower */ + ser_set_dtr(devfd, 1); + ser_set_rts(devfd, 0); + + /* Allow some time to settle for the cablepower */ + usleep(100000); + + /* Only try pure 'Q1', not older ones like 'D' or 'QS' + * > [Q1\r] + * < [(226.0 195.0 226.0 014 49.0 27.5 30.0 00001000\r] + */ + for (retry = 1; retry <= MAXTRIES; retry++) { + + /* simplified code */ + ser_flush_io(devfd); + if ((ret = ser_send(devfd, "Q1\r")) > 0) { + + /* Get Q1 reply */ + if ((ret = ser_get_buf(devfd, buf, sizeof(buf), SER_WAIT_SEC, 0)) > 0) { + + /* Check answer */ + /* should at least (and most) be 46 chars */ + if (ret >= 46) { + if (buf[0] == '(') { + + dev = nutscan_new_device(); + dev->type = TYPE_EATON_SERIAL; + dev->driver = strdup(Q1_DRIVER_NAME); + dev->port = strdup(port_name); +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&dev_mutex); +#endif + dev_ret = nutscan_add_device_to_device(dev_ret, dev); +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&dev_mutex); +#endif + break; + } + } + } + } + } + } + } + } + /* Close the device */ + ser_close(devfd, NULL); + } + return dev; +} + +static void * nutscan_scan_eaton_serial_device(void * port_arg) +{ + nutscan_device_t * dev = NULL; + char* port_name = (char*) port_arg; + + /* Try SHUT first */ + if ((dev = nutscan_scan_eaton_serial_shut(port_name)) == NULL) { + usleep(100000); + /* Else, try XCP */ + if ((dev = nutscan_scan_eaton_serial_xcp(port_name)) == NULL) { + /* Else, try Q1 */ + usleep(100000); + dev = nutscan_scan_eaton_serial_q1(port_name); + } + /* Else try UTalk? */ + } + return dev; +} + +nutscan_device_t * nutscan_scan_eaton_serial(const char* ports_range) +{ + bool_t pass = TRUE; /* Track that we may spawn a scanning thread */ + struct sigaction oldact; + int change_action_handler = 0; + char *current_port_name = NULL; + char **serial_ports_list; + int current_port_nb; +#ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE + sem_t * semaphore = nutscan_semaphore(); +# endif + pthread_t thread; + nutscan_thread_t * thread_array = NULL; + size_t thread_count = 0, i; + + pthread_mutex_init(&dev_mutex, NULL); +#endif /* HAVE_PTHREAD */ + + /* 1) Get ports_list */ + serial_ports_list = nutscan_get_serial_ports_list(ports_range); + if (serial_ports_list == NULL) { + return NULL; + } + + /* Ignore SIGPIPE if the caller hasn't set a handler for it yet */ + if (sigaction(SIGPIPE, NULL, &oldact) == 0) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + if (oldact.sa_handler == SIG_DFL) { + change_action_handler = 1; + signal(SIGPIPE, SIG_IGN); + } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic pop +#endif + } + + /* port(s) iterator */ + current_port_nb = 0; + while (serial_ports_list[current_port_nb] != NULL) { +#ifdef HAVE_PTHREAD + /* NOTE: With many enough targets to scan, this can crash + * by spawning too many children; add a limit and loop to + * "reap" some already done with their work. And probably + * account them in thread_array[] as something to not wait + * for below in pthread_join()... + */ + +# ifdef HAVE_SEMAPHORE + /* Just wait for someone to free a semaphored slot, + * if none are available, and then/otherwise grab one + */ + if (thread_array == NULL) { + /* Starting point, or after a wait to complete + * all earlier runners */ + sem_wait(semaphore); + pass = TRUE; + } else { + pass = (sem_trywait(semaphore) == 0); + } +# else +# ifdef HAVE_PTHREAD_TRYJOIN + /* A somewhat naive and brute-force solution for + * systems without a semaphore.h. This may suffer + * some off-by-one errors, using a few more threads + * than intended (if we race a bit at the wrong time, + * probably up to one per enabled scanner routine). + */ + + /* TOTHINK: Should there be a threadcount_mutex when + * we just read the value in if() and while() below? + * At worst we would overflow the limit a bit due to + * other protocol scanners... + */ + if (curr_threads >= max_threads) { + upsdebugx(2, "%s: already running %zu scanning threads " + "(launched overall: %zu), " + "waiting until some would finish", + __func__, curr_threads, thread_count); + while (curr_threads >= max_threads) { + for (i = 0; i < thread_count ; i++) { + int ret; + + if (!thread_array[i].active) continue; + + pthread_mutex_lock(&threadcount_mutex); + upsdebugx(3, "%s: Trying to join thread #%i...", __func__, i); + ret = pthread_tryjoin_np(thread_array[i].thread, NULL); + switch (ret) { + case ESRCH: /* No thread with the ID thread could be found - already "joined"? */ + upsdebugx(5, "%s: Was thread #%zu joined earlier?", __func__, i); + break; + case 0: /* thread exited */ + if (curr_threads > 0) { + curr_threads --; + upsdebugx(4, "%s: Joined a finished thread #%zu", __func__, i); + } else { + /* threadcount_mutex fault? */ + upsdebugx(0, "WARNING: %s: Accounting of thread count " + "says we are already at 0", __func__); + } + thread_array[i].active = FALSE; + break; + case EBUSY: /* actively running */ + upsdebugx(6, "%s: thread #%zu still busy (%i)", + __func__, i, ret); + break; + case EDEADLK: /* Errors with thread interactions... bail out? */ + case EINVAL: /* Errors with thread interactions... bail out? */ + default: /* new pthreads abilities? */ + upsdebugx(5, "%s: thread #%zu reported code %i", + __func__, i, ret); + break; + } + pthread_mutex_unlock(&threadcount_mutex); + } + + if (curr_threads >= max_threads) { + usleep (10000); /* microSec's, so 0.01s here */ + } + } + upsdebugx(2, "%s: proceeding with scan", __func__); + } + /* NOTE: No change to default "pass" in this ifdef: + * if we got to this line, we have a slot to use */ +# endif /* HAVE_PTHREAD_TRYJOIN */ +# endif /* HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ + + if (pass) { + current_port_name = serial_ports_list[current_port_nb]; + +#ifdef HAVE_PTHREAD + if (pthread_create(&thread, NULL, nutscan_scan_eaton_serial_device, (void*)current_port_name) == 0) { +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_lock(&threadcount_mutex); + curr_threads++; +# endif /* HAVE_PTHREAD_TRYJOIN */ + + thread_count++; + nutscan_thread_t *new_thread_array = realloc(thread_array, + thread_count * sizeof(nutscan_thread_t)); + if (new_thread_array == NULL) { + upsdebugx(1, "%s: Failed to realloc thread array", __func__); + break; + } + else { + thread_array = new_thread_array; + } + thread_array[thread_count - 1].thread = thread; + thread_array[thread_count - 1].active = TRUE; + +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_unlock(&threadcount_mutex); +# endif /* HAVE_PTHREAD_TRYJOIN */ + } +#else /* if not HAVE_PTHREAD */ + nutscan_scan_eaton_serial_device(current_port_name); +#endif /* if HAVE_PTHREAD */ + current_port_nb++; + } else { /* if not pass -- all slots busy */ +#ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE + /* Wait for all current scans to complete */ + if (thread_array != NULL) { + upsdebugx (2, "%s: Running too many scanning threads, " + "waiting until older ones would finish", + __func__); + for (i = 0; i < thread_count ; i++) { + int ret; + if (!thread_array[i].active) { + /* Probably should not get here, + * but handle it just in case */ + upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %zu to be not active", + __func__, i); + sem_post(semaphore); + continue; + } + thread_array[i].active = FALSE; + ret = pthread_join(thread_array[i].thread, NULL); + if (ret != 0) { + upsdebugx(0, "WARNING: %s: Midway clean-up: pthread_join() returned code %i", + __func__, ret); + } + sem_post(semaphore); + } + thread_count = 0; + free(thread_array); + thread_array = NULL; + } +# else +# ifdef HAVE_PTHREAD_TRYJOIN + /* TODO: Move the wait-loop for TRYJOIN here? */ +# endif /* HAVE_PTHREAD_TRYJOIN */ +# endif /* HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ + } /* if: could we "pass" or not? */ + } /* while */ + +#ifdef HAVE_PTHREAD + if (thread_array != NULL) { + upsdebugx(2, "%s: all planned scans launched, waiting for threads to complete", __func__); + for (i = 0; i < thread_count; i++) { + int ret; + + if (!thread_array[i].active) continue; + + ret = pthread_join(thread_array[i].thread, NULL); + if (ret != 0) { + upsdebugx(0, "WARNING: %s: Clean-up: pthread_join() returned code %i", + __func__, ret); + } + thread_array[i].active = FALSE; +# ifdef HAVE_SEMAPHORE + sem_post(semaphore); +# else +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_lock(&threadcount_mutex); + if (curr_threads > 0) { + curr_threads --; + upsdebugx(5, "%s: Clean-up: Joined a finished thread #%zu", + __func__, i); + } else { + upsdebugx(0, "WARNING: %s: Clean-up: Accounting of thread count " + "says we are already at 0", __func__); + } + pthread_mutex_unlock(&threadcount_mutex); +# endif /* HAVE_PTHREAD_TRYJOIN */ +# endif /* HAVE_SEMAPHORE */ + } + free(thread_array); + upsdebugx(2, "%s: all threads freed", __func__); + } + pthread_mutex_destroy(&dev_mutex); +#endif /* HAVE_PTHREAD */ + + if (change_action_handler) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + signal(SIGPIPE, SIG_DFL); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic pop +#endif + } + + /* free everything... */ + i = 0; + while (serial_ports_list[i] != NULL) { + free(serial_ports_list[i]); + i++; + } + free( serial_ports_list); + return nutscan_rewind_device(dev_ret); +} diff --git a/tools/nut-scanner/scan_ipmi.c b/tools/nut-scanner/scan_ipmi.c index eac17c3..6dca351 100644 --- a/tools/nut-scanner/scan_ipmi.c +++ b/tools/nut-scanner/scan_ipmi.c @@ -1,6 +1,6 @@ -/* scan_ipmi.c: detect NUT supported Power Supply Units - * - * Copyright (C) 2011 - Arnaud Quette +/* + * Copyright (C) + * 2011 - 2012 Arnaud Quette * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,6 +16,12 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +/*! \file scan_ipmi.c + \brief detect NUT supported Power Supply Units + \author Arnaud Quette +*/ + #include "common.h" #include "nut-scan.h" @@ -28,24 +34,60 @@ #define NUT_IPMI_DRV_NAME "nut-ipmipsu" +/* IPMI defines */ +/* 5 seconds for establishing an IPMI connection */ +#define IPMI_SESSION_TIMEOUT_LENGTH_DEFAULT 5000 +#define IPMI_RETRANSMISSION_TIMEOUT_LENGTH_DEFAULT 250 + /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; -static int (*nut_ipmi_fru_parse_close_device_id) (ipmi_fru_parse_ctx_t ctx); -static void (*nut_ipmi_fru_parse_ctx_destroy) (ipmi_fru_parse_ctx_t ctx); -static void (*nut_ipmi_sdr_cache_ctx_destroy) (ipmi_sdr_cache_ctx_t ctx); -static void (*nut_ipmi_sdr_parse_ctx_destroy) (ipmi_sdr_parse_ctx_t ctx); -static ipmi_fru_parse_ctx_t (*nut_ipmi_fru_parse_ctx_create) (ipmi_ctx_t ipmi_ctx); -static int (*nut_ipmi_fru_parse_ctx_set_flags) (ipmi_fru_parse_ctx_t ctx, unsigned int flags); -static int (*nut_ipmi_fru_parse_open_device_id) (ipmi_fru_parse_ctx_t ctx, uint8_t fru_device_id); -static char * (*nut_ipmi_fru_parse_ctx_errormsg) (ipmi_fru_parse_ctx_t ctx); -static int (*nut_ipmi_fru_parse_read_data_area) (ipmi_fru_parse_ctx_t ctx, +#ifdef HAVE_FREEIPMI_11X_12X + /* Functions symbols remapping */ + #define IPMI_FRU_CLOSE_DEVICE_ID "ipmi_fru_close_device_id" + #define IPMI_FRU_CTX_DESTROY "ipmi_fru_ctx_destroy" + #define IPMI_FRU_CTX_CREATE "ipmi_fru_ctx_create" + #define IPMI_FRU_CTX_SET_FLAGS "ipmi_fru_ctx_set_flags" + #define IPMI_FRU_OPEN_DEVICE_ID "ipmi_fru_open_device_id" + #define IPMI_FRU_CTX_ERRORMSG "ipmi_fru_ctx_errormsg" + #define IPMI_FRU_READ_DATA_AREA "ipmi_fru_read_data_area" + #define IPMI_FRU_PARSE_NEXT "ipmi_fru_next" + typedef ipmi_fru_ctx_t ipmi_fru_parse_ctx_t; + typedef ipmi_sdr_ctx_t ipmi_sdr_cache_ctx_t; + /* Functions remapping */ + static void (*nut_ipmi_sdr_ctx_destroy) (ipmi_sdr_ctx_t ctx); +#else /* HAVE_FREEIPMI_11X_12X */ + #define IPMI_FRU_AREA_SIZE_MAX IPMI_FRU_PARSE_AREA_SIZE_MAX + #define IPMI_FRU_FLAGS_SKIP_CHECKSUM_CHECKS IPMI_FRU_PARSE_FLAGS_SKIP_CHECKSUM_CHECKS + #define IPMI_FRU_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION + /* Functions symbols remapping */ + #define IPMI_FRU_CLOSE_DEVICE_ID "ipmi_fru_parse_close_device_id" + #define IPMI_FRU_CTX_DESTROY "ipmi_fru_parse_ctx_destroy" + #define IPMI_FRU_CTX_CREATE "ipmi_fru_parse_ctx_create" + #define IPMI_FRU_CTX_SET_FLAGS "ipmi_fru_parse_ctx_set_flags" + #define IPMI_FRU_OPEN_DEVICE_ID "ipmi_fru_parse_open_device_id" + #define IPMI_FRU_CTX_ERRORMSG "ipmi_fru_parse_ctx_errormsg" + #define IPMI_FRU_READ_DATA_AREA "ipmi_fru_parse_read_data_area" + #define IPMI_FRU_PARSE_NEXT "ipmi_fru_parse_next" + /* Functions remapping */ + static void (*nut_ipmi_sdr_cache_ctx_destroy) (ipmi_sdr_cache_ctx_t ctx); + static void (*nut_ipmi_sdr_parse_ctx_destroy) (ipmi_sdr_parse_ctx_t ctx); +#endif /* HAVE_FREEIPMI_11X_12X */ + + +static int (*nut_ipmi_fru_close_device_id) (ipmi_fru_parse_ctx_t ctx); +static void (*nut_ipmi_fru_ctx_destroy) (ipmi_fru_parse_ctx_t ctx); +static ipmi_fru_parse_ctx_t (*nut_ipmi_fru_ctx_create) (ipmi_ctx_t ipmi_ctx); +static int (*nut_ipmi_fru_ctx_set_flags) (ipmi_fru_parse_ctx_t ctx, unsigned int flags); +static int (*nut_ipmi_fru_open_device_id) (ipmi_fru_parse_ctx_t ctx, uint8_t fru_device_id); +static char * (*nut_ipmi_fru_ctx_errormsg) (ipmi_fru_parse_ctx_t ctx); +static int (*nut_ipmi_fru_read_data_area) (ipmi_fru_parse_ctx_t ctx, unsigned int *area_type, unsigned int *area_length, void *areabuf, unsigned int areabuflen); -static int (*nut_ipmi_fru_parse_next) (ipmi_fru_parse_ctx_t ctx); +static int (*nut_ipmi_fru_next) (ipmi_fru_parse_ctx_t ctx); static ipmi_ctx_t (*nut_ipmi_ctx_create) (void); static int (*nut_ipmi_ctx_find_inband) (ipmi_ctx_t ctx, ipmi_driver_type_t *driver_type, @@ -55,29 +97,48 @@ static int (*nut_ipmi_ctx_find_inband) (ipmi_ctx_t ctx, const char *driver_device, unsigned int workaround_flags, unsigned int flags); +static int (*nut_ipmi_ctx_open_outofband) (ipmi_ctx_t ctx, + const char *hostname, + const char *username, + const char *password, + uint8_t authentication_type, + uint8_t privilege_level, + unsigned int session_timeout, + unsigned int retransmission_timeout, + unsigned int workaround_flags, + unsigned int flags); +static int (*nut_ipmi_ctx_errnum) (ipmi_ctx_t ctx); static char * (*nut_ipmi_ctx_errormsg) (ipmi_ctx_t ctx); static int (*nut_ipmi_ctx_close) (ipmi_ctx_t ctx); static void (*nut_ipmi_ctx_destroy) (ipmi_ctx_t ctx); +/* Internal functions */ +static nutscan_device_t * nutscan_scan_ipmi_device(const char * IPaddr, nutscan_ipmi_t * sec); -/* Return 0 on error */ -int nutscan_load_ipmi_library() +/* Return 0 on error; visible externally */ +int nutscan_load_ipmi_library(const char *libname_path); +int nutscan_load_ipmi_library(const char *libname_path) { - if( dl_handle != NULL ) { + if (dl_handle != NULL) { /* if previous init failed */ - if( dl_handle == (void *)1 ) { + if (dl_handle == (void *)1) { return 0; } /* init has already been done */ return 1; } - if( lt_dlinit() != 0 ) { + if (libname_path == NULL) { + fprintf(stderr, "IPMI library not found. IPMI search disabled.\n"); + return 0; + } + + if (lt_dlinit() != 0) { fprintf(stderr, "Error initializing lt_init\n"); return 0; } - dl_handle = lt_dlopenext("libfreeipmi"); + dl_handle = lt_dlopen(libname_path); if (!dl_handle) { dl_error = lt_dlerror(); goto err; @@ -86,99 +147,133 @@ int nutscan_load_ipmi_library() /* Clear any existing error */ lt_dlerror(); - *(void **) (&nut_ipmi_fru_parse_close_device_id) = lt_dlsym(dl_handle, "ipmi_fru_parse_close_device_id"); - if ((dl_error = lt_dlerror()) != NULL) { + *(void **) (&nut_ipmi_fru_close_device_id) = lt_dlsym(dl_handle, IPMI_FRU_CLOSE_DEVICE_ID); + if ((dl_error = lt_dlerror()) != NULL) { goto err; } - *(void **) (&nut_ipmi_fru_parse_ctx_destroy) = lt_dlsym(dl_handle, "ipmi_fru_parse_ctx_destroy"); - if ((dl_error = lt_dlerror()) != NULL) { + *(void **) (&nut_ipmi_fru_ctx_destroy) = lt_dlsym(dl_handle, IPMI_FRU_CTX_DESTROY); + if ((dl_error = lt_dlerror()) != NULL) { goto err; } +#ifdef HAVE_FREEIPMI_11X_12X + + *(void **) (&nut_ipmi_sdr_ctx_destroy) = lt_dlsym(dl_handle, "ipmi_sdr_ctx_destroy"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + +#else /* HAVE_FREEIPMI_11X_12X */ + *(void **) (&nut_ipmi_sdr_cache_ctx_destroy) = lt_dlsym(dl_handle, "ipmi_sdr_cache_ctx_destroy"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_sdr_parse_ctx_destroy) = lt_dlsym(dl_handle, "ipmi_sdr_parse_ctx_destroy"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } +#endif /* HAVE_FREEIPMI_11X_12X */ + + *(void **) (&nut_ipmi_fru_ctx_create) = lt_dlsym(dl_handle, IPMI_FRU_CTX_CREATE); + if ((dl_error = lt_dlerror()) != NULL) { goto err; } - *(void **) (&nut_ipmi_fru_parse_ctx_create) = lt_dlsym(dl_handle, "ipmi_fru_parse_ctx_create"); - if ((dl_error = lt_dlerror()) != NULL) { + *(void **) (&nut_ipmi_fru_ctx_set_flags) = lt_dlsym(dl_handle, IPMI_FRU_CTX_SET_FLAGS); + if ((dl_error = lt_dlerror()) != NULL) { goto err; } - *(void **) (&nut_ipmi_fru_parse_ctx_set_flags) = lt_dlsym(dl_handle, "ipmi_fru_parse_ctx_set_flags"); - if ((dl_error = lt_dlerror()) != NULL) { + *(void **) (&nut_ipmi_fru_open_device_id) = lt_dlsym(dl_handle, IPMI_FRU_OPEN_DEVICE_ID); + if ((dl_error = lt_dlerror()) != NULL) { goto err; } - *(void **) (&nut_ipmi_fru_parse_open_device_id) = lt_dlsym(dl_handle, "ipmi_fru_parse_open_device_id"); - if ((dl_error = lt_dlerror()) != NULL) { + *(void **) (&nut_ipmi_fru_ctx_errormsg) = lt_dlsym(dl_handle, IPMI_FRU_CTX_ERRORMSG); + if ((dl_error = lt_dlerror()) != NULL) { goto err; } - *(void **) (&nut_ipmi_fru_parse_ctx_errormsg) = lt_dlsym(dl_handle, "ipmi_fru_parse_ctx_errormsg"); - if ((dl_error = lt_dlerror()) != NULL) { + *(void **) (&nut_ipmi_fru_read_data_area) = lt_dlsym(dl_handle, IPMI_FRU_READ_DATA_AREA); + if ((dl_error = lt_dlerror()) != NULL) { goto err; } - *(void **) (&nut_ipmi_fru_parse_read_data_area) = lt_dlsym(dl_handle, "ipmi_fru_parse_read_data_area"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } - - *(void **) (&nut_ipmi_fru_parse_next) = lt_dlsym(dl_handle, "ipmi_fru_parse_next"); - if ((dl_error = lt_dlerror()) != NULL) { + *(void **) (&nut_ipmi_fru_next) = lt_dlsym(dl_handle, IPMI_FRU_PARSE_NEXT); + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_ctx_create) = lt_dlsym(dl_handle, "ipmi_ctx_create"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_ctx_find_inband) = lt_dlsym(dl_handle, "ipmi_ctx_find_inband"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_ipmi_ctx_open_outofband) = lt_dlsym(dl_handle, "ipmi_ctx_open_outofband"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_ipmi_ctx_errnum) = lt_dlsym(dl_handle, "ipmi_ctx_errnum"); + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_ctx_errormsg) = lt_dlsym(dl_handle, "ipmi_ctx_errormsg"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_ctx_close) = lt_dlsym(dl_handle, "ipmi_ctx_close"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_ipmi_ctx_destroy) = lt_dlsym(dl_handle, "ipmi_ctx_destroy"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } return 1; err: - fprintf(stderr, "%s\n", dl_error); + fprintf(stderr, "Cannot load IPMI library (%s) : %s. IPMI search disabled.\n", libname_path, dl_error); dl_handle = (void *)1; + lt_dlexit(); return 0; } /* end of dynamic link library stuff */ /* Cleanup IPMI contexts */ +#ifdef HAVE_FREEIPMI_11X_12X +static void nut_freeipmi_cleanup(ipmi_fru_parse_ctx_t fru_parse_ctx, + ipmi_sdr_ctx_t sdr_ctx) +#else /* HAVE_FREEIPMI_11X_12X */ static void nut_freeipmi_cleanup(ipmi_fru_parse_ctx_t fru_parse_ctx, ipmi_sdr_cache_ctx_t sdr_cache_ctx, ipmi_sdr_parse_ctx_t sdr_parse_ctx) +#endif /* HAVE_FREEIPMI_11X_12X */ { if (fru_parse_ctx) { - (*nut_ipmi_fru_parse_close_device_id) (fru_parse_ctx); - (*nut_ipmi_fru_parse_ctx_destroy) (fru_parse_ctx); + (*nut_ipmi_fru_close_device_id) (fru_parse_ctx); + (*nut_ipmi_fru_ctx_destroy) (fru_parse_ctx); } +#ifdef HAVE_FREEIPMI_11X_12X + + if (sdr_ctx) { + (*nut_ipmi_sdr_ctx_destroy) (sdr_ctx); + } + +#else /* HAVE_FREEIPMI_11X_12X */ + if (sdr_cache_ctx) { (*nut_ipmi_sdr_cache_ctx_destroy) (sdr_cache_ctx); } @@ -186,36 +281,54 @@ static void nut_freeipmi_cleanup(ipmi_fru_parse_ctx_t fru_parse_ctx, if (sdr_parse_ctx) { (*nut_ipmi_sdr_parse_ctx_destroy) (sdr_parse_ctx); } + +#endif /* HAVE_FREEIPMI_11X_12X */ } /* Return 1 if supported, 0 otherwise */ -int is_ipmi_device_supported(ipmi_ctx_t ipmi_ctx, int ipmi_id) +static int is_ipmi_device_supported(ipmi_ctx_t ipmi_ctx, int ipmi_id) { int ret = -1; unsigned int area_type = 0; unsigned int area_length = 0; - uint8_t areabuf[IPMI_FRU_PARSE_AREA_SIZE_MAX+1]; + uint8_t areabuf[IPMI_FRU_AREA_SIZE_MAX + 1]; ipmi_fru_parse_ctx_t fru_parse_ctx = NULL; +#ifdef HAVE_FREEIPMI_11X_12X + ipmi_sdr_ctx_t sdr_ctx = NULL; +#else /* HAVE_FREEIPMI_11X_12X */ ipmi_sdr_cache_ctx_t sdr_cache_ctx = NULL; ipmi_sdr_parse_ctx_t sdr_parse_ctx = NULL; +#endif /* HAVE_FREEIPMI_11X_12X */ /* Parse FRU information */ - if (!(fru_parse_ctx = (*nut_ipmi_fru_parse_ctx_create) (ipmi_ctx))) + if (!(fru_parse_ctx = (*nut_ipmi_fru_ctx_create) (ipmi_ctx))) { - fprintf(stderr, "ipmi_fru_parse_ctx_create()\n"); - return 0; - } - - /* lots of motherboards calculate checksums incorrectly */ - if ((*nut_ipmi_fru_parse_ctx_set_flags) (fru_parse_ctx, IPMI_FRU_PARSE_FLAGS_SKIP_CHECKSUM_CHECKS) < 0) - { - nut_freeipmi_cleanup(fru_parse_ctx, sdr_cache_ctx, sdr_parse_ctx); + fprintf(stderr, "Error with %s(): %s\n", IPMI_FRU_CTX_CREATE, (*nut_ipmi_ctx_errormsg)(ipmi_ctx)); return 0; } - if ((*nut_ipmi_fru_parse_open_device_id) (fru_parse_ctx, ipmi_id) < 0) + /* lots of motherboards calculate checksums incorrectly */ + if ((*nut_ipmi_fru_ctx_set_flags) (fru_parse_ctx, IPMI_FRU_FLAGS_SKIP_CHECKSUM_CHECKS) < 0) { +#ifdef HAVE_FREEIPMI_11X_12X + nut_freeipmi_cleanup(fru_parse_ctx, sdr_ctx); +#else nut_freeipmi_cleanup(fru_parse_ctx, sdr_cache_ctx, sdr_parse_ctx); +#endif /* HAVE_FREEIPMI_11X_12X */ + return 0; + } + + if (ipmi_id < 0 || (unsigned int)ipmi_id > UINT8_MAX) { + fprintf(stderr, "is_ipmi_device_supported: ipmi_id=%d is out of range!\n", ipmi_id); + return 0; + } + if ((*nut_ipmi_fru_open_device_id) (fru_parse_ctx, (uint8_t)ipmi_id) < 0) + { +#ifdef HAVE_FREEIPMI_11X_12X + nut_freeipmi_cleanup(fru_parse_ctx, sdr_ctx); +#else + nut_freeipmi_cleanup(fru_parse_ctx, sdr_cache_ctx, sdr_parse_ctx); +#endif /* HAVE_FREEIPMI_11X_12X */ return 0; } @@ -224,46 +337,59 @@ int is_ipmi_device_supported(ipmi_ctx_t ipmi_ctx, int ipmi_id) /* clear fields */ area_type = 0; area_length = 0; - memset (areabuf, '\0', IPMI_FRU_PARSE_AREA_SIZE_MAX + 1); + memset (areabuf, '\0', IPMI_FRU_AREA_SIZE_MAX + 1); /* parse FRU buffer */ - if ((*nut_ipmi_fru_parse_read_data_area) (fru_parse_ctx, + if ((*nut_ipmi_fru_read_data_area) (fru_parse_ctx, &area_type, &area_length, areabuf, - IPMI_FRU_PARSE_AREA_SIZE_MAX) < 0) + IPMI_FRU_AREA_SIZE_MAX) < 0) { +#ifdef HAVE_FREEIPMI_11X_12X + nut_freeipmi_cleanup(fru_parse_ctx, sdr_ctx); +#else nut_freeipmi_cleanup(fru_parse_ctx, sdr_cache_ctx, sdr_parse_ctx); +#endif /* HAVE_FREEIPMI_11X_12X */ return 0; } if (area_length) { - if (area_type == IPMI_FRU_PARSE_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION) + if (area_type == IPMI_FRU_AREA_TYPE_MULTIRECORD_POWER_SUPPLY_INFORMATION) { /* Found a POWER_SUPPLY record */ +#ifdef HAVE_FREEIPMI_11X_12X + nut_freeipmi_cleanup(fru_parse_ctx, sdr_ctx); +#else nut_freeipmi_cleanup(fru_parse_ctx, sdr_cache_ctx, sdr_parse_ctx); +#endif /* HAVE_FREEIPMI_11X_12X */ return 1; } } - } while ((ret = (*nut_ipmi_fru_parse_next) (fru_parse_ctx)) == 1); + } while ((ret = (*nut_ipmi_fru_next) (fru_parse_ctx)) == 1); /* No need for further errors checking */ +#ifdef HAVE_FREEIPMI_11X_12X + nut_freeipmi_cleanup(fru_parse_ctx, sdr_ctx); +#else nut_freeipmi_cleanup(fru_parse_ctx, sdr_cache_ctx, sdr_parse_ctx); +#endif /* HAVE_FREEIPMI_11X_12X */ return 0; } -/* return NULL on error */ -nutscan_device_t * nutscan_scan_ipmi() +/* Check for IPMI support on a specific (local or remote) system + * Return NULL on error, or a valid nutscan_device_t otherwise */ +nutscan_device_t * nutscan_scan_ipmi_device(const char * IPaddr, nutscan_ipmi_t * ipmi_sec) { ipmi_ctx_t ipmi_ctx = NULL; nutscan_device_t * nut_dev = NULL; nutscan_device_t * current_nut_dev = NULL; int ret = -1; int ipmi_id = 0; - char port_id[10]; + char port_id[64]; - if( !nutscan_avail_ipmi ) { + if (!nutscan_avail_ipmi) { return NULL; } @@ -271,37 +397,164 @@ nutscan_device_t * nutscan_scan_ipmi() if (!(ipmi_ctx = (*nut_ipmi_ctx_create) ())) { /* we have to force cleanup, since exit handler is not yet installed */ - fprintf(stderr, "ipmi_ctx_create\n"); + fprintf(stderr, "Failed to ipmi_ctx_create\n"); return NULL; } - if ((ret = (*nut_ipmi_ctx_find_inband) (ipmi_ctx, - NULL, - 0, /* don't disable auto-probe */ - 0, - 0, - NULL, - 0, /* workaround flags, none by default */ - 0 /* flags */ - )) < 0) + /* Are we scanning locally, or over the network? */ + if (IPaddr == NULL) { - fprintf(stderr, "ipmi_ctx_find_inband: %s\n", - (*nut_ipmi_ctx_errormsg) (ipmi_ctx)); - return NULL; + /* FIXME: we need root right to access local IPMI! + if (!ipmi_is_root ()) { + fprintf(stderr, "IPMI scan: %s\n", ipmi_ctx_strerror (IPMI_ERR_PERMISSION)); + } */ + + if ((ret = (*nut_ipmi_ctx_find_inband) (ipmi_ctx, + NULL, + 0, /* don't disable auto-probe */ + 0, + 0, + NULL, + 0, /* workaround flags, none by default */ + 0 /* flags */ + )) < 0) + { + upsdebugx(2, "ipmi_ctx_find_inband (local scan): %s", + (*nut_ipmi_ctx_errormsg) (ipmi_ctx)); + return NULL; + } + if (!ret) + { + /* No local IPMI device detected */ + return NULL; + } } - if (!ret) - { - /* No local IPMI device detected */ - return NULL; + else { + +#if 0 + if (ipmi_sec->ipmi_version == IPMI_2_0) { + + /* FIXME: need processing?! + * int parse_kg (void *out, unsigned int outlen, const char *in) + * if ((rv = parse_kg (common_cmd_args_config->k_g, IPMI_MAX_K_G_LENGTH + 1, data->string)) < 0) + * { + * fprintf (stderr, "Config File Error: k_g input formatted incorrectly\n"); + * exit (EXIT_FAILURE); + * }*/ + if ((ret = (*nut_ipmi_ctx_open_outofband_2_0) (ipmi_ctx, + IPaddr, + ipmi_sec->username, + ipmi_sec->password, + ipmi_sec->K_g_BMC_key, +/*???*/ (ipmi_sec->K_g_BMC_key) ? config->k_g_len : 0, + ipmi_sec->privilege_level, + ipmi_sec->cipher_suite_id, + IPMI_SESSION_TIMEOUT_LENGTH_DEFAULT, + IPMI_RETRANSMISSION_TIMEOUT_LENGTH_DEFAULT, + ipmi_dev->workaround_flags, + flags) < 0) + { + upsdebugx(2, "nut_ipmi_ctx_open_outofband_2_0 (%s): %s", + IPaddr, (*nut_ipmi_ctx_errormsg) (c->ipmi_ctx)); + IPMI_MONITORING_DEBUG (("ipmi_ctx_open_outofband_2_0 (%s): %s", + IPaddr, ipmi_ctx_errormsg (c->ipmi_ctx))); + + if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_USERNAME_INVALID) + c->errnum = IPMI_MONITORING_ERR_USERNAME_INVALID; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_PASSWORD_INVALID) + c->errnum = IPMI_MONITORING_ERR_PASSWORD_INVALID; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_PRIVILEGE_LEVEL_INSUFFICIENT) + c->errnum = IPMI_MONITORING_ERR_PRIVILEGE_LEVEL_INSUFFICIENT; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_PRIVILEGE_LEVEL_CANNOT_BE_OBTAINED) + c->errnum = IPMI_MONITORING_ERR_PRIVILEGEL_LEVEL_CANNOT_BE_OBTAINED; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_K_G_INVALID) + c->errnum = IPMI_MONITORING_ERR_K_G_INVALID; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_CIPHER_SUITE_ID_UNAVAILABLE) + c->errnum = IPMI_MONITORING_ERR_CIPHER_SUITE_ID_UNAVAILABLE; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_PASSWORD_VERIFICATION_TIMEOUT) + c->errnum = IPMI_MONITORING_ERR_PASSWORD_VERIFICATION_TIMEOUT; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_IPMI_2_0_UNAVAILABLE) + c->errnum = IPMI_MONITORING_ERR_IPMI_2_0_UNAVAILABLE; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_CONNECTION_TIMEOUT) + c->errnum = IPMI_MONITORING_ERR_CONNECTION_TIMEOUT; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_SESSION_TIMEOUT) + c->errnum = IPMI_MONITORING_ERR_SESSION_TIMEOUT; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_BAD_COMPLETION_CODE + || ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_IPMI_ERROR) + c->errnum = IPMI_MONITORING_ERR_IPMI_ERROR; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_BMC_BUSY) + c->errnum = IPMI_MONITORING_ERR_BMC_BUSY; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_OUT_OF_MEMORY) + c->errnum = IPMI_MONITORING_ERR_OUT_OF_MEMORY; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_HOSTNAME_INVALID) + c->errnum = IPMI_MONITORING_ERR_HOSTNAME_INVALID; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_PARAMETERS) + c->errnum = IPMI_MONITORING_ERR_PARAMETERS; + else if (ipmi_ctx_errnum (c->ipmi_ctx) == IPMI_ERR_SYSTEM_ERROR) + c->errnum = IPMI_MONITORING_ERR_SYSTEM_ERROR; + else + c->errnum = IPMI_MONITORING_ERR_INTERNAL_ERROR; + return (-1); + } + } + else { /* Not IPMI 2.0 */ + +#endif /* 0 */ + + /* Fall back to IPMI 1.5 */ + if (ipmi_sec->authentication_type < 0 + || (unsigned int)ipmi_sec->authentication_type > UINT8_MAX + ) { + upsdebugx(2, "nutscan_scan_ipmi_device (%s): " + "authentication_type=%d is out of range!", + IPaddr, ipmi_sec->authentication_type); + return 0; + } + if (ipmi_sec->privilege_level < 0 + || (unsigned int)ipmi_sec->privilege_level > UINT8_MAX + ) { + upsdebugx(2, "nutscan_scan_ipmi_device (%s): " + "privilege_level=%d is out of range!", + IPaddr, ipmi_sec->privilege_level); + return 0; + } + if ((ret = (*nut_ipmi_ctx_open_outofband) (ipmi_ctx, + IPaddr, + ipmi_sec->username, + ipmi_sec->password, + (uint8_t)ipmi_sec->authentication_type, + (uint8_t)ipmi_sec->privilege_level, + IPMI_SESSION_TIMEOUT_LENGTH_DEFAULT, + IPMI_RETRANSMISSION_TIMEOUT_LENGTH_DEFAULT, + ipmi_sec->workaround_flags, + IPMI_FLAGS_DEFAULT + )) < 0) + { + /* No IPMI device detected on this host! + if ((*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_USERNAME_INVALID + || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_PASSWORD_INVALID + || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_PRIVILEGE_LEVEL_INSUFFICIENT + || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_PRIVILEGE_LEVEL_CANNOT_BE_OBTAINED + || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_AUTHENTICATION_TYPE_UNAVAILABLE + || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_PASSWORD_VERIFICATION_TIMEOUT + || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_HOSTNAME_INVALID + || (*nut_ipmi_ctx_errnum) (ipmi_ctx) == IPMI_ERR_CONNECTION_TIMEOUT) { */ + + /* FIXME: don't log timeout errors */ + upsdebugx(2, "nut_ipmi_ctx_open_outofband (%s): %s", + IPaddr, (*nut_ipmi_ctx_errormsg) (ipmi_ctx)); + return NULL; + /*}*/ + } } - /* Loop through all possible devices */ + /* Loop through all possible components */ for (ipmi_id = 0 ; ipmi_id <= IPMI_FRU_DEVICE_ID_MAX ; ipmi_id++) { if (is_ipmi_device_supported(ipmi_ctx, ipmi_id)) { - if ( (nut_dev = nutscan_new_device()) == NULL ) { - fprintf(stderr,"Memory allocation error\n"); + if ((nut_dev = nutscan_new_device()) == NULL) { + fprintf(stderr, "Memory allocation error\n"); nutscan_free_device(current_nut_dev); break; } @@ -309,8 +562,16 @@ nutscan_device_t * nutscan_scan_ipmi() /* Fill the device structure (sufficient with driver and port) */ nut_dev->type = TYPE_IPMI; nut_dev->driver = strdup(NUT_IPMI_DRV_NAME); - sprintf(port_id, "id%x", ipmi_id); + if (IPaddr == NULL) { + sprintf(port_id, "id%x", ipmi_id); + } + else { + /* FIXME: also check against "localhost" and its IPv{4,6} */ + sprintf(port_id, "id%x@%s", ipmi_id, IPaddr); + } nut_dev->port = strdup(port_id); + /* FIXME: also dump device.serial? + * using drivers/libfreeipmi_get_board_info() */ current_nut_dev = nutscan_add_device_to_device( current_nut_dev, @@ -318,6 +579,7 @@ nutscan_device_t * nutscan_scan_ipmi() memset (port_id, 0, sizeof(port_id)); } + } /* Final cleanup */ @@ -328,10 +590,55 @@ nutscan_device_t * nutscan_scan_ipmi() return current_nut_dev; } + +/* General IPMI scan entry point: scan 1 to n devices, local or remote, + * for IPMI support + * Return NULL on error, or a valid nutscan_device_t otherwise */ +nutscan_device_t * nutscan_scan_ipmi(const char * start_ip, const char * stop_ip, nutscan_ipmi_t * sec) +{ + nutscan_ip_iter_t ip; + char * ip_str = NULL; + nutscan_ipmi_t * tmp_sec; + nutscan_device_t * nut_dev = NULL; + nutscan_device_t * current_nut_dev = NULL; + + if (!nutscan_avail_ipmi) { + return NULL; + } + + + /* Are we scanning locally, or through the network? */ + if (start_ip == NULL) + { + /* Local PSU scan */ + current_nut_dev = nutscan_scan_ipmi_device(NULL, NULL); + } + else { + ip_str = nutscan_ip_iter_init(&ip, start_ip, stop_ip); + + while (ip_str != NULL) { + tmp_sec = malloc(sizeof(nutscan_ipmi_t)); + memcpy(tmp_sec, sec, sizeof(nutscan_ipmi_t)); + + if ((current_nut_dev = nutscan_scan_ipmi_device(ip_str, tmp_sec)) != NULL) { + /* Store the positive result */ + current_nut_dev = nutscan_add_device_to_device(current_nut_dev, nut_dev); + } + /* Prepare the next iteration */ + ip_str = nutscan_ip_iter_inc(&ip); + } + } + + return nutscan_rewind_device(current_nut_dev); +} #else /* WITH_IPMI */ /* stub function */ -nutscan_device_t * nutscan_scan_ipmi() +nutscan_device_t * nutscan_scan_ipmi(const char * startIP, const char * stopIP, nutscan_ipmi_t * sec) { + NUT_UNUSED_VARIABLE(startIP); + NUT_UNUSED_VARIABLE(stopIP); + NUT_UNUSED_VARIABLE(sec); + return NULL; } #endif /* WITH_IPMI */ diff --git a/tools/nut-scanner/scan_nut.c b/tools/nut-scanner/scan_nut.c index 5fb8b70..a0f2b3e 100644 --- a/tools/nut-scanner/scan_nut.c +++ b/tools/nut-scanner/scan_nut.c @@ -1,6 +1,6 @@ -/* scan_nut.c: detect remote NUT services - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) 2011 - EATON + * Copyright (C) 2016-2021 - EATON - Various threads-related improvements * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,24 +17,116 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/*! \file scan_nut.c + \brief detect remote NUT services + \author Frederic Bohe + \author Jim Klimov +*/ + #include "common.h" #include "upsclient.h" #include "nut-scan.h" -#ifdef HAVE_PTHREAD -#include -#endif +#include +/* dynamic link library stuff */ +static lt_dlhandle dl_handle = NULL; +static const char *dl_error = NULL; + +static int (*nut_upscli_splitaddr)(const char *buf, char **hostname, int *port); +static int (*nut_upscli_tryconnect)(UPSCONN_t *ups, const char *host, int port, + int flags, struct timeval * timeout); +static int (*nut_upscli_list_start)(UPSCONN_t *ups, size_t numq, + const char **query); +static int (*nut_upscli_list_next)(UPSCONN_t *ups, size_t numq, + const char **query, size_t *numa, char ***answer); +static int (*nut_upscli_disconnect)(UPSCONN_t *ups); static nutscan_device_t * dev_ret = NULL; #ifdef HAVE_PTHREAD static pthread_mutex_t dev_mutex; #endif +/* use explicit booleans */ +#ifndef FALSE +typedef enum ebool { FALSE = 0, TRUE } bool_t; +#else +typedef int bool_t; +#endif + struct scan_nut_arg { char * hostname; - long timeout; + useconds_t timeout; }; +/* return 0 on error; visible externally */ +int nutscan_load_upsclient_library(const char *libname_path); +int nutscan_load_upsclient_library(const char *libname_path) +{ + if (dl_handle != NULL) { + /* if previous init failed */ + if (dl_handle == (void *)1) { + return 0; + } + /* init has already been done */ + return 1; + } + + if (libname_path == NULL) { + fprintf(stderr, "NUT client library not found. NUT search disabled.\n"); + return 0; + } + + if (lt_dlinit() != 0) { + fprintf(stderr, "Error initializing lt_init\n"); + return 0; + } + + dl_handle = lt_dlopen(libname_path); + if (!dl_handle) { + dl_error = lt_dlerror(); + goto err; + } + + lt_dlerror(); /* Clear any existing error */ + + *(void **) (&nut_upscli_splitaddr) = lt_dlsym(dl_handle, + "upscli_splitaddr"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_upscli_tryconnect) = lt_dlsym(dl_handle, + "upscli_tryconnect"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_upscli_list_start) = lt_dlsym(dl_handle, + "upscli_list_start"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_upscli_list_next) = lt_dlsym(dl_handle, + "upscli_list_next"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_upscli_disconnect) = lt_dlsym(dl_handle, + "upscli_disconnect"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + return 1; +err: + fprintf(stderr, "Cannot load NUT library (%s) : %s. NUT search disabled.\n", libname_path, dl_error); + dl_handle = (void *)1; + lt_dlexit(); + return 0; +} + /* FIXME: SSL support */ static void * list_nut_devices(void * arg) { @@ -42,13 +134,13 @@ static void * list_nut_devices(void * arg) char *target_hostname = nut_arg->hostname; struct timeval tv; int port; - unsigned int numq, numa; + size_t numq, numa; const char *query[4]; char **answer; char *hostname = NULL; UPSCONN_t *ups = malloc(sizeof(*ups)); nutscan_device_t * dev = NULL; - int buf_size; + size_t buf_size; tv.tv_sec = nut_arg->timeout / (1000*1000); tv.tv_usec = nut_arg->timeout % (1000*1000); @@ -56,28 +148,35 @@ static void * list_nut_devices(void * arg) query[0] = "UPS"; numq = 1; - if (upscli_splitaddr(target_hostname, &hostname, &port) != 0) { - free(target_hostname); - free(nut_arg); - return NULL; - } - if (upscli_tryconnect(ups, hostname, port,UPSCLI_CONN_TRYSSL,&tv) < 0) { + if ((*nut_upscli_splitaddr)(target_hostname, &hostname, &port) != 0) { free(target_hostname); free(nut_arg); + free(ups); return NULL; } - if(upscli_list_start(ups, numq, query) < 0) { + if ((*nut_upscli_tryconnect)(ups, hostname, port, UPSCLI_CONN_TRYSSL, &tv) < 0) { free(target_hostname); free(nut_arg); + free(ups); return NULL; } - while (upscli_list_next(ups, numq, query, &numa, &answer) == 1) { + if ((*nut_upscli_list_start)(ups, numq, query) < 0) { + (*nut_upscli_disconnect)(ups); + free(target_hostname); + free(nut_arg); + free(ups); + return NULL; + } + + while ((*nut_upscli_list_next)(ups, numq, query, &numa, &answer) == 1) { /* UPS */ if (numa < 3) { + (*nut_upscli_disconnect)(ups); free(target_hostname); free(nut_arg); + free(ups); return NULL; } /* FIXME: check for duplication by getting driver.port and device.serial @@ -85,113 +184,359 @@ static void * list_nut_devices(void * arg) /* FIXME: * - also print answer[2] if != "Unavailable"? * - for upsmon.conf or ups.conf (using dummy-ups)? */ - if (numa >= 3) { - dev = nutscan_new_device(); - dev->type = TYPE_NUT; - dev->driver = strdup("nutclient"); - /* +1+1 is for '@' character and terminnating 0 */ - buf_size = strlen(answer[1])+strlen(hostname)+1+1; - dev->port = malloc(buf_size); - if( dev->port ) { - snprintf(dev->port,buf_size,"%s@%s",answer[1], - hostname); -#ifdef HAVE_PTHREAD - pthread_mutex_lock(&dev_mutex); -#endif - dev_ret = nutscan_add_device_to_device(dev_ret,dev); -#ifdef HAVE_PTHREAD - pthread_mutex_unlock(&dev_mutex); -#endif - } + dev = nutscan_new_device(); + dev->type = TYPE_NUT; + dev->driver = strdup("nutclient"); + /* +1+1 is for '@' character and terminating 0 */ + buf_size = strlen(answer[1]) + strlen(hostname) + 1 + 1; + dev->port = malloc(buf_size); + if (dev->port) { + snprintf(dev->port, buf_size, "%s@%s", answer[1], + hostname); +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&dev_mutex); +#endif + dev_ret = nutscan_add_device_to_device(dev_ret, dev); +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&dev_mutex); +#endif } + } + (*nut_upscli_disconnect)(ups); free(target_hostname); free(nut_arg); + free(ups); return NULL; } -nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, const char* port,long usec_timeout) +nutscan_device_t * nutscan_scan_nut(const char* startIP, const char* stopIP, const char* port, useconds_t usec_timeout) { + bool_t pass = TRUE; /* Track that we may spawn a scanning thread */ nutscan_ip_iter_t ip; char * ip_str = NULL; char * ip_dest = NULL; char buf[SMALLBUF]; struct sigaction oldact; int change_action_handler = 0; - int i; struct scan_nut_arg *nut_arg; #ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE + sem_t * semaphore = nutscan_semaphore(); + sem_t semaphore_scantype_inst; + sem_t * semaphore_scantype = &semaphore_scantype_inst; +# endif /* HAVE_SEMAPHORE */ pthread_t thread; - pthread_t * thread_array = NULL; - int thread_count = 0; + nutscan_thread_t * thread_array = NULL; + size_t thread_count = 0, i; +# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) + size_t max_threads_scantype = max_threads_oldnut; +# endif - pthread_mutex_init(&dev_mutex,NULL); + pthread_mutex_init(&dev_mutex, NULL); + +# ifdef HAVE_SEMAPHORE + if (max_threads_scantype > 0) { +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic push #endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic ignored "-Wunreachable-code" +#endif +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + /* Different platforms, different sizes, none fits all... */ + if (SIZE_MAX > UINT_MAX && max_threads_scantype > UINT_MAX) { +#ifdef __clang__ +#pragma clang diagnostic pop +#endif +#ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE +#pragma GCC diagnostic pop +#endif + upsdebugx(1, + "WARNING: %s: Limiting max_threads_scantype to range acceptable for sem_init()", + __func__); + max_threads_scantype = UINT_MAX - 1; + } + sem_init(semaphore_scantype, 0, (unsigned int)max_threads_scantype); + } +# endif /* HAVE_SEMAPHORE */ - if( !nutscan_avail_nut ) { - return NULL; - } +#endif /* HAVE_PTHREAD */ + + if (!nutscan_avail_nut) { + return NULL; + } /* Ignore SIGPIPE if the caller hasn't set a handler for it yet */ - if( sigaction(SIGPIPE, NULL, &oldact) == 0 ) { - if( oldact.sa_handler == SIG_DFL ) { + if (sigaction(SIGPIPE, NULL, &oldact) == 0) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + if (oldact.sa_handler == SIG_DFL) { change_action_handler = 1; - signal(SIGPIPE,SIG_IGN); + signal(SIGPIPE, SIG_IGN); } +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic pop +#endif } - ip_str = nutscan_ip_iter_init(&ip,startIP,stopIP); + ip_str = nutscan_ip_iter_init(&ip, startIP, stopIP); - while( ip_str != NULL ) - { - if( port ) { - if( ip.type == IPv4 ) { - snprintf(buf,sizeof(buf),"%s:%s",ip_str,port); + while (ip_str != NULL) { +#ifdef HAVE_PTHREAD + /* NOTE: With many enough targets to scan, this can crash + * by spawning too many children; add a limit and loop to + * "reap" some already done with their work. And probably + * account them in thread_array[] as something to not wait + * for below in pthread_join()... + */ + +# ifdef HAVE_SEMAPHORE + /* Just wait for someone to free a semaphored slot, + * if none are available, and then/otherwise grab one + */ + if (thread_array == NULL) { + /* Starting point, or after a wait to complete + * all earlier runners */ + if (max_threads_scantype > 0) + sem_wait(semaphore_scantype); + sem_wait(semaphore); + pass = TRUE; + } else { + pass = ((max_threads_scantype == 0 || sem_trywait(semaphore_scantype) == 0) && + sem_trywait(semaphore) == 0); + } +# else +# ifdef HAVE_PTHREAD_TRYJOIN + /* A somewhat naive and brute-force solution for + * systems without a semaphore.h. This may suffer + * some off-by-one errors, using a few more threads + * than intended (if we race a bit at the wrong time, + * probably up to one per enabled scanner routine). + */ + + /* TOTHINK: Should there be a threadcount_mutex when + * we just read the value in if() and while() below? + * At worst we would overflow the limit a bit due to + * other protocol scanners... + */ + if (curr_threads >= max_threads + || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) + ) { + upsdebugx(2, "%s: already running %zu scanning threads " + "(launched overall: %zu), " + "waiting until some would finish", + __func__, curr_threads, thread_count); + while (curr_threads >= max_threads + || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) + ) { + for (i = 0; i < thread_count ; i++) { + int ret; + + if (!thread_array[i].active) continue; + + pthread_mutex_lock(&threadcount_mutex); + upsdebugx(3, "%s: Trying to join thread #%i...", __func__, i); + ret = pthread_tryjoin_np(thread_array[i].thread, NULL); + switch (ret) { + case ESRCH: /* No thread with the ID thread could be found - already "joined"? */ + upsdebugx(5, "%s: Was thread #%zu joined earlier?", __func__, i); + break; + case 0: /* thread exited */ + if (curr_threads > 0) { + curr_threads --; + upsdebugx(4, "%s: Joined a finished thread #%zu", __func__, i); + } else { + /* threadcount_mutex fault? */ + upsdebugx(0, "WARNING: %s: Accounting of thread count " + "says we are already at 0", __func__); + } + thread_array[i].active = FALSE; + break; + case EBUSY: /* actively running */ + upsdebugx(6, "%s: thread #%zu still busy (%i)", + __func__, i, ret); + break; + case EDEADLK: /* Errors with thread interactions... bail out? */ + case EINVAL: /* Errors with thread interactions... bail out? */ + default: /* new pthreads abilities? */ + upsdebugx(5, "%s: thread #%zu reported code %i", + __func__, i, ret); + break; + } + pthread_mutex_unlock(&threadcount_mutex); + } + + if (curr_threads >= max_threads + || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) + ) { + usleep (10000); /* microSec's, so 0.01s here */ + } + } + upsdebugx(2, "%s: proceeding with scan", __func__); + } + /* NOTE: No change to default "pass" in this ifdef: + * if we got to this line, we have a slot to use */ +# endif /* HAVE_PTHREAD_TRYJOIN */ +# endif /* HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ + + if (pass) { + if (port) { + if (ip.type == IPv4) { + snprintf(buf, sizeof(buf), "%s:%s", ip_str, port); + } + else { + snprintf(buf, sizeof(buf), "[%s]:%s", ip_str, port); + } + + ip_dest = strdup(buf); } else { - snprintf(buf,sizeof(buf),"[%s]:%s",ip_str,port); + ip_dest = strdup(ip_str); } - ip_dest = strdup(buf); - } - else { - ip_dest = strdup(ip_str); - } + if ((nut_arg = malloc(sizeof(struct scan_nut_arg))) == NULL) { + free(ip_dest); + break; + } - if((nut_arg = malloc(sizeof(struct scan_nut_arg))) == NULL ) { - free(ip_dest); - break; - } - - nut_arg->timeout = usec_timeout; - nut_arg->hostname = ip_dest; -#ifdef HAVE_PTHREAD - if (pthread_create(&thread,NULL,list_nut_devices,(void*)nut_arg)==0){ - thread_count++; - thread_array = realloc(thread_array, - thread_count*sizeof(pthread_t)); - thread_array[thread_count-1] = thread; - } -#else - list_nut_devices(nut_arg); -#endif - free(ip_str); - ip_str = nutscan_ip_iter_inc(&ip); - } + nut_arg->timeout = usec_timeout; + nut_arg->hostname = ip_dest; #ifdef HAVE_PTHREAD - for ( i=0; i < thread_count ; i++) { - pthread_join(thread_array[i],NULL); + if (pthread_create(&thread, NULL, list_nut_devices, (void*)nut_arg) == 0) { +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_lock(&threadcount_mutex); + curr_threads++; +# endif /* HAVE_PTHREAD_TRYJOIN */ + + thread_count++; + nutscan_thread_t *new_thread_array = realloc(thread_array, + thread_count * sizeof(nutscan_thread_t)); + if (new_thread_array == NULL) { + upsdebugx(1, "%s: Failed to realloc thread array", __func__); + break; + } + else { + thread_array = new_thread_array; + } + thread_array[thread_count - 1].thread = thread; + thread_array[thread_count - 1].active = TRUE; + +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_unlock(&threadcount_mutex); +# endif /* HAVE_PTHREAD_TRYJOIN */ + } +#else /* not HAVE_PTHREAD */ + list_nut_devices(nut_arg); +#endif /* if HAVE_PTHREAD */ + free(ip_str); + ip_str = nutscan_ip_iter_inc(&ip); + } else { /* if not pass -- all slots busy */ +#ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE + /* Wait for all current scans to complete */ + if (thread_array != NULL) { + upsdebugx (2, "%s: Running too many scanning threads, " + "waiting until older ones would finish", + __func__); + for (i = 0; i < thread_count ; i++) { + int ret; + if (!thread_array[i].active) { + /* Probably should not get here, + * but handle it just in case */ + upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %zu to be not active", + __func__, i); + sem_post(semaphore); + if (max_threads_scantype > 0) + sem_post(semaphore_scantype); + continue; + } + thread_array[i].active = FALSE; + ret = pthread_join(thread_array[i].thread, NULL); + if (ret != 0) { + upsdebugx(0, "WARNING: %s: Midway clean-up: pthread_join() returned code %i", + __func__, ret); + } + sem_post(semaphore); + if (max_threads_scantype > 0) + sem_post(semaphore_scantype); + } + thread_count = 0; + free(thread_array); + thread_array = NULL; + } +# else +# ifdef HAVE_PTHREAD_TRYJOIN + /* TODO: Move the wait-loop for TRYJOIN here? */ +# endif /* HAVE_PTHREAD_TRYJOIN */ +# endif /* HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ + } /* if: could we "pass" or not? */ + } /* while */ + +#ifdef HAVE_PTHREAD + if (thread_array != NULL) { + upsdebugx(2, "%s: all planned scans launched, waiting for threads to complete", __func__); + for (i = 0; i < thread_count; i++) { + int ret; + + if (!thread_array[i].active) continue; + + ret = pthread_join(thread_array[i].thread, NULL); + if (ret != 0) { + upsdebugx(0, "WARNING: %s: Clean-up: pthread_join() returned code %i", + __func__, ret); + } + thread_array[i].active = FALSE; +# ifdef HAVE_SEMAPHORE + sem_post(semaphore); + if (max_threads_scantype > 0) + sem_post(semaphore_scantype); +# else +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_lock(&threadcount_mutex); + if (curr_threads > 0) { + curr_threads --; + upsdebugx(5, "%s: Clean-up: Joined a finished thread #%zu", + __func__, i); + } else { + upsdebugx(0, "WARNING: %s: Clean-up: Accounting of thread count " + "says we are already at 0", __func__); + } + pthread_mutex_unlock(&threadcount_mutex); +# endif /* HAVE_PTHREAD_TRYJOIN */ +# endif /* HAVE_SEMAPHORE */ + } + free(thread_array); + upsdebugx(2, "%s: all threads freed", __func__); } pthread_mutex_destroy(&dev_mutex); - free(thread_array); -#endif - if(change_action_handler) { - signal(SIGPIPE,SIG_DFL); +# ifdef HAVE_SEMAPHORE + if (max_threads_scantype > 0) + sem_destroy(semaphore_scantype); +# endif /* HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ + + if (change_action_handler) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wstrict-prototypes" +#endif + signal(SIGPIPE, SIG_DFL); +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_STRICT_PROTOTYPES) +# pragma GCC diagnostic pop +#endif } - return dev_ret; + return nutscan_rewind_device(dev_ret); } diff --git a/tools/nut-scanner/scan_snmp.c b/tools/nut-scanner/scan_snmp.c index 32b2b1f..48bdaaa 100644 --- a/tools/nut-scanner/scan_snmp.c +++ b/tools/nut-scanner/scan_snmp.c @@ -1,6 +1,6 @@ -/* scan_snmp.c: detect NUT supported SNMP devices - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) 2011 - EATON + * Copyright (C) 2016-2021 - EATON - Various threads-related improvements * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,6 +17,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/*! \file scan_snmp.c + \brief detect NUT supported SNMP devices + \author Frederic Bohe + \author Arnaud Quette + \author Jim Klimov +*/ + #include "common.h" #include "nut-scan.h" @@ -51,22 +58,29 @@ #include #include -#ifdef HAVE_PTHREAD -#include -#endif #include "nutscan-snmp.h" +/* Address API change */ +#if ( ! NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol ) && ( ! defined usmAESPrivProtocol ) +#define USMAESPRIVPROTOCOL "usmAES128PrivProtocol" +#else +#define USMAESPRIVPROTOCOL "usmAESPrivProtocol" +#endif #define SysOID ".1.3.6.1.2.1.1.2.0" +/* use explicit booleans */ +#ifndef FALSE +typedef enum ebool { FALSE = 0, TRUE } bool_t; +#else +typedef int bool_t; +#endif + static nutscan_device_t * dev_ret = NULL; #ifdef HAVE_PTHREAD static pthread_mutex_t dev_mutex; -static pthread_mutex_t lib_mutex; -static pthread_t * thread_array = NULL; -static int thread_count = 0; #endif -long g_usec_timeout ; +static useconds_t g_usec_timeout ; /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; @@ -76,48 +90,86 @@ static void (*nut_init_snmp)(const char *type); static void (*nut_snmp_sess_init)(netsnmp_session * session); static void * (*nut_snmp_sess_open)(struct snmp_session *session); static int (*nut_snmp_sess_close)(void *handle); -static struct snmp_session * (*nut_snmp_sess_session)(void *handle); +static struct snmp_session * (*nut_snmp_sess_session)(void *handle); static void * (*nut_snmp_parse_oid)(const char *input, oid *objid, size_t *objidlen); -static struct snmp_pdu * (*nut_snmp_pdu_create) (int command ); -netsnmp_variable_list * (*nut_snmp_add_null_var)(netsnmp_pdu *pdu, +static struct snmp_pdu * (*nut_snmp_pdu_create) (int command); +static netsnmp_variable_list * (*nut_snmp_add_null_var)(netsnmp_pdu *pdu, const oid *objid, size_t objidlen); static int (*nut_snmp_sess_synch_response) (void *sessp, netsnmp_pdu *pdu, netsnmp_pdu **response); static int (*nut_snmp_oid_compare) (const oid *in_name1, size_t len1, const oid *in_name2, size_t len2); static void (*nut_snmp_free_pdu) (netsnmp_pdu *pdu); -static int (*nut_generate_Ku)(const oid * hashtype, u_int hashtype_len, - u_char * P, size_t pplen, u_char * Ku, size_t * kulen); -static const char * (*nut_snmp_api_errstring) (int snmp_errnumber); -static int (*nut_snmp_errno); -static oid * (*nut_usmAESPrivProtocol); -static oid * (*nut_usmHMACMD5AuthProtocol); -static oid * (*nut_usmHMACSHA1AuthProtocol); -static oid * (*nut_usmDESPrivProtocol); -/* return 0 on error */ -int nutscan_load_snmp_library() -{ -#ifdef HAVE_PTHREAD - pthread_mutex_lock(&lib_mutex); +/* NOTE: Net-SNMP headers just are weird like that, in the same release: +net-snmp/types.h: size_t securityAuthProtoLen; +net-snmp/library/keytools.h: int generate_Ku(const oid * hashtype, u_int hashtype_len, ... + * Should we match in configure like for "getnameinfo()" arg types? + * Currently we cast one to another below (detecting target type could help). + */ +static int (*nut_generate_Ku)(const oid * hashtype, u_int hashtype_len, + unsigned char * P, size_t pplen, unsigned char * Ku, size_t * kulen); +static char* (*nut_snmp_out_toggle_options)(char *options); +static const char * (*nut_snmp_api_errstring) (int snmp_errnumber); + +/* Variables (not methods) exported by libnet-snmp: */ +static int *nut_snmp_errno; +#if NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol +static oid *nut_usmAESPrivProtocol; /* might be usmAES128PrivProtocol on some systems */ +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol +static oid *nut_usmHMACMD5AuthProtocol; +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol +static oid *nut_usmHMACSHA1AuthProtocol; +#endif +#if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol +static oid *nut_usmDESPrivProtocol; +#endif +#if NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 +# if NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol +static oid *nut_usmAES192PrivProtocol; +# endif +# if NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol +static oid *nut_usmAES256PrivProtocol; +# endif +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol +static oid *nut_usmHMAC192SHA256AuthProtocol; +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol +static oid *nut_usmHMAC256SHA384AuthProtocol; +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol +static oid *nut_usmHMAC384SHA512AuthProtocol; #endif - if( dl_handle != NULL ) { +/* return 0 on error; visible externally */ +int nutscan_load_snmp_library(const char *libname_path); + +int nutscan_load_snmp_library(const char *libname_path) +{ + if (dl_handle != NULL) { /* if previous init failed */ - if( dl_handle == (void *)1 ) { + if (dl_handle == (void *)1) { return 0; } /* init has already been done */ return 1; } - if( lt_dlinit() != 0 ) { - fprintf(stderr, "Error initializing lt_init\n"); - return 0; - } + if (libname_path == NULL) { + upsdebugx(1, "SNMP library not found. SNMP search disabled"); + return 0; + } - dl_handle = lt_dlopenext("libnetsnmp"); + if (lt_dlinit() != 0) { + upsdebugx(1, "Error initializing lt_init"); + return 0; + } + + dl_handle = lt_dlopen(libname_path); if (!dl_handle) { dl_error = lt_dlerror(); goto err; @@ -125,131 +177,184 @@ int nutscan_load_snmp_library() lt_dlerror(); /* Clear any existing error */ *(void **) (&nut_init_snmp) = lt_dlsym(dl_handle, "init_snmp"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_sess_init) = lt_dlsym(dl_handle, "snmp_sess_init"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_sess_open) = lt_dlsym(dl_handle, "snmp_sess_open"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_sess_close) = lt_dlsym(dl_handle, "snmp_sess_close"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_sess_session) = lt_dlsym(dl_handle, "snmp_sess_session"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_parse_oid) = lt_dlsym(dl_handle, "snmp_parse_oid"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_pdu_create) = lt_dlsym(dl_handle, "snmp_pdu_create"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_add_null_var) = lt_dlsym(dl_handle, "snmp_add_null_var"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_sess_synch_response) = lt_dlsym(dl_handle, "snmp_sess_synch_response"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_oid_compare) = lt_dlsym(dl_handle, "snmp_oid_compare"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } - *(void **) (&nut_snmp_free_pdu) = lt_dlsym(dl_handle,"snmp_free_pdu"); - if ((dl_error = lt_dlerror()) != NULL) { + *(void **) (&nut_snmp_free_pdu) = lt_dlsym(dl_handle, "snmp_free_pdu"); + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_generate_Ku) = lt_dlsym(dl_handle, "generate_Ku"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_snmp_out_toggle_options) = lt_dlsym(dl_handle, + "snmp_out_toggle_options"); + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_api_errstring) = lt_dlsym(dl_handle, "snmp_api_errstring"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } *(void **) (&nut_snmp_errno) = lt_dlsym(dl_handle, "snmp_errno"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } +#if NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol *(void **) (&nut_usmAESPrivProtocol) = lt_dlsym(dl_handle, - "usmAESPrivProtocol"); - if ((dl_error = lt_dlerror()) != NULL) { + USMAESPRIVPROTOCOL); + if ((dl_error = lt_dlerror()) != NULL) { goto err; } +#endif /* NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol */ +#if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol *(void **) (&nut_usmHMACMD5AuthProtocol) = lt_dlsym(dl_handle, "usmHMACMD5AuthProtocol"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } +#endif /* NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol */ +#if NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol *(void **) (&nut_usmHMACSHA1AuthProtocol) = lt_dlsym(dl_handle, "usmHMACSHA1AuthProtocol"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } +#endif /* NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol */ +#if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol *(void **) (&nut_usmDESPrivProtocol) = lt_dlsym(dl_handle, "usmDESPrivProtocol"); - if ((dl_error = lt_dlerror()) != NULL) { + if ((dl_error = lt_dlerror()) != NULL) { goto err; } +#endif /* NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol */ + +#if NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 +# if NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol + *(void **) (&nut_usmAES192PrivProtocol) = lt_dlsym(dl_handle, + "usmAES192PrivProtocol"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } +# endif /* NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol */ + +# if NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol + *(void **) (&nut_usmAES256PrivProtocol) = lt_dlsym(dl_handle, + "usmAES256PrivProtocol"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } +# endif /* NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol */ +#endif /* NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 */ + +#if NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol + *(void **) (&nut_usmHMAC192SHA256AuthProtocol) = lt_dlsym(dl_handle, + "usmHMAC192SHA256AuthProtocol"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } +#endif /* NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol */ + +#if NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol + *(void **) (&nut_usmHMAC256SHA384AuthProtocol) = lt_dlsym(dl_handle, + "usmHMAC256SHA384AuthProtocol"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } +#endif /* NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol */ + +#if NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol + *(void **) (&nut_usmHMAC384SHA512AuthProtocol) = lt_dlsym(dl_handle, + "usmHMAC384SHA512AuthProtocol"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } +#endif /* NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol */ -#ifdef HAVE_PTHREAD - pthread_mutex_unlock(&dev_mutex); -#endif return 1; + err: - fprintf(stderr, "%s\n", dl_error); + fprintf(stderr, "Cannot load SNMP library (%s) : %s. SNMP search disabled.\n", + libname_path, dl_error); dl_handle = (void *)1; -#ifdef HAVE_PTHREAD - pthread_mutex_unlock(&dev_mutex); -#endif + lt_dlexit(); return 0; } /* end of dynamic link library stuff */ -static void scan_snmp_add_device(nutscan_snmp_t * sec, struct snmp_pdu *response,char * mib) +static void scan_snmp_add_device(nutscan_snmp_t * sec, struct snmp_pdu *response, char * mib) { nutscan_device_t * dev = NULL; struct snmp_session * session; char * buf; session = (*nut_snmp_sess_session)(sec->handle); - if(session == NULL) { + if (session == NULL) { return; } /* SNMP device found */ @@ -257,49 +362,53 @@ static void scan_snmp_add_device(nutscan_snmp_t * sec, struct snmp_pdu *response dev->type = TYPE_SNMP; dev->driver = strdup("snmp-ups"); dev->port = strdup(session->peername); - buf = malloc( response->variables->val_len + 1 ); - if( buf ) { - memcpy(buf,response->variables->val.string, - response->variables->val_len); - buf[response->variables->val_len]=0; - nutscan_add_option_to_device(dev,"desc",buf); - free(buf); + if (response != NULL) { + buf = malloc (response->variables->val_len + 1); + if (buf) { + memcpy(buf, response->variables->val.string, + response->variables->val_len); + buf[response->variables->val_len] = 0; + nutscan_add_option_to_device(dev, "desc", buf); + free(buf); + } } - nutscan_add_option_to_device(dev,"mibs",mib); + nutscan_add_option_to_device(dev, "mibs", mib); /* SNMP v3 */ - if( session->community == NULL || session->community[0] == 0) { - if( sec->secLevel ) { - nutscan_add_option_to_device(dev,"secLevel", - sec->secLevel); + if (session->community == NULL || session->community[0] == 0) { + nutscan_add_option_to_device(dev, "snmp_version", "v3"); + + if (sec->secLevel) { + nutscan_add_option_to_device(dev, "secLevel", + sec->secLevel); } - if( sec->secName ) { - nutscan_add_option_to_device(dev,"secName", - sec->secName); + if (sec->secName) { + nutscan_add_option_to_device(dev, "secName", + sec->secName); } - if( sec->authPassword ) { - nutscan_add_option_to_device(dev,"authPassword", - sec->authPassword); + if (sec->authPassword) { + nutscan_add_option_to_device(dev, "authPassword", + sec->authPassword); } - if( sec->privPassword ) { - nutscan_add_option_to_device(dev,"privPassword", - sec->privPassword); + if (sec->privPassword) { + nutscan_add_option_to_device(dev, "privPassword", + sec->privPassword); } - if( sec->authProtocol ) { - nutscan_add_option_to_device(dev,"authProtocol", - sec->authProtocol); + if (sec->authProtocol) { + nutscan_add_option_to_device(dev, "authProtocol", + sec->authProtocol); } - if( sec->privProtocol ) { - nutscan_add_option_to_device(dev,"privProtocol", - sec->privProtocol); + if (sec->privProtocol) { + nutscan_add_option_to_device(dev, "privProtocol", + sec->privProtocol); } } else { - buf = malloc( session->community_len + 1 ); - if( buf ) { - memcpy(buf,session->community, + buf = malloc (session->community_len + 1); + if (buf) { + memcpy(buf, session->community, session->community_len); - buf[session->community_len]=0; - nutscan_add_option_to_device(dev,"community",buf); + buf[session->community_len] = 0; + nutscan_add_option_to_device(dev, "community", buf); free(buf); } } @@ -307,17 +416,17 @@ static void scan_snmp_add_device(nutscan_snmp_t * sec, struct snmp_pdu *response #ifdef HAVE_PTHREAD pthread_mutex_lock(&dev_mutex); #endif - dev_ret = nutscan_add_device_to_device(dev_ret,dev); + dev_ret = nutscan_add_device_to_device(dev_ret, dev); #ifdef HAVE_PTHREAD pthread_mutex_unlock(&dev_mutex); #endif } -static struct snmp_pdu * scan_snmp_get_manufacturer(char* oid_str,void* handle) +static struct snmp_pdu * scan_snmp_get_oid(char* oid_str, void* handle) { - size_t name_len; - oid name[MAX_OID_LEN]; + size_t name_len; + oid name[MAX_OID_LEN]; struct snmp_pdu *pdu, *response = NULL; int status; int index = 0; @@ -338,19 +447,21 @@ static struct snmp_pdu * scan_snmp_get_manufacturer(char* oid_str,void* handle) (*nut_snmp_add_null_var)(pdu, name, name_len); - status = (*nut_snmp_sess_synch_response)(handle,pdu, &response); - if( response == NULL ) { + status = (*nut_snmp_sess_synch_response)(handle, pdu, &response); + if (response == NULL) { index++; return NULL; } - if(status!=STAT_SUCCESS||response->errstat!=SNMP_ERR_NOERROR|| - response->variables == NULL || - response->variables->name == NULL || - (*nut_snmp_oid_compare)(response->variables->name, - response->variables->name_length, - name, name_len) != 0 || - response->variables->val.string == NULL ) { + if (status != STAT_SUCCESS + || response->errstat != SNMP_ERR_NOERROR + || response->variables == NULL + || response->variables->name == NULL + || ((*nut_snmp_oid_compare)(response->variables->name, + response->variables->name_length, + name, name_len) != 0) + || response->variables->val.string == NULL + ) { (*nut_snmp_free_pdu)(response); index++; return NULL; @@ -359,21 +470,38 @@ static struct snmp_pdu * scan_snmp_get_manufacturer(char* oid_str,void* handle) return response; } -static void try_all_oid(void * arg) +static void try_all_oid(void * arg, const char * mib_found) { struct snmp_pdu *response = NULL; int index = 0; nutscan_snmp_t * sec = (nutscan_snmp_t *)arg; - while(snmp_device_table[index].oid != NULL) { + upsdebugx(2, "Entering %s for %s", __func__, sec->peername); - response = scan_snmp_get_manufacturer(snmp_device_table[index].oid,sec->handle); - if( response == NULL ) { + while (snmp_device_table[index].mib != NULL) { + + if (snmp_device_table[index].oid == NULL + || snmp_device_table[index].oid[0] == '\0' + ) { index++; continue; } - scan_snmp_add_device(sec,response,snmp_device_table[index].mib); + response = scan_snmp_get_oid(snmp_device_table[index].oid, sec->handle); + if (response == NULL) { + index++; + continue; + } + + /* add device only if not yet detected with the same mib */ + if (mib_found == NULL || (strcmp(mib_found, snmp_device_table[index].mib) != 0)) { + scan_snmp_add_device(sec, response, snmp_device_table[index].mib); + upsdebugx(3, "Found another match for device with MIB '%s'", + snmp_device_table[index].mib); + } + else { + upsdebugx(3, "Skip duplicated device %s", snmp_device_table[index].mib); + } (*nut_snmp_free_pdu)(response); response = NULL; @@ -388,9 +516,9 @@ static int init_session(struct snmp_session * snmp_sess, nutscan_snmp_t * sec) snmp_sess->peername = sec->peername; - if( sec->community != NULL || sec->secLevel == NULL ) { + if (sec->community != NULL || sec->secLevel == NULL) { snmp_sess->version = SNMP_VERSION_1; - if( sec->community != NULL ) { + if (sec->community != NULL) { snmp_sess->community = (unsigned char *)sec->community; snmp_sess->community_len = strlen(sec->community); } @@ -399,7 +527,7 @@ static int init_session(struct snmp_session * snmp_sess, nutscan_snmp_t * sec) snmp_sess->community_len = strlen("public"); } } - else { /* SNMP v3 */ + else { /* SNMP v3 */ snmp_sess->version = SNMP_VERSION_3; /* Security level */ @@ -410,130 +538,230 @@ static int init_session(struct snmp_session * snmp_sess, nutscan_snmp_t * sec) else if (strcmp(sec->secLevel, "authPriv") == 0) snmp_sess->securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; else { - fprintf(stderr,"Bad SNMPv3 securityLevel: %s\n", - sec->secLevel); + fprintf(stderr, + "Bad SNMPv3 securityLevel: %s\n", + sec->secLevel); return 0; } /* Security name */ - if( sec->secName == NULL ) { - fprintf(stderr,"securityName is required for SNMPv3\n"); + if (sec->secName == NULL) { + fprintf(stderr, "securityName is required for SNMPv3\n"); return 0; } snmp_sess->securityName = strdup(sec->secName); snmp_sess->securityNameLen = strlen(snmp_sess->securityName); /* Everything is ready for NOAUTH */ - if( snmp_sess->securityLevel == SNMP_SEC_LEVEL_NOAUTH ) { + if (snmp_sess->securityLevel == SNMP_SEC_LEVEL_NOAUTH) { return 1; } /* Process mandatory fields, based on the security level */ - switch (snmp_sess->securityLevel) { - case SNMP_SEC_LEVEL_AUTHNOPRIV: - if (sec->authPassword == NULL) { - fprintf(stderr, - "authPassword is required for SNMPv3 in %s mode\n", + switch (snmp_sess->securityLevel) { + case SNMP_SEC_LEVEL_AUTHNOPRIV: + if (sec->authPassword == NULL) { + fprintf(stderr, + "authPassword is required " + "for SNMPv3 in %s mode\n", sec->secLevel); return 0; } break; - case SNMP_SEC_LEVEL_AUTHPRIV: - if ((sec->authPassword == NULL) || + case SNMP_SEC_LEVEL_AUTHPRIV: + if ((sec->authPassword == NULL) || (sec->privPassword == NULL)) { - fprintf(stderr, - "authPassword and privPassword are required for SNMPv3 in %s mode\n", + fprintf(stderr, + "authPassword and privPassword are " + "required for SNMPv3 in %s mode\n", sec->secLevel); return 0; } break; - default: - /* nothing else needed */ - break; - } + default: + /* nothing else needed */ + break; + } - /* Process authentication protocol and key */ - snmp_sess->securityAuthKeyLen = USM_AUTH_KU_LEN; + /* Process authentication protocol and key */ + snmp_sess->securityAuthKeyLen = USM_AUTH_KU_LEN; +#if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol /* default to MD5 */ - snmp_sess->securityAuthProto = (*nut_usmHMACMD5AuthProtocol); + snmp_sess->securityAuthProto = nut_usmHMACMD5AuthProtocol; snmp_sess->securityAuthProtoLen = - sizeof((*nut_usmHMACMD5AuthProtocol))/ - sizeof(oid); + sizeof(usmHMACMD5AuthProtocol)/ + sizeof(oid); +#endif - if( sec->authProtocol ) { + if (sec->authProtocol) { +#if NUT_HAVE_LIBNETSNMP_usmHMACSHA1AuthProtocol if (strcmp(sec->authProtocol, "SHA") == 0) { - snmp_sess->securityAuthProto = - (*nut_usmHMACSHA1AuthProtocol); + snmp_sess->securityAuthProto = nut_usmHMACSHA1AuthProtocol; snmp_sess->securityAuthProtoLen = - sizeof((*nut_usmHMACSHA1AuthProtocol))/ + sizeof(usmHMACSHA1AuthProtocol)/ sizeof(oid); } - else { - if (strcmp(sec->authProtocol, "MD5") != 0) { - fprintf(stderr, - "Bad SNMPv3 authProtocol: %s", - sec->authProtocol); - return 0; - } + else +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC192SHA256AuthProtocol + if (strcmp(sec->authProtocol, "SHA256") == 0) { + snmp_sess->securityAuthProto = nut_usmHMAC192SHA256AuthProtocol; + snmp_sess->securityAuthProtoLen = + sizeof(usmHMAC192SHA256AuthProtocol)/ + sizeof(oid); + } + else +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC256SHA384AuthProtocol + if (strcmp(sec->authProtocol, "SHA384") == 0) { + snmp_sess->securityAuthProto = nut_usmHMAC256SHA384AuthProtocol; + snmp_sess->securityAuthProtoLen = + sizeof(usmHMAC256SHA384AuthProtocol)/ + sizeof(oid); + } + else +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMAC384SHA512AuthProtocol + if (strcmp(sec->authProtocol, "SHA512") == 0) { + snmp_sess->securityAuthProto = nut_usmHMAC384SHA512AuthProtocol; + snmp_sess->securityAuthProtoLen = + sizeof(usmHMAC384SHA512AuthProtocol)/ + sizeof(oid); + } + else +#endif +#if NUT_HAVE_LIBNETSNMP_usmHMACMD5AuthProtocol + if (strcmp(sec->authProtocol, "MD5") != 0) { +#else + { +#endif + fprintf(stderr, + "Bad SNMPv3 authProtocol: %s\n", + sec->authProtocol); + return 0; } } /* set the authentication key to a MD5/SHA1 hashed version of * our passphrase (must be at least 8 characters long) */ +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if ((uintmax_t)snmp_sess->securityAuthProtoLen > UINT_MAX) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + fprintf(stderr, "Bad SNMPv3 securityAuthProtoLen: %zu", + snmp_sess->securityAuthProtoLen); + return 0; + } if ((*nut_generate_Ku)(snmp_sess->securityAuthProto, - snmp_sess->securityAuthProtoLen, - (u_char *) sec->authPassword, + (u_int)snmp_sess->securityAuthProtoLen, + (unsigned char *) sec->authPassword, strlen(sec->authPassword), snmp_sess->securityAuthKey, &snmp_sess->securityAuthKeyLen) - != SNMPERR_SUCCESS) { - fprintf(stderr, - "Error generating Ku from authentication pass phrase\n"); - return 0; + != SNMPERR_SUCCESS + ) { + fprintf(stderr, + "Error generating Ku from " + "authentication pass phrase\n"); + return 0; } /* Everything is ready for AUTHNOPRIV */ - if( snmp_sess->securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV ) { + if (snmp_sess->securityLevel == SNMP_SEC_LEVEL_AUTHNOPRIV) { return 1; } +#if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol /* default to DES */ - snmp_sess->securityPrivProto=(*nut_usmDESPrivProtocol); + snmp_sess->securityPrivProto = nut_usmDESPrivProtocol; snmp_sess->securityPrivProtoLen = - sizeof((*nut_usmDESPrivProtocol))/sizeof(oid); + sizeof(usmDESPrivProtocol)/sizeof(oid); +#endif - if( sec->privProtocol ) { + if (sec->privProtocol) { +#if NUT_HAVE_LIBNETSNMP_usmAESPrivProtocol || NUT_HAVE_LIBNETSNMP_usmAES128PrivProtocol if (strcmp(sec->privProtocol, "AES") == 0) { - snmp_sess->securityPrivProto= - (*nut_usmAESPrivProtocol); - snmp_sess->securityPrivProtoLen = - sizeof((*nut_usmAESPrivProtocol))/ + snmp_sess->securityPrivProto = nut_usmAESPrivProtocol; + snmp_sess->securityPrivProtoLen = + sizeof(usmAESPrivProtocol)/ sizeof(oid); } - else { - if (strcmp(sec->privProtocol, "DES") != 0) { - fprintf(stderr, - "Bad SNMPv3 authProtocol: %s\n" - ,sec->authProtocol); + else +#endif +#if NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 +# if NUT_HAVE_LIBNETSNMP_usmAES192PrivProtocol + if (strcmp(sec->privProtocol, "AES192") == 0) { + snmp_sess->securityPrivProto = nut_usmAES192PrivProtocol; + snmp_sess->securityPrivProtoLen = + sizeof(usmAES192PrivProtocol)/ + sizeof(oid); + } + else +# endif +# if NUT_HAVE_LIBNETSNMP_usmAES256PrivProtocol + if (strcmp(sec->privProtocol, "AES256") == 0) { + snmp_sess->securityPrivProto = nut_usmAES256PrivProtocol; + snmp_sess->securityPrivProtoLen = + sizeof(usmAES256PrivProtocol)/ + sizeof(oid); + } + else +# endif +#endif /* NUT_HAVE_LIBNETSNMP_DRAFT_BLUMENTHAL_AES_04 */ +#if NUT_HAVE_LIBNETSNMP_usmDESPrivProtocol + if (strcmp(sec->privProtocol, "DES") != 0) { +#else + { +#endif + fprintf(stderr, + "Bad SNMPv3 privProtocol: %s\n", + sec->privProtocol); return 0; - } } } /* set the private key to a MD5/SHA hashed version of * our passphrase (must be at least 8 characters long) */ snmp_sess->securityPrivKeyLen = USM_PRIV_KU_LEN; +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic push +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif +#ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE +# pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare" +#endif + if ((uintmax_t)snmp_sess->securityAuthProtoLen > UINT_MAX) { +#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) ) +# pragma GCC diagnostic pop +#endif + fprintf(stderr, "Bad SNMPv3 securityAuthProtoLen: %zu", + snmp_sess->securityAuthProtoLen); + return 0; + } if ((*nut_generate_Ku)(snmp_sess->securityAuthProto, - snmp_sess->securityAuthProtoLen, - (u_char *) sec->privPassword, + (u_int)snmp_sess->securityAuthProtoLen, + (unsigned char *) sec->privPassword, strlen(sec->privPassword), snmp_sess->securityPrivKey, &snmp_sess->securityPrivKeyLen) - != SNMPERR_SUCCESS) { - fprintf(stderr, - "Error generating Ku from private pass phrase\n"); - return 0; + != SNMPERR_SUCCESS + ) { + fprintf(stderr, + "Error generating Ku from " + "private pass phrase\n"); + return 0; } } @@ -546,32 +774,40 @@ static void * try_SysOID(void * arg) struct snmp_session snmp_sess; void * handle; struct snmp_pdu *pdu, *response = NULL, *resp = NULL; - oid name[MAX_OID_LEN]; - size_t name_len = MAX_OID_LEN; + oid name[MAX_OID_LEN]; + size_t name_len = MAX_OID_LEN; nutscan_snmp_t * sec = (nutscan_snmp_t *)arg; int index = 0; - int sysoid_found = 0; + char *mib_found = NULL; + + upsdebugx(2, "Entering %s for %s", __func__, sec->peername); /* Initialize session */ - if( !init_session(&snmp_sess,sec) ) { + if (!init_session(&snmp_sess, sec)) { goto try_SysOID_free; } - + snmp_sess.retries = 0; - snmp_sess.timeout = g_usec_timeout; + /* netsnmp timeout is accounted in uS, but typed as long + * and not useconds_t (which is at most long per POSIX) + */ + snmp_sess.timeout = (long)g_usec_timeout; /* Open the session */ handle = (*nut_snmp_sess_open)(&snmp_sess); /* establish the session */ if (handle == NULL) { - fprintf(stderr,"Failed to open SNMP session for %s.\n", + upsdebugx(2, + "Failed to open SNMP session for %s", sec->peername); goto try_SysOID_free; } /* create and send request. */ if (!(*nut_snmp_parse_oid)(SysOID, name, &name_len)) { - fprintf(stderr,"SNMP errors: %s\n", - (*nut_snmp_api_errstring)((*nut_snmp_errno))); + upsdebugx(2, + "SNMP errors for %s: %s", + sec->peername, + (*nut_snmp_api_errstring)((*nut_snmp_errno))); (*nut_snmp_sess_close)(handle); goto try_SysOID_free; } @@ -579,7 +815,7 @@ static void * try_SysOID(void * arg) pdu = (*nut_snmp_pdu_create)(SNMP_MSG_GET); if (pdu == NULL) { - fprintf(stderr,"Not enough memory\n"); + fprintf(stderr, "Not enough memory\n"); (*nut_snmp_sess_close)(handle); goto try_SysOID_free; } @@ -596,44 +832,58 @@ static void * try_SysOID(void * arg) /* SysOID is supposed to give the required MIB. */ /* Check if the received OID match with a known sysOID */ - if(response->variables != NULL && - response->variables->val.objid != NULL){ - while(snmp_device_table[index].oid != NULL) { - if(snmp_device_table[index].sysoid == NULL ) { + if (response->variables != NULL && + response->variables->val.objid != NULL + ) { + while (snmp_device_table[index].mib != NULL) { + if (snmp_device_table[index].sysoid == NULL) { index++; continue; } name_len = MAX_OID_LEN; + if (!(*nut_snmp_parse_oid)( snmp_device_table[index].sysoid, - name, &name_len)) { + name, &name_len) + ) { index++; continue; } - if ( (*nut_snmp_oid_compare)( + if ((*nut_snmp_oid_compare)( response->variables->val.objid, response->variables->val_len/sizeof(oid), - name, name_len) == 0 ) { - /* we have found a relevent sysoid */ - resp = scan_snmp_get_manufacturer( - snmp_device_table[index].oid, - handle); - if( resp != NULL ) { - scan_snmp_add_device(sec,resp, - snmp_device_table[index].mib); - sysoid_found = 1; - (*nut_snmp_free_pdu)(resp); + name, name_len) == 0 + ) { + /* we have found a relevant sysoid */ + + /* add mib if no complementary oid is present */ + /* FIXME: No desc defined when add device */ + if (snmp_device_table[index].oid == NULL + || snmp_device_table[index].oid[0] == '\0' + ) { + scan_snmp_add_device(sec, NULL, snmp_device_table[index].mib); + mib_found = snmp_device_table[index].sysoid; + } + /* else test complementary oid before adding mib */ + else { + resp = scan_snmp_get_oid( + snmp_device_table[index].oid, + handle); + if (resp != NULL) { + scan_snmp_add_device(sec, resp, snmp_device_table[index].mib); + mib_found = snmp_device_table[index].mib; + (*nut_snmp_free_pdu)(resp); + } } } index++; } } - /* try a list of known OID */ - if( !sysoid_found ) { - try_all_oid(sec); - } + /* try a list of known OID, if no device was found otherwise */ + if (mib_found == NULL) + try_all_oid(sec, mib_found); (*nut_snmp_free_pdu)(response); response = NULL; @@ -642,7 +892,7 @@ static void * try_SysOID(void * arg) (*nut_snmp_sess_close)(handle); try_SysOID_free: - if( sec->peername ) { + if (sec->peername) { free(sec->peername); } free(sec); @@ -650,64 +900,295 @@ try_SysOID_free: return NULL; } -nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip,long usec_timeout, nutscan_snmp_t * sec) +nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip, + useconds_t usec_timeout, nutscan_snmp_t * sec) { - int i; + bool_t pass = TRUE; /* Track that we may spawn a scanning thread */ nutscan_snmp_t * tmp_sec; nutscan_ip_iter_t ip; char * ip_str = NULL; #ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE + sem_t * semaphore = nutscan_semaphore(); + sem_t semaphore_scantype_inst; + sem_t * semaphore_scantype = &semaphore_scantype_inst; +# endif /* HAVE_SEMAPHORE */ pthread_t thread; + nutscan_thread_t * thread_array = NULL; + size_t thread_count = 0, i; +# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) + size_t max_threads_scantype = max_threads_netsnmp; +# endif - pthread_mutex_init(&dev_mutex,NULL); - pthread_mutex_init(&lib_mutex,NULL); -#endif + pthread_mutex_init(&dev_mutex, NULL); - if( !nutscan_avail_snmp ) { - return NULL; - } +# ifdef HAVE_SEMAPHORE + if (max_threads_scantype > 0) { + if (SIZE_MAX > UINT_MAX && max_threads_scantype > UINT_MAX) { + upsdebugx(1, + "WARNING: %s: Limiting max_threads_scantype to range acceptable for sem_init()", + __func__); + max_threads_scantype = UINT_MAX - 1; + } + sem_init(semaphore_scantype, 0, (unsigned int)max_threads_scantype); + } +# endif /* HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ + + if (!nutscan_avail_snmp) { + return NULL; + } g_usec_timeout = usec_timeout; + /* Force numeric OIDs resolution (ie, do not resolve to textual names) + * This is mostly for the convenience of debug output */ + if (nut_snmp_out_toggle_options("n") != NULL) { + upsdebugx(1, "Failed to enable numeric OIDs resolution"); + } + /* Initialize the SNMP library */ (*nut_init_snmp)("nut-scanner"); ip_str = nutscan_ip_iter_init(&ip, start_ip, stop_ip); - while(ip_str != NULL) { - tmp_sec = malloc(sizeof(nutscan_snmp_t)); - memcpy(tmp_sec, sec, sizeof(nutscan_snmp_t)); - tmp_sec->peername = ip_str; - + while (ip_str != NULL) { #ifdef HAVE_PTHREAD - if (pthread_create(&thread,NULL,try_SysOID,(void*)tmp_sec)==0){ - thread_count++; - thread_array = realloc(thread_array, - thread_count*sizeof(pthread_t)); - thread_array[thread_count-1] = thread; + /* NOTE: With many enough targets to scan, this can crash + * by spawning too many children; add a limit and loop to + * "reap" some already done with their work. And probably + * account them in thread_array[] as something to not wait + * for below in pthread_join()... + */ + +# ifdef HAVE_SEMAPHORE + /* Just wait for someone to free a semaphored slot, + * if none are available, and then/otherwise grab one + */ + if (thread_array == NULL) { + /* Starting point, or after a wait to complete + * all earlier runners */ + if (max_threads_scantype > 0) + sem_wait(semaphore_scantype); + sem_wait(semaphore); + pass = TRUE; + } else { + pass = ((max_threads_scantype == 0 || sem_trywait(semaphore_scantype) == 0) && + sem_trywait(semaphore) == 0); } -#else - try_SysOID((void *)tmp_sec); -#endif - ip_str = nutscan_ip_iter_inc(&ip); - }; +# else +# ifdef HAVE_PTHREAD_TRYJOIN + /* A somewhat naive and brute-force solution for + * systems without a semaphore.h. This may suffer + * some off-by-one errors, using a few more threads + * than intended (if we race a bit at the wrong time, + * probably up to one per enabled scanner routine). + */ + + /* TOTHINK: Should there be a threadcount_mutex when + * we just read the value in if() and while() below? + * At worst we would overflow the limit a bit due to + * other protocol scanners... + */ + if (curr_threads >= max_threads + || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) + ) { + upsdebugx(2, "%s: already running %zu scanning threads " + "(launched overall: %zu), " + "waiting until some would finish", + __func__, curr_threads, thread_count); + while (curr_threads >= max_threads + || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) + ) { + for (i = 0; i < thread_count ; i++) { + int ret; + + if (!thread_array[i].active) continue; + + pthread_mutex_lock(&threadcount_mutex); + upsdebugx(3, "%s: Trying to join thread #%i...", __func__, i); + ret = pthread_tryjoin_np(thread_array[i].thread, NULL); + switch (ret) { + case ESRCH: /* No thread with the ID thread could be found - already "joined"? */ + upsdebugx(5, "%s: Was thread #%zu joined earlier?", __func__, i); + break; + case 0: /* thread exited */ + if (curr_threads > 0) { + curr_threads --; + upsdebugx(4, "%s: Joined a finished thread #%zu", __func__, i); + } else { + /* threadcount_mutex fault? */ + upsdebugx(0, "WARNING: %s: Accounting of thread count " + "says we are already at 0", __func__); + } + thread_array[i].active = FALSE; + break; + case EBUSY: /* actively running */ + upsdebugx(6, "%s: thread #%zu still busy (%i)", + __func__, i, ret); + break; + case EDEADLK: /* Errors with thread interactions... bail out? */ + case EINVAL: /* Errors with thread interactions... bail out? */ + default: /* new pthreads abilities? */ + upsdebugx(5, "%s: thread #%zu reported code %i", + __func__, i, ret); + break; + } + pthread_mutex_unlock(&threadcount_mutex); + } + + if (curr_threads >= max_threads + || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) + ) { + usleep (10000); /* microSec's, so 0.01s here */ + } + } + upsdebugx(2, "%s: proceeding with scan", __func__); + } + /* NOTE: No change to default "pass" in this ifdef: + * if we got to this line, we have a slot to use */ +# endif /* HAVE_PTHREAD_TRYJOIN */ +# endif /* HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ + + if (pass) { + tmp_sec = malloc(sizeof(nutscan_snmp_t)); + memcpy(tmp_sec, sec, sizeof(nutscan_snmp_t)); + tmp_sec->peername = ip_str; #ifdef HAVE_PTHREAD - for ( i=0; i < thread_count ; i++) { - pthread_join(thread_array[i],NULL); + if (pthread_create(&thread, NULL, try_SysOID, (void*)tmp_sec) == 0) { +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_lock(&threadcount_mutex); + curr_threads++; +# endif /* HAVE_PTHREAD_TRYJOIN */ + + thread_count++; + nutscan_thread_t *new_thread_array = realloc(thread_array, + thread_count * sizeof(nutscan_thread_t)); + if (new_thread_array == NULL) { + upsdebugx(1, "%s: Failed to realloc thread array", __func__); + break; + } + else { + thread_array = new_thread_array; + } + thread_array[thread_count - 1].thread = thread; + thread_array[thread_count - 1].active = TRUE; + +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_unlock(&threadcount_mutex); +# endif /* HAVE_PTHREAD_TRYJOIN */ + } +#else /* not HAVE_PTHREAD */ + try_SysOID((void *)tmp_sec); +#endif /* if HAVE_PTHREAD */ +/* free(ip_str); */ /* Do not free() here - seems to cause a double-free instead */ + ip_str = nutscan_ip_iter_inc(&ip); +/* free(tmp_sec); */ + } else { /* if not pass -- all slots busy */ +#ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE + /* Wait for all current scans to complete */ + if (thread_array != NULL) { + upsdebugx (2, "%s: Running too many scanning threads, " + "waiting until older ones would finish", + __func__); + for (i = 0; i < thread_count ; i++) { + int ret; + if (!thread_array[i].active) { + /* Probably should not get here, + * but handle it just in case */ + upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %zu to be not active", + __func__, i); + sem_post(semaphore); + if (max_threads_scantype > 0) + sem_post(semaphore_scantype); + continue; + } + thread_array[i].active = FALSE; + ret = pthread_join(thread_array[i].thread, NULL); + if (ret != 0) { + upsdebugx(0, "WARNING: %s: Midway clean-up: pthread_join() returned code %i", + __func__, ret); + } + sem_post(semaphore); + if (max_threads_scantype > 0) + sem_post(semaphore_scantype); + } + thread_count = 0; + free(thread_array); + thread_array = NULL; + } +# else +# ifdef HAVE_PTHREAD_TRYJOIN + /* TODO: Move the wait-loop for TRYJOIN here? */ +# endif /* HAVE_PTHREAD_TRYJOIN */ +# endif /* HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ + } /* if: could we "pass" or not? */ + } /* while */ + +#ifdef HAVE_PTHREAD + if (thread_array != NULL) { + upsdebugx(2, "%s: all planned scans launched, waiting for threads to complete", __func__); + for (i = 0; i < thread_count; i++) { + int ret; + + if (!thread_array[i].active) continue; + + ret = pthread_join(thread_array[i].thread, NULL); + if (ret != 0) { + upsdebugx(0, "WARNING: %s: Clean-up: pthread_join() returned code %i", + __func__, ret); + } + thread_array[i].active = FALSE; +# ifdef HAVE_SEMAPHORE + sem_post(semaphore); + if (max_threads_scantype > 0) + sem_post(semaphore_scantype); +# else +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_lock(&threadcount_mutex); + if (curr_threads > 0) { + curr_threads --; + upsdebugx(5, "%s: Clean-up: Joined a finished thread #%zu", + __func__, i); + } else { + upsdebugx(0, "WARNING: %s: Clean-up: Accounting of thread count " + "says we are already at 0", __func__); + } + pthread_mutex_unlock(&threadcount_mutex); +# endif /* HAVE_PTHREAD_TRYJOIN */ +# endif /* HAVE_SEMAPHORE */ + } + free(thread_array); + upsdebugx(2, "%s: all threads freed", __func__); } pthread_mutex_destroy(&dev_mutex); - free(thread_array); -#endif - return dev_ret; +# ifdef HAVE_SEMAPHORE + if (max_threads_scantype > 0) + sem_destroy(semaphore_scantype); +# endif /* HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ + + nutscan_device_t * result = nutscan_rewind_device(dev_ret); + dev_ret = NULL; + return result; } + #else /* WITH_SNMP */ -nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip,long usec_timeout, nutscan_snmp_t * sec) + +nutscan_device_t * nutscan_scan_snmp(const char * start_ip, const char * stop_ip, + useconds_t usec_timeout, nutscan_snmp_t * sec) { + NUT_UNUSED_VARIABLE(start_ip); + NUT_UNUSED_VARIABLE(stop_ip); + NUT_UNUSED_VARIABLE(usec_timeout); + NUT_UNUSED_VARIABLE(sec); return NULL; } + #endif /* WITH_SNMP */ - - diff --git a/tools/nut-scanner/scan_usb.c b/tools/nut-scanner/scan_usb.c index 3fac8a6..a828827 100644 --- a/tools/nut-scanner/scan_usb.c +++ b/tools/nut-scanner/scan_usb.c @@ -1,6 +1,5 @@ -/* scan_usb.c: detect NUT supported USB devices - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) 2011-2016 - EATON * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,6 +16,12 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/*! \file scan_usb.c + \brief detect NUT supported USB devices + \author Frederic Bohe + \author Arnaud Quette +*/ + #include "common.h" #include "nut-scan.h" @@ -30,85 +35,150 @@ /* dynamic link library stuff */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; -static int (*nut_usb_close)(usb_dev_handle *dev); -static int (*nut_usb_find_busses)(void); -static char * (*nut_usb_strerror)(void); -static void (*nut_usb_init)(void); -static int (*nut_usb_get_string_simple)(usb_dev_handle *dev, int index, +static int (*nut_usb_close)(libusb_device_handle *dev); +static int (*nut_usb_get_string_simple)(libusb_device_handle *dev, int index, char *buf, size_t buflen); -static struct usb_bus * (*nut_usb_busses); -static usb_dev_handle * (*nut_usb_open)(struct usb_device *dev); -static int (*nut_usb_find_devices)(void); -/* return 0 on error */ -int nutscan_load_usb_library() + +/* Compatibility layer between libusb 0.1 and 1.0 */ +#if WITH_LIBUSB_1_0 + #define USB_INIT_SYMBOL "libusb_init" + #define USB_OPEN_SYMBOL "libusb_open" + #define USB_CLOSE_SYMBOL "libusb_close" + #define USB_STRERROR_SYMBOL "libusb_strerror" + static int (*nut_usb_open)(libusb_device *dev, libusb_device_handle **handle); + static int (*nut_usb_init)(libusb_context **ctx); + static void (*nut_usb_exit)(libusb_context *ctx); + static char * (*nut_usb_strerror)(enum libusb_error errcode); + static ssize_t (*nut_usb_get_device_list)(libusb_context *ctx, libusb_device ***list); + static void (*nut_usb_free_device_list)(libusb_device **list, int unref_devices); + static uint8_t (*nut_usb_get_bus_number)(libusb_device *dev); + static int (*nut_usb_get_device_descriptor)(libusb_device *dev, + struct libusb_device_descriptor *desc); +#else + #define USB_INIT_SYMBOL "usb_init" + #define USB_OPEN_SYMBOL "usb_open" + #define USB_CLOSE_SYMBOL "usb_close" + #define USB_STRERROR_SYMBOL "usb_strerror" + static libusb_device_handle * (*nut_usb_open)(struct usb_device *dev); + static void (*nut_usb_init)(void); + static int (*nut_usb_find_busses)(void); + static struct usb_bus * (*nut_usb_busses); + static int (*nut_usb_find_devices)(void); + static char * (*nut_usb_strerror)(void); +#endif + +/* return 0 on error; visible externally */ +int nutscan_load_usb_library(const char *libname_path); +int nutscan_load_usb_library(const char *libname_path) { - if( dl_handle != NULL ) { - /* if previous init failed */ - if( dl_handle == (void *)1 ) { - return 0; - } - /* init has already been done */ - return 1; - } + if (dl_handle != NULL) { + /* if previous init failed */ + if (dl_handle == (void *)1) { + return 0; + } + /* init has already been done */ + return 1; + } - if( lt_dlinit() != 0 ) { + if (libname_path == NULL) { + fprintf(stderr, "USB library not found. USB search disabled.\n"); + return 0; + } + + if (lt_dlinit() != 0) { fprintf(stderr, "Error initializing lt_init\n"); return 0; } - dl_handle = lt_dlopenext("libusb"); - if (!dl_handle) { - dl_error = lt_dlerror(); - goto err; - } - lt_dlerror(); /* Clear any existing error */ - *(void **) (&nut_usb_close) = lt_dlsym(dl_handle, "usb_close"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + dl_handle = lt_dlopen(libname_path); + if (!dl_handle) { + dl_error = lt_dlerror(); + goto err; + } - *(void **) (&nut_usb_find_busses) = lt_dlsym(dl_handle, "usb_find_busses"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_usb_init) = lt_dlsym(dl_handle, USB_INIT_SYMBOL); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_usb_strerror) = lt_dlsym(dl_handle, "usb_strerror"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_usb_open) = lt_dlsym(dl_handle, USB_OPEN_SYMBOL); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_usb_init) = lt_dlsym(dl_handle, "usb_init"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + lt_dlerror(); /* Clear any existing error */ + *(void **) (&nut_usb_close) = lt_dlsym(dl_handle, USB_CLOSE_SYMBOL); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_usb_get_string_simple) = lt_dlsym(dl_handle, - "usb_get_string_simple"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_usb_strerror) = lt_dlsym(dl_handle, USB_STRERROR_SYMBOL); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_usb_busses) = lt_dlsym(dl_handle, "usb_busses"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } +#if WITH_LIBUSB_1_0 + *(void **) (&nut_usb_exit) = lt_dlsym(dl_handle, "libusb_exit"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_usb_open) = lt_dlsym(dl_handle, "usb_open"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_usb_get_device_list) = lt_dlsym(dl_handle, "libusb_get_device_list"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **)(&nut_usb_find_devices) = lt_dlsym(dl_handle,"usb_find_devices"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_usb_free_device_list) = lt_dlsym(dl_handle, "libusb_free_device_list"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_usb_get_bus_number) = lt_dlsym(dl_handle, "libusb_get_bus_number"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_usb_get_device_descriptor) = lt_dlsym(dl_handle, "libusb_get_device_descriptor"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_usb_get_string_simple) = lt_dlsym(dl_handle, + "libusb_get_string_descriptor_ascii"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } +#else /* for libusb 0.1 */ + *(void **) (&nut_usb_find_busses) = lt_dlsym(dl_handle, "usb_find_busses"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_usb_busses) = lt_dlsym(dl_handle, "usb_busses"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **)(&nut_usb_find_devices) = lt_dlsym(dl_handle, "usb_find_devices"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + *(void **) (&nut_usb_get_string_simple) = lt_dlsym(dl_handle, + "usb_get_string_simple"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } +#endif /* WITH_LIBUSB_1_0 */ + + return 1; - return 1; err: - fprintf(stderr, "%s\n", dl_error); - dl_handle = (void *)1; - return 0; + fprintf(stderr, "Cannot load USB library (%s) : %s. USB search disabled.\n", libname_path, dl_error); + dl_handle = (void *)1; + lt_dlexit(); + return 0; } /* end of dynamic link library stuff */ @@ -117,10 +187,10 @@ static char* is_usb_device_supported(usb_device_id_t *usb_device_id_list, { usb_device_id_t *usbdev; - for (usbdev=usb_device_id_list; usbdev->driver_name != NULL; usbdev++) { - if ( (usbdev->vendorID == dev_VendorID) - && (usbdev->productID == dev_ProductID) ) { - + for (usbdev = usb_device_id_list; usbdev->driver_name != NULL; usbdev++) { + if ((usbdev->vendorID == dev_VendorID) + && (usbdev->productID == dev_ProductID) + ) { return usbdev->driver_name; } } @@ -137,128 +207,259 @@ nutscan_device_t * nutscan_scan_usb() char *serialnumber = NULL; char *device_name = NULL; char *vendor_name = NULL; + uint8_t iManufacturer = 0, iProduct = 0, iSerialNumber = 0; + uint16_t VendorID; + uint16_t ProductID; + char *busname; +#if WITH_LIBUSB_1_0 + libusb_device *dev; + libusb_device **devlist; + uint8_t bus; +#else /* => WITH_LIBUSB_0_1 */ struct usb_device *dev; struct usb_bus *bus; - usb_dev_handle *udev; +#endif /* WITH_LIBUSB_1_0 */ + libusb_device_handle *udev; nutscan_device_t * nut_dev = NULL; nutscan_device_t * current_nut_dev = NULL; - if( !nutscan_avail_usb ) { - return NULL; - } + if (!nutscan_avail_usb) { + return NULL; + } /* libusb base init */ + /* Initialize Libusb */ +#if WITH_LIBUSB_1_0 + if ((*nut_usb_init)(NULL) < 0) { + (*nut_usb_exit)(NULL); + fatal_with_errno(EXIT_FAILURE, "Failed to init libusb 1.0"); + } +#else /* => WITH_LIBUSB_0_1 */ (*nut_usb_init)(); (*nut_usb_find_busses)(); (*nut_usb_find_devices)(); +#endif /* WITH_LIBUSB_1_0 */ +#if WITH_LIBUSB_1_0 + ssize_t devcount = 0; + struct libusb_device_descriptor dev_desc; + int i; + + devcount = (*nut_usb_get_device_list)(NULL, &devlist); + if (devcount <= 0) { + (*nut_usb_exit)(NULL); + fatal_with_errno(EXIT_FAILURE, "No USB device found"); + } + + for (i = 0; i < devcount; i++) { + + dev = devlist[i]; + (*nut_usb_get_device_descriptor)(dev, &dev_desc); + + VendorID = dev_desc.idVendor; + ProductID = dev_desc.idProduct; + + iManufacturer = dev_desc.iManufacturer; + iProduct = dev_desc.iProduct; + iSerialNumber = dev_desc.iSerialNumber; + bus = (*nut_usb_get_bus_number)(dev); + busname = (char *)malloc(4); + if (busname == NULL) { + (*nut_usb_free_device_list)(devlist, 1); + (*nut_usb_exit)(NULL); + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } + snprintf(busname, 4, "%03d", bus); +#else /* => WITH_LIBUSB_0_1 */ for (bus = (*nut_usb_busses); bus; bus = bus->next) { for (dev = bus->devices; dev; dev = dev->next) { + + VendorID = dev->descriptor.idVendor; + ProductID = dev->descriptor.idProduct; + + iManufacturer = dev->descriptor.iManufacturer; + iProduct = dev->descriptor.iProduct; + iSerialNumber = dev->descriptor.iSerialNumber; + busname = bus->dirname; +#endif if ((driver_name = is_usb_device_supported(usb_device_table, - dev->descriptor.idVendor, - dev->descriptor.idProduct)) != NULL) { + VendorID, ProductID)) != NULL) { /* open the device */ +#if WITH_LIBUSB_1_0 + ret = (*nut_usb_open)(dev, &udev); + if (!udev || ret != LIBUSB_SUCCESS) { + fprintf(stderr,"Failed to open device " + "bus '%s', skipping: %s\n", + busname, + (*nut_usb_strerror)(ret)); + + /* Note: closing is not applicable + * it seems, and can even segfault + * (even though an udev is not NULL + * when e.g. permissions problem) + */ + + free (busname); + + continue; + } +#else /* => WITH_LIBUSB_0_1 */ udev = (*nut_usb_open)(dev); if (!udev) { - fprintf(stderr,"Failed to open device, \ - skipping. (%s)\n", + /* TOTHINK: any errno or similar to test? */ + fprintf(stderr, "Failed to open device " + "bus '%s',skipping: %s\n", + busname, (*nut_usb_strerror)()); continue; } +#endif /* get serial number */ - if (dev->descriptor.iSerialNumber) { + if (iSerialNumber) { ret = (*nut_usb_get_string_simple)(udev, - dev->descriptor.iSerialNumber, - string, sizeof(string)); + iSerialNumber, string, sizeof(string)); if (ret > 0) { - serialnumber = strdup(string); + serialnumber = strdup(str_rtrim(string, ' ')); + if (serialnumber == NULL) { + (*nut_usb_close)(udev); +#if WITH_LIBUSB_1_0 + free(busname); + (*nut_usb_free_device_list)(devlist, 1); + (*nut_usb_exit)(NULL); +#endif /* WITH_LIBUSB_1_0 */ + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } } } + /* get product name */ - if (dev->descriptor.iProduct) { + if (iProduct) { ret = (*nut_usb_get_string_simple)(udev, - dev->descriptor.iProduct, - string, sizeof(string)); + iProduct, string, sizeof(string)); if (ret > 0) { - device_name = strdup(string); + device_name = strdup(str_rtrim(string, ' ')); + if (device_name == NULL) { + free(serialnumber); + (*nut_usb_close)(udev); +#if WITH_LIBUSB_1_0 + free(busname); + (*nut_usb_free_device_list)(devlist, 1); + (*nut_usb_exit)(NULL); +#endif /* WITH_LIBUSB_1_0 */ + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } } } /* get vendor name */ - if (dev->descriptor.iManufacturer) { + if (iManufacturer) { ret = (*nut_usb_get_string_simple)(udev, - dev->descriptor.iManufacturer, - string, sizeof(string)); + iManufacturer, string, sizeof(string)); if (ret > 0) { - vendor_name = strdup(string); + vendor_name = strdup(str_rtrim(string, ' ')); + if (vendor_name == NULL) { + free(serialnumber); + free(device_name); + (*nut_usb_close)(udev); +#if WITH_LIBUSB_1_0 + free(busname); + (*nut_usb_free_device_list)(devlist, 1); + (*nut_usb_exit)(NULL); +#endif /* WITH_LIBUSB_1_0 */ + fatal_with_errno(EXIT_FAILURE, "Out of memory"); + } } } nut_dev = nutscan_new_device(); - if(nut_dev == NULL) { - fprintf(stderr,"Memory allocation \ - error\n"); + if (nut_dev == NULL) { + fprintf(stderr, + "Memory allocation error\n"); nutscan_free_device(current_nut_dev); free(serialnumber); free(device_name); free(vendor_name); + (*nut_usb_close)(udev); +#if WITH_LIBUSB_1_0 + free(busname); + (*nut_usb_free_device_list)(devlist, 1); + (*nut_usb_exit)(NULL); +#endif /* WITH_LIBUSB_1_0 */ return NULL; } nut_dev->type = TYPE_USB; - if(driver_name) { + if (driver_name) { nut_dev->driver = strdup(driver_name); } nut_dev->port = strdup("auto"); - sprintf(string,"%04X",dev->descriptor.idVendor); - nutscan_add_option_to_device(nut_dev,"vendorid", - string); - sprintf(string,"%04X", - dev->descriptor.idProduct); - nutscan_add_option_to_device(nut_dev,"productid", - string); - if(device_name) { + + sprintf(string, "%04X", VendorID); + nutscan_add_option_to_device(nut_dev, + "vendorid", + string); + + sprintf(string, "%04X", ProductID); + nutscan_add_option_to_device(nut_dev, + "productid", + string); + + if (device_name) { nutscan_add_option_to_device(nut_dev, - "product", - device_name); + "product", + device_name); free(device_name); + device_name = NULL; } - if(serialnumber) { + + if (serialnumber) { nutscan_add_option_to_device(nut_dev, - "serial", - serialnumber); + "serial", + serialnumber); free(serialnumber); + serialnumber = NULL; } - if(vendor_name) { + + if (vendor_name) { nutscan_add_option_to_device(nut_dev, - "vendor", - vendor_name); + "vendor", + vendor_name); free(vendor_name); + vendor_name = NULL; } - nutscan_add_option_to_device(nut_dev,"bus", - bus->dirname); + + nutscan_add_option_to_device(nut_dev, + "bus", + busname); current_nut_dev = nutscan_add_device_to_device( - current_nut_dev, - nut_dev); + current_nut_dev, + nut_dev); memset (string, 0, sizeof(string)); (*nut_usb_close)(udev); } +#if WITH_LIBUSB_0_1 } } +#else /* not WITH_LIBUSB_0_1 */ + free(busname); + } - return current_nut_dev; + (*nut_usb_free_device_list)(devlist, 1); + (*nut_usb_exit)(NULL); +#endif /* WITH_LIBUSB_0_1 */ + + return nutscan_rewind_device(current_nut_dev); } -#else /* WITH_USB */ +#else /* not WITH_USB */ nutscan_device_t * nutscan_scan_usb() { return NULL; } #endif /* WITH_USB */ - diff --git a/tools/nut-scanner/scan_xml_http.c b/tools/nut-scanner/scan_xml_http.c index b66f42f..6676a81 100644 --- a/tools/nut-scanner/scan_xml_http.c +++ b/tools/nut-scanner/scan_xml_http.c @@ -1,6 +1,7 @@ -/* scan_xml_http.c: detect NUT supported XML HTTP devices - * - * Copyright (C) 2011 - Frederic Bohe +/* + * Copyright (C) 2011 - EATON + * Copyright (C) 2016 - EATON - IP addressed XML scan + * Copyright (C) 2016-2021 - EATON - Various threads-related improvements * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -17,8 +18,16 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/*! \file scan_xml_http.c + \brief detect NUT supported XML HTTP devices + \author Frederic Bohe + \author Michal Vyskocil + \author Jim Klimov +*/ + #include "common.h" #include "nut-scan.h" + #ifdef WITH_NEON #include #include @@ -33,6 +42,7 @@ #include /* dynamic link library stuff */ +static char * libname = "libneon"; /* Note: this is for info messages, not the SONAME */ static lt_dlhandle dl_handle = NULL; static const char *dl_error = NULL; @@ -42,198 +52,665 @@ static void (*nut_ne_xml_push_handler)(ne_xml_parser *p, ne_xml_endelm_cb *endelm, void *userdata); static void (*nut_ne_xml_destroy)(ne_xml_parser *p); +static int (*nut_ne_xml_failed)(ne_xml_parser *p); static ne_xml_parser * (*nut_ne_xml_create)(void); static int (*nut_ne_xml_parse)(ne_xml_parser *p, const char *block, size_t len); -/* return 0 on error */ -int nutscan_load_neon_library() +static nutscan_device_t * dev_ret = NULL; +#ifdef HAVE_PTHREAD +static pthread_mutex_t dev_mutex; +#endif + +/* use explicit booleans */ +#ifndef FALSE +typedef enum ebool { FALSE = 0, TRUE } bool_t; +#else +typedef int bool_t; +#endif + +/* return 0 on error; visible externally */ +int nutscan_load_neon_library(const char *libname_path); +int nutscan_load_neon_library(const char *libname_path) { + if (dl_handle != NULL) { + /* if previous init failed */ + if (dl_handle == (void *)1) { + return 0; + } + /* init has already been done */ + return 1; + } - if( dl_handle != NULL ) { - /* if previous init failed */ - if( dl_handle == (void *)1 ) { - return 0; - } - /* init has already been done */ - return 1; - } + if (libname_path == NULL) { + fprintf(stderr, "Neon library not found. XML search disabled.\n"); + return 0; + } - if( lt_dlinit() != 0 ) { - fprintf(stderr, "Error initializing lt_init\n"); - return 0; - } + if (lt_dlinit() != 0) { + fprintf(stderr, "Error initializing lt_init\n"); + return 0; + } - dl_handle = lt_dlopenext("libneon"); - if (!dl_handle) { - dl_error = lt_dlerror(); - goto err; - } + dl_handle = lt_dlopen(libname_path); + if (!dl_handle) { + dl_error = lt_dlerror(); + goto err; + } - lt_dlerror(); /* Clear any existing error */ - *(void **) (&nut_ne_xml_push_handler) = lt_dlsym(dl_handle, - "ne_xml_push_handler"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + lt_dlerror(); /* Clear any existing error */ + *(void **) (&nut_ne_xml_push_handler) = lt_dlsym(dl_handle, + "ne_xml_push_handler"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_ne_xml_destroy) = lt_dlsym(dl_handle,"ne_xml_destroy"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_ne_xml_destroy) = lt_dlsym(dl_handle, "ne_xml_destroy"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_ne_xml_create) = lt_dlsym(dl_handle,"ne_xml_create"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_ne_xml_create) = lt_dlsym(dl_handle, "ne_xml_create"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - *(void **) (&nut_ne_xml_parse) = lt_dlsym(dl_handle,"ne_xml_parse"); - if ((dl_error = lt_dlerror()) != NULL) { - goto err; - } + *(void **) (&nut_ne_xml_parse) = lt_dlsym(dl_handle, "ne_xml_parse"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } - return 1; + *(void **) (&nut_ne_xml_failed) = lt_dlsym(dl_handle, "ne_xml_failed"); + if ((dl_error = lt_dlerror()) != NULL) { + goto err; + } + + return 1; err: - fprintf(stderr, "%s\n", dl_error); - dl_handle = (void *)1; - return 0; + fprintf(stderr, "Cannot load XML library (%s) : %s. XML search disabled.\n", libname, dl_error); + dl_handle = (void *)1; + lt_dlexit(); + return 0; } +/* A start-element callback for element with given namespace/name. */ static int startelm_cb(void *userdata, int parent, const char *nspace, const char *name, const char **atts) { nutscan_device_t * dev = (nutscan_device_t *)userdata; char buf[SMALLBUF]; int i = 0; - while( atts[i] != NULL ) { - if(strcmp(atts[i],"type") == 0) { - snprintf(buf,sizeof(buf),"%s",atts[i+1]); - nutscan_add_option_to_device(dev,"desc",buf); - return 0; + int result = -1; + while (atts[i] != NULL) { + upsdebugx(5, "startelm_cb() : parent=%d nspace='%s' name='%s' atts[%d]='%s' atts[%d]='%s'", + parent, nspace, name, i, atts[i], (i + 1), atts[i + 1]); + /* The Eaton/MGE ePDUs almost exclusively support only XMLv4 protocol + * (only the very first generation of G2/G3 NMCs supported an older + * protocol, but all should have been FW upgraded by now), which NUT + * drivers don't yet support. To avoid failing drivers later, the + * nut-scanner should not suggest netxml-ups configuration for ePDUs + * at this time. */ + if (strcmp(atts[i], "class") == 0 && strcmp(atts[i + 1], "DEV.PDU") == 0) { + upsdebugx(3, "startelm_cb() : XML v4 protocol is not supported by current NUT drivers, skipping device!"); + /* netxml-ups currently only supports XML version 3 (for UPS), + * and not version 4 (for UPS and PDU)! */ + return -1; } - i=i+2; + if (strcmp(atts[i], "type") == 0) { + snprintf(buf, sizeof(buf), "%s", atts[i + 1]); + nutscan_add_option_to_device(dev, "desc", buf); + result = 0; + } + i = i + 2; } - return 0; + return result; } -nutscan_device_t * nutscan_scan_xml_http(long usec_timeout) +static void * nutscan_scan_xml_http_generic(void * arg) { + nutscan_xml_t * sec = (nutscan_xml_t *)arg; char *scanMsg = ""; - int port = 4679; +/* Note: at this time the HTTP/XML scan is in fact not implemented - just the UDP part */ +/* uint16_t port_http = 80; */ + uint16_t port_udp = 4679; +/* A NULL "ip" causes a broadcast scan; otherwise the single ip address is queried directly */ + char *ip = NULL; + useconds_t usec_timeout = 0; int peerSocket; int sockopt_on = 1; - struct sockaddr_in sockAddress; - socklen_t sockAddressLength = sizeof(sockAddress); - memset(&sockAddress, 0, sizeof(sockAddress)); + struct sockaddr_in sockAddress_udp; + socklen_t sockAddressLength = sizeof(sockAddress_udp); + memset(&sockAddress_udp, 0, sizeof(sockAddress_udp)); fd_set fds; struct timeval timeout; int ret; - char buf[SMALLBUF]; + char buf[SMALLBUF + 8]; char string[SMALLBUF]; ssize_t recv_size; + int i; nutscan_device_t * nut_dev = NULL; - nutscan_device_t * current_nut_dev = NULL; + if (sec != NULL) { +/* if (sec->port_http > 0 && sec->port_http <= 65534) + * port_http = sec->port_http; */ + if (sec->port_udp > 0 && sec->port_udp <= 65534) + port_udp = sec->port_udp; + if (sec->usec_timeout > 0) + usec_timeout = sec->usec_timeout; + ip = sec->peername; /* NULL or not... */ + } - if( !nutscan_avail_xml_http ) { - return NULL; - } + if (usec_timeout <= 0) + usec_timeout = 5000000; /* Driver default : 5sec */ - if((peerSocket = socket(AF_INET, SOCK_DGRAM, 0)) != -1) - { + if (!nutscan_avail_xml_http) { + return NULL; + } + + if ((peerSocket = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + fprintf(stderr, "Error creating socket\n"); + return NULL; + } + +/* FIXME : Per http://stackoverflow.com/questions/683624/udp-broadcast-on-all-interfaces + * A single sendto() generates a single packet, so one must iterate all known interfaces... */ +#define MAX_RETRIES 3 + for (i = 0; i != MAX_RETRIES ; i++) { /* Initialize socket */ - sockAddress.sin_family = AF_INET; - sockAddress.sin_addr.s_addr = INADDR_BROADCAST; - sockAddress.sin_port = htons(port); - setsockopt(peerSocket, SOL_SOCKET, SO_BROADCAST, &sockopt_on, + sockAddress_udp.sin_family = AF_INET; + if (ip == NULL) { + upsdebugx(2, + "nutscan_scan_xml_http_generic() : scanning connected network segment(s) " + "with a broadcast, attempt %d of %d with a timeout of %jd usec", + (i + 1), MAX_RETRIES, (uintmax_t)usec_timeout); + sockAddress_udp.sin_addr.s_addr = INADDR_BROADCAST; + setsockopt(peerSocket, SOL_SOCKET, SO_BROADCAST, &sockopt_on, sizeof(sockopt_on)); + } else { + upsdebugx(2, + "nutscan_scan_xml_http_generic() : scanning IP '%s' with a unicast, " + "attempt %d of %d with a timeout of %jd usec", + ip, (i + 1), MAX_RETRIES, (uintmax_t)usec_timeout); + inet_pton(AF_INET, ip, &(sockAddress_udp.sin_addr)); + } + sockAddress_udp.sin_port = htons(port_udp); /* Send scan request */ - if(sendto(peerSocket, scanMsg, strlen(scanMsg), 0, - (struct sockaddr *)&sockAddress, - sockAddressLength) <= 0) - { - fprintf(stderr,"Error sending Eaton \n"); + if (sendto(peerSocket, scanMsg, strlen(scanMsg), 0, + (struct sockaddr *)&sockAddress_udp, + sockAddressLength) <= 0 + ) { + fprintf(stderr, + "Error sending Eaton to %s, #%d/%d\n", + (ip ? ip : ""), (i + 1), MAX_RETRIES); + usleep(usec_timeout); + continue; } else { + int retNum = 0; FD_ZERO(&fds); - FD_SET(peerSocket,&fds); + FD_SET(peerSocket, &fds); timeout.tv_sec = usec_timeout / 1000000; timeout.tv_usec = usec_timeout % 1000000; - while ((ret=select(peerSocket+1,&fds,NULL,NULL, - &timeout) )) { + upsdebugx(5, "nutscan_scan_xml_http_generic() : sent request to %s, " + "loop #%d/%d, waiting for responses", + (ip ? ip : ""), (i + 1), MAX_RETRIES); + while ((ret = select(peerSocket + 1, &fds, NULL, NULL, + &timeout)) + ) { + retNum ++; + upsdebugx(5, "nutscan_scan_xml_http_generic() : request to %s, " + "loop #%d/%d, response #%d", + (ip ? ip : ""), (i + 1), MAX_RETRIES, retNum); timeout.tv_sec = usec_timeout / 1000000; timeout.tv_usec = usec_timeout % 1000000; - if( ret == -1 ) { + if (ret == -1) { fprintf(stderr, "Error waiting on \ - socket: %d\n",errno); + socket: %d\n", errno); break; } sockAddressLength = sizeof(struct sockaddr_in); - recv_size = recvfrom(peerSocket,buf, - sizeof(buf),0, - (struct sockaddr *)&sockAddress, - &sockAddressLength); + recv_size = recvfrom(peerSocket, buf, + sizeof(buf), 0, + (struct sockaddr *)&sockAddress_udp, + &sockAddressLength); - if(recv_size==-1) { + if (recv_size < 0) { fprintf(stderr, "Error reading \ - socket: %d\n",errno); + socket: %d, #%d/%d\n", errno, (i + 1), MAX_RETRIES); + usleep(usec_timeout); continue; } - if( getnameinfo( - (struct sockaddr *)&sockAddress, - sizeof(struct sockaddr_in),string, - sizeof(string),NULL,0, - NI_NUMERICHOST) != 0) { + if (getnameinfo( + (struct sockaddr *)&sockAddress_udp, + sizeof(struct sockaddr_in), string, + sizeof(string), NULL, 0, + NI_NUMERICHOST) != 0 + ) { fprintf(stderr, - "Error converting IP address \ - : %d\n",errno); + "Error converting IP address: %d\n", errno); + usleep(usec_timeout); continue; } - nut_dev = nutscan_new_device(); - if(nut_dev == NULL) { - fprintf(stderr,"Memory allocation \ - error\n"); - return NULL; - } + nut_dev = nutscan_new_device(); + if (nut_dev == NULL) { + fprintf(stderr, "Memory allocation error\n"); + goto end_abort; + } - nut_dev->type = TYPE_XML; +#ifdef HAVE_PTHREAD + pthread_mutex_lock(&dev_mutex); +#endif + upsdebugx(5, + "Some host at IP %s replied to NetXML UDP request on port %d, " + "inspecting the response...", + string, port_udp); + nut_dev->type = TYPE_XML; /* Try to read device type */ ne_xml_parser *parser = (*nut_ne_xml_create)(); (*nut_ne_xml_push_handler)(parser, startelm_cb, NULL, NULL, nut_dev); - (*nut_ne_xml_parse)(parser, buf, strlen(buf)); + /* recv_size is a ssize_t, so in range of size_t */ + (*nut_ne_xml_parse)(parser, buf, (size_t)recv_size); + int parserFailed = (*nut_ne_xml_failed)(parser); /* 0 = ok, nonzero = fail */ (*nut_ne_xml_destroy)(parser); - nut_dev->driver = strdup("netxml-ups"); - sprintf(buf,"http://%s",string); - nut_dev->port = strdup(buf); - - current_nut_dev = nutscan_add_device_to_device( - current_nut_dev,nut_dev); + if (parserFailed == 0) { + nut_dev->driver = strdup("netxml-ups"); + sprintf(buf, "http://%s", string); + nut_dev->port = strdup(buf); + upsdebugx(3, + "nutscan_scan_xml_http_generic(): " + "Adding configuration for driver='%s' port='%s'", + nut_dev->driver, nut_dev->port); + dev_ret = nutscan_add_device_to_device( + dev_ret, nut_dev); +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&dev_mutex); +#endif + } + else + { + fprintf(stderr, + "Device at IP %s replied with NetXML but was not deemed compatible " + "with 'netxml-ups' driver (unsupported protocol version, etc.)\n", + string); + nutscan_free_device(nut_dev); + nut_dev = NULL; +#ifdef HAVE_PTHREAD + pthread_mutex_unlock(&dev_mutex); +#endif + if (ip == NULL) { + /* skip this device; note that for a + * broadcast scan there may be more + * in the loop's queue */ + continue; + } + } + if (ip != NULL) { + upsdebugx(2, + "nutscan_scan_xml_http_generic(): we collected one reply " + "to unicast for %s (repsponse from %s), done", + ip, string); + goto end; + } + } /* while select() responses */ + if (ip == NULL && dev_ret != NULL) { + upsdebugx(2, + "nutscan_scan_xml_http_generic(): we collected one round of replies " + "to broadcast with no errors, done"); + goto end; } } } - else - { - fprintf(stderr,"Error creating socket\n"); - } + upsdebugx(2, + "nutscan_scan_xml_http_generic(): no replies collected for %s, done", + (ip ? ip : "")); + goto end; - - return current_nut_dev; -} -#else /* WITH_NEON */ -nutscan_device_t * nutscan_scan_xml_http(long usec_timeout) -{ +end_abort: + upsdebugx(1, + "Had to abort nutscan_scan_xml_http_generic() for %s, see fatal details above", + (ip ? ip : "")); +end: + if (ip != NULL) /* do not free "ip", it comes from caller */ + close(peerSocket); return NULL; } + +nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char * end_ip, useconds_t usec_timeout, nutscan_xml_t * sec) +{ + bool_t pass = TRUE; /* Track that we may spawn a scanning thread */ + nutscan_xml_t * tmp_sec = NULL; + nutscan_device_t * result = NULL; + + if (!nutscan_avail_xml_http) { + return NULL; + } + + if (start_ip == NULL && end_ip != NULL) { + start_ip = end_ip; + } + + if (start_ip == NULL) { + upsdebugx(1, "Scanning XML/HTTP bus using broadcast."); + } else { + if ((start_ip == end_ip) || (end_ip == NULL) || (0 == strncmp(start_ip, end_ip, 128))) { + upsdebugx(1, "Scanning XML/HTTP bus for single IP (%s).", start_ip); + } else { + /* Iterate the range of IPs to scan */ + nutscan_ip_iter_t ip; + char * ip_str = NULL; +#ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE + sem_t * semaphore = nutscan_semaphore(); + sem_t semaphore_scantype_inst; + sem_t * semaphore_scantype = &semaphore_scantype_inst; +# endif /* HAVE_SEMAPHORE */ + pthread_t thread; + nutscan_thread_t * thread_array = NULL; + size_t thread_count = 0, i; +# if (defined HAVE_PTHREAD_TRYJOIN) || (defined HAVE_SEMAPHORE) + size_t max_threads_scantype = max_threads_netxml; +# endif + + pthread_mutex_init(&dev_mutex, NULL); + +# ifdef HAVE_SEMAPHORE + if (max_threads_scantype > 0) { + if (SIZE_MAX > UINT_MAX && max_threads_scantype > UINT_MAX) { + upsdebugx(1, + "WARNING: %s: Limiting max_threads_scantype to range acceptable for sem_init()", + __func__); + max_threads_scantype = UINT_MAX - 1; + } + sem_init(semaphore_scantype, 0, (unsigned int)max_threads_scantype); + } +# endif /* HAVE_SEMAPHORE */ + +#endif /* HAVE_PTHREAD */ + + ip_str = nutscan_ip_iter_init(&ip, start_ip, end_ip); + + while (ip_str != NULL) { +#ifdef HAVE_PTHREAD + /* NOTE: With many enough targets to scan, this can crash + * by spawning too many children; add a limit and loop to + * "reap" some already done with their work. And probably + * account them in thread_array[] as something to not wait + * for below in pthread_join()... + */ + +# ifdef HAVE_SEMAPHORE + /* Just wait for someone to free a semaphored slot, + * if none are available, and then/otherwise grab one + */ + if (thread_array == NULL) { + /* Starting point, or after a wait to complete + * all earlier runners */ + if (max_threads_scantype > 0) + sem_wait(semaphore_scantype); + sem_wait(semaphore); + pass = TRUE; + } else { + pass = ((max_threads_scantype == 0 || sem_trywait(semaphore_scantype) == 0) && + sem_trywait(semaphore) == 0); + } +# else +# ifdef HAVE_PTHREAD_TRYJOIN + /* A somewhat naive and brute-force solution for + * systems without a semaphore.h. This may suffer + * some off-by-one errors, using a few more threads + * than intended (if we race a bit at the wrong time, + * probably up to one per enabled scanner routine). + */ + + /* TOTHINK: Should there be a threadcount_mutex when + * we just read the value in if() and while() below? + * At worst we would overflow the limit a bit due to + * other protocol scanners... + */ + if (curr_threads >= max_threads + || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) + ) { + upsdebugx(2, "%s: already running %zu scanning threads " + "(launched overall: %zu), " + "waiting until some would finish", + __func__, curr_threads, thread_count); + while (curr_threads >= max_threads + || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) + ) { + for (i = 0; i < thread_count ; i++) { + int ret; + + if (!thread_array[i].active) continue; + + pthread_mutex_lock(&threadcount_mutex); + upsdebugx(3, "%s: Trying to join thread #%i...", __func__, i); + ret = pthread_tryjoin_np(thread_array[i].thread, NULL); + switch (ret) { + case ESRCH: /* No thread with the ID thread could be found - already "joined"? */ + upsdebugx(5, "%s: Was thread #%zu joined earlier?", __func__, i); + break; + case 0: /* thread exited */ + if (curr_threads > 0) { + curr_threads --; + upsdebugx(4, "%s: Joined a finished thread #%zu", __func__, i); + } else { + /* threadcount_mutex fault? */ + upsdebugx(0, "WARNING: %s: Accounting of thread count " + "says we are already at 0", __func__); + } + thread_array[i].active = FALSE; + break; + case EBUSY: /* actively running */ + upsdebugx(6, "%s: thread #%zu still busy (%i)", + __func__, i, ret); + break; + case EDEADLK: /* Errors with thread interactions... bail out? */ + case EINVAL: /* Errors with thread interactions... bail out? */ + default: /* new pthreads abilities? */ + upsdebugx(5, "%s: thread #%zu reported code %i", + __func__, i, ret); + break; + } + pthread_mutex_unlock(&threadcount_mutex); + } + + if (curr_threads >= max_threads + || (curr_threads >= max_threads_scantype && max_threads_scantype > 0) + ) { + usleep (10000); /* microSec's, so 0.01s here */ + } + } + upsdebugx(2, "%s: proceeding with scan", __func__); + } + /* NOTE: No change to default "pass" in this ifdef: + * if we got to this line, we have a slot to use */ +# endif /* HAVE_PTHREAD_TRYJOIN */ +# endif /* HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ + + if (pass) { + tmp_sec = malloc(sizeof(nutscan_xml_t)); + if (tmp_sec == NULL) { + fprintf(stderr, + "Memory allocation error\n"); + return NULL; + } + memcpy(tmp_sec, sec, sizeof(nutscan_xml_t)); + tmp_sec->peername = ip_str; + if (tmp_sec->usec_timeout <= 0) { + tmp_sec->usec_timeout = usec_timeout; + } + +#ifdef HAVE_PTHREAD + if (pthread_create(&thread, NULL, nutscan_scan_xml_http_generic, (void *)tmp_sec) == 0) { +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_lock(&threadcount_mutex); + curr_threads++; +# endif /* HAVE_PTHREAD_TRYJOIN */ + + thread_count++; + nutscan_thread_t *new_thread_array = realloc(thread_array, + thread_count*sizeof(nutscan_thread_t)); + if (new_thread_array == NULL) { + upsdebugx(1, "%s: Failed to realloc thread array", __func__); + break; + } + else { + thread_array = new_thread_array; + } + thread_array[thread_count - 1].thread = thread; + thread_array[thread_count - 1].active = TRUE; + +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_unlock(&threadcount_mutex); +# endif /* HAVE_PTHREAD_TRYJOIN */ + } +#else /* not HAVE_PTHREAD */ + nutscan_scan_xml_http_generic((void *)tmp_sec); +#endif /* if HAVE_PTHREAD */ + +/* free(ip_str); */ /* One of these free()s seems to cause a double-free instead */ + ip_str = nutscan_ip_iter_inc(&ip); +/* free(tmp_sec); */ + } else { /* if not pass -- all slots busy */ +#ifdef HAVE_PTHREAD +# ifdef HAVE_SEMAPHORE + /* Wait for all current scans to complete */ + if (thread_array != NULL) { + upsdebugx (2, "%s: Running too many scanning threads, " + "waiting until older ones would finish", + __func__); + for (i = 0; i < thread_count ; i++) { + int ret; + if (!thread_array[i].active) { + /* Probably should not get here, + * but handle it just in case */ + upsdebugx(0, "WARNING: %s: Midway clean-up: did not expect thread %zu to be not active", + __func__, i); + sem_post(semaphore); + if (max_threads_scantype > 0) + sem_post(semaphore_scantype); + continue; + } + thread_array[i].active = FALSE; + ret = pthread_join(thread_array[i].thread, NULL); + if (ret != 0) { + upsdebugx(0, "WARNING: %s: Midway clean-up: pthread_join() returned code %i", + __func__, ret); + } + sem_post(semaphore); + if (max_threads_scantype > 0) + sem_post(semaphore_scantype); + } + thread_count = 0; + free(thread_array); + thread_array = NULL; + } +# else +# ifdef HAVE_PTHREAD_TRYJOIN + /* TODO: Move the wait-loop for TRYJOIN here? */ +# endif /* HAVE_PTHREAD_TRYJOIN */ +# endif /* HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ + } /* if: could we "pass" or not? */ + } /* while */ + +#ifdef HAVE_PTHREAD + if (thread_array != NULL) { + upsdebugx(2, "%s: all planned scans launched, waiting for threads to complete", __func__); + for (i = 0; i < thread_count; i++) { + int ret; + + if (!thread_array[i].active) continue; + + ret = pthread_join(thread_array[i].thread, NULL); + if (ret != 0) { + upsdebugx(0, "WARNING: %s: Clean-up: pthread_join() returned code %i", + __func__, ret); + } + thread_array[i].active = FALSE; +# ifdef HAVE_SEMAPHORE + sem_post(semaphore); + if (max_threads_scantype > 0) + sem_post(semaphore_scantype); +# else +# ifdef HAVE_PTHREAD_TRYJOIN + pthread_mutex_lock(&threadcount_mutex); + if (curr_threads > 0) { + curr_threads --; + upsdebugx(5, "%s: Clean-up: Joined a finished thread #%zu", + __func__, i); + } else { + upsdebugx(0, "WARNING: %s: Clean-up: Accounting of thread count " + "says we are already at 0", __func__); + } + pthread_mutex_unlock(&threadcount_mutex); +# endif /* HAVE_PTHREAD_TRYJOIN */ +# endif /* HAVE_SEMAPHORE */ + } + free(thread_array); + upsdebugx(2, "%s: all threads freed", __func__); + } + pthread_mutex_destroy(&dev_mutex); + +# ifdef HAVE_SEMAPHORE + if (max_threads_scantype > 0) + sem_destroy(semaphore_scantype); +# endif /* HAVE_SEMAPHORE */ +#endif /* HAVE_PTHREAD */ + + result = nutscan_rewind_device(dev_ret); + dev_ret = NULL; + return result; + } + } + + tmp_sec = malloc(sizeof(nutscan_xml_t)); + if (tmp_sec == NULL) { + fprintf(stderr, + "Memory allocation error\n"); + return NULL; + } + + memcpy(tmp_sec, sec, sizeof(nutscan_xml_t)); + if (start_ip == NULL) { + tmp_sec->peername = NULL; + } else { + tmp_sec->peername = strdup(start_ip); + } + + if (tmp_sec->usec_timeout <= 0) { + tmp_sec->usec_timeout = usec_timeout; + } + + nutscan_scan_xml_http_generic(tmp_sec); + result = nutscan_rewind_device(dev_ret); + dev_ret = NULL; + free(tmp_sec); + return result; +} + +#else /* WITH_NEON */ + +nutscan_device_t * nutscan_scan_xml_http_range(const char * start_ip, const char * end_ip, useconds_t usec_timeout, nutscan_xml_t * sec) +{ + NUT_UNUSED_VARIABLE(start_ip); + NUT_UNUSED_VARIABLE(end_ip); + NUT_UNUSED_VARIABLE(usec_timeout); + NUT_UNUSED_VARIABLE(sec); + return NULL; +} + #endif /* WITH_NEON */ diff --git a/tools/nut-snmpinfo.py b/tools/nut-snmpinfo.py.in similarity index 66% rename from tools/nut-snmpinfo.py rename to tools/nut-snmpinfo.py.in index 89cc359..c76e9de 100755 --- a/tools/nut-snmpinfo.py +++ b/tools/nut-snmpinfo.py.in @@ -1,5 +1,7 @@ -#!/usr/bin/env python -# Copyright (C) 2011 - Frederic Bohe +#!@PYTHON@ +# Copyright (C) 2011-2021 Eaton +# Authors: Frederic Bohe +# Arnaud Quette # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -15,13 +17,22 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -# This program extracts all SNMP information related to NUT snmp-ups -# drivers. +# This program extracts all SNMP information related to NUT snmp-ups drivers. import glob import re +import sys +import os -output_file_name="./nut-scanner/nutscan-snmp.h" +TOP_SRCDIR = os.getenv('TOP_SRCDIR') +if TOP_SRCDIR is None: + TOP_SRCDIR=".." + +TOP_BUILDDIR = os.getenv('TOP_BUILDDIR') +if TOP_BUILDDIR is None: + TOP_BUILDDIR=".." + +output_file_name = TOP_BUILDDIR + "/tools/nut-scanner/nutscan-snmp.h" output_file = open(output_file_name,'w') #expand #define constant @@ -32,7 +43,16 @@ def expand_define(filename,constant): if constant in line and "#define" in line: line_without_carriage_return = re.sub("[\n\r]", "", line) line_with_single_blank = re.sub("[ \t]+", " ", line_without_carriage_return) - define_line = line_with_single_blank.split(" "); + line_without_eol_comments = line_with_single_blank + # Note: per precedent in code, this assumes a single-line /* ... */ trailing comment + i = line_without_eol_comments.find('/*') + if i >= 0: + line_without_eol_comments = line_without_eol_comments[:i] + i = line_without_eol_comments.find('//') + if i >= 0: + line_without_eol_comments = line_without_eol_comments[:i] + line_without_eol_comments = line_without_eol_comments.strip() + define_line = line_without_eol_comments.split(" ") #define_line[0] = "#define" #define_line[1] = const name #define_line[2...] = const value (may be other const name) @@ -44,12 +64,14 @@ def expand_define(filename,constant): clean_elem = re.sub("\"", "", elem) ret_line = ret_line + clean_elem else: - ret_line = ret_line + expand_define(filename,elem); + ret_line = ret_line + expand_define(filename,elem) return ret_line -output_file.write( "/* nutscan-snmp\n" ) -output_file.write( " * Copyright (C) 2011 - Frederic Bohe \n" ) +output_file.write( "/* nutscan-snmp.h - fully generated during build of NUT\n" ) +output_file.write( " * Copyright (C) 2011-2019 EATON\n" ) +output_file.write( " * Authors: Frederic Bohe \n" ) +output_file.write( " * Arnaud Quette \n" ) output_file.write( " *\n" ) output_file.write( " * This program is free software; you can redistribute it and/or modify\n" ) output_file.write( " * it under the terms of the GNU General Public License as published by\n" ) @@ -70,25 +92,32 @@ output_file.write( "#ifndef DEVSCAN_SNMP_H\n" ) output_file.write( "#define DEVSCAN_SNMP_H\n" ) output_file.write( "\n" ) output_file.write( "typedef struct {\n" ) -output_file.write( " char * oid;\n" ) -output_file.write( " char * mib;\n" ) -output_file.write( " char * sysoid;\n" ) +output_file.write( "\tchar *\toid;\n" ) +output_file.write( "\tchar *\tmib;\n" ) +output_file.write( "\tchar *\tsysoid;\n" ) output_file.write( "} snmp_device_id_t;\n" ) output_file.write( "\n" ) output_file.write( "/* SNMP IDs device table */\n" ) output_file.write( "static snmp_device_id_t snmp_device_table[] = {\n" ) -for filename in glob.glob('../drivers/*-mib.c'): +for filename in sorted(glob.glob(TOP_SRCDIR + '/drivers/*-mib.c')): list_of_line = open(filename,'r').read().split(';') for line in list_of_line: if "mib2nut_info_t" in line: + # Discard commented lines + # Note that we only search for the beginning of the comment, the + # end can be in the following line, due to the .split(';') + m = re.search(r'/\*.*', line) + if m: + #sys.stderr.write('discarding line'+line+'\n') + continue #clean up line line2 = re.sub("[\n\t\r}]", "", line) # split line line = line2.split("{",1) #line[1] is the part between {} line2 = line[1].split(",") - mib = line2[0] + mib = line2[0].lstrip(" ") #line2[3] is the OID of the device model name which #could be made of #define const and string. source_oid = line2[3] @@ -109,7 +138,7 @@ for filename in glob.glob('../drivers/*-mib.c'): clean_elem = re.sub("\"", "", elem) oid = oid+clean_elem else: - oid = oid + expand_define(filename,elem); + oid = oid + expand_define(filename,elem) #decode source_sysoid line = source_sysoid.lstrip(" ") @@ -122,16 +151,22 @@ for filename in glob.glob('../drivers/*-mib.c'): clean_elem = re.sub("\"", "", elem) sysoid = sysoid+clean_elem else: - sysoid = sysoid + expand_define(filename,elem); + sysoid = sysoid + expand_define(filename,elem) + # Sanity checks if sysoid == "": sysoid = "NULL" else: sysoid = "\"" + sysoid + "\"" - output_file.write( "\t{ \"" + oid + "\" , " + mib + ", " + sysoid + "},\n" ) + if oid == "": + oid = "NULL" + else: + oid = "\"" + oid + "\"" -output_file.write( " /* Terminating entry */\n" ) -output_file.write( " { NULL, NULL, NULL}\n" ) + output_file.write( "\t{ " + oid + ", " + mib + ", " + sysoid + " },\n" ) + +output_file.write( "\t/* Terminating entry */\n" ) +output_file.write( "\t{ NULL, NULL, NULL }\n" ) output_file.write( "};\n" ) output_file.write( "#endif /* DEVSCAN_SNMP_H */\n" ) diff --git a/tools/nut-usbinfo.pl b/tools/nut-usbinfo.pl index 7833086..9fd07a1 100755 --- a/tools/nut-usbinfo.pl +++ b/tools/nut-usbinfo.pl @@ -1,8 +1,8 @@ #!/usr/bin/env perl -# Current Version : 1.1 -# Copyright (C) 2008 - 2011 -# Arnaud Quette -# dloic (loic.dardant AT gmail DOT com) +# Current Version : 1.3 +# Copyright (C) 2008 - 2012 dloic (loic.dardant AT gmail DOT com) +# Copyright (C) 2008 - 2015 Arnaud Quette +# Copyright (C) 2013 - 2014 Charles Lepple # # Based on the usbdevice.pl script, made for the Ubuntu Media Center # for the final use of the LIRC project. @@ -20,31 +20,42 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - + +# TODO list: +# - rewrite using glob, as in other helper scripts +# - manage deps in Makefile.am + use File::Find; use strict; -# path to scan for USB_DEVICE pattern -my $scanPath="../drivers"; -# HAL output file -my $outputHAL="../scripts/hal/ups-nut-device.fdi.in"; +my $TOP_SRCDIR = ".."; +if (defined $ENV{'TOP_SRCDIR'}) { + $TOP_SRCDIR = $ENV{'TOP_SRCDIR'}; +} + +my $TOP_BUILDDIR = ".."; +if (defined $ENV{'TOP_BUILDDIR'}) { + $TOP_BUILDDIR = $ENV{'TOP_BUILDDIR'}; +} + +# path to scan for USB_DEVICE pattern +my $scanPath="$TOP_SRCDIR/drivers"; # Hotplug output file -my $outputHotplug="../scripts/hotplug/libhid.usermap"; +my $outputHotplug="$TOP_BUILDDIR/scripts/hotplug/libhid.usermap"; # udev output file -my $outputUdev="../scripts/udev/nut-usbups.rules.in"; +my $outputUdev="$TOP_BUILDDIR/scripts/udev/nut-usbups.rules.in"; + +# BSD devd output file +my $output_devd="$TOP_BUILDDIR/scripts/devd/nut-usb.conf.in"; # UPower output file -my $outputUPower="../scripts/upower/95-upower-hid.rules"; -# tmp output, to allow generating the ENV{UPOWER_VENDOR} header list -my $tmpOutputUPower; -# mfr header flag -my $upowerMfrHeaderDone = 0; +my $outputUPower="$TOP_BUILDDIR/scripts/upower/95-upower-hid.hwdb"; # NUT device scanner - C header -my $outputDevScanner = "./nut-scanner/nutscan-usb.h"; +my $outputDevScanner = "$TOP_BUILDDIR/tools/nut-scanner/nutscan-usb.h"; my $GPL_header = "\ * Copyright (C) 2011 - Arnaud Quette \ @@ -63,7 +74,7 @@ my $GPL_header = "\ * along with this program; if not, write to the Free Software\ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"; -# array of products indexed by vendorID +# array of products indexed by vendorID my %vendor; # contain for each vendor, its name (and...) @@ -71,19 +82,12 @@ my %vendorName; ################# MAIN ################# -find(\&find_usbdevs,$scanPath); +find({wanted=>\&find_usbdevs, preprocess=>sub{sort @_}}, $scanPath); &gen_usb_files; ################# SUB METHOD ################# sub gen_usb_files { - # HAL file header - open my $outHAL, ">$outputHAL" || die "error $outputHAL : $!"; - print $outHAL ' '."\n"; - print $outHAL ''."\n"; - print $outHAL ' '."\n"; - print $outHAL ' '."\n"; - # Hotplug file header open my $outHotplug, ">$outputHotplug" || die "error $outputHotplug : $!"; print $outHotplug '# This file is generated and installed by the Network UPS Tools package.'."\n"; @@ -99,27 +103,48 @@ sub gen_usb_files # Udev file header open my $outUdev, ">$outputUdev" || die "error $outputUdev : $!"; print $outUdev '# This file is generated and installed by the Network UPS Tools package.'."\n\n"; - print $outUdev 'ACTION!="add|change", GOTO="nut-usbups_rules_end"'."\n"; + print $outUdev 'ACTION=="remove", GOTO="nut-usbups_rules_end"'."\n"; print $outUdev 'SUBSYSTEM=="usb_device", GOTO="nut-usbups_rules_real"'."\n"; print $outUdev 'SUBSYSTEM=="usb", GOTO="nut-usbups_rules_real"'."\n"; - print $outUdev 'SUBSYSTEM!="usb", GOTO="nut-usbups_rules_end"'."\n\n"; + print $outUdev 'GOTO="nut-usbups_rules_end"'."\n\n"; print $outUdev 'LABEL="nut-usbups_rules_real"'."\n"; + open my $out_devd, ">$output_devd" || die "error $output_devd : $!"; + print $out_devd '# This file is generated and installed by the Network UPS Tools package.'."\n"; + print $out_devd "# Homepage: http://www.networkupstools.org/\n\n"; + # UPower file header open my $outputUPower, ">$outputUPower" || die "error $outputUPower : $!"; print $outputUPower '##############################################################################################################'."\n"; print $outputUPower '# Uninterruptible Power Supplies with USB HID interfaces'."\n#\n"; - print $outputUPower '# to keep up to date, monitor: http://svn.debian.org/wsvn/nut/trunk/scripts/upower/95-upower-hid.rules'."\n\n"; - print $outputUPower '# only support USB, else ignore'."\n".'SUBSYSTEM!="usb", GOTO="up_hid_end"'."\n\n"; - print $outputUPower '# if usbraw device, ignore'."\n".'KERNEL!="hiddev*", GOTO="up_hid_end"'."\n\n"; - print $outputUPower '# if an interface, ignore'."\n".'ENV{DEVTYPE}=="usb_interface", GOTO="up_hid_end"'."\n\n"; + print $outputUPower '# This file was automatically generated by NUT:'."\n"; + print $outputUPower '# https://github.com/networkupstools/nut/'."\n#\n"; + print $outputUPower '# To keep up to date, monitor upstream NUT'."\n"; + print $outputUPower '# https://github.com/networkupstools/nut/commits/master/scripts/upower/95-upower-hid.hwdb'."\n"; + print $outputUPower "# or checkout the NUT repository and call 'tools/nut-usbinfo.pl'\n"; # Device scanner header open my $outputDevScanner, ">$outputDevScanner" || die "error $outputDevScanner : $!"; print $outputDevScanner '/* nutscan-usb'.$GPL_header."\n */\n\n"; print $outputDevScanner "#ifndef DEVSCAN_USB_H\n#define DEVSCAN_USB_H\n\n"; - print $outputDevScanner "#include \n"; - print $outputDevScanner "#include \"nut_stdint.h\"\t/* for uint16_t */\n\n"; + print $outputDevScanner "#include \"nut_stdint.h\"\t/* for uint16_t etc. */\n\n"; + print $outputDevScanner "#include \t/* for PATH_MAX in usb.h etc. */\n\n"; + print $outputDevScanner "#include \t/* for MAXPATHLEN etc. */\n\n"; + print $outputDevScanner "/* libusb header file */\n"; + print $outputDevScanner "#if (!WITH_LIBUSB_1_0) && (!WITH_LIBUSB_0_1)\n"; + print $outputDevScanner "#error \"configure script error: Neither WITH_LIBUSB_1_0 nor WITH_LIBUSB_0_1 is set\"\n"; + print $outputDevScanner "#endif\n\n"; + print $outputDevScanner "#if (WITH_LIBUSB_1_0) && (WITH_LIBUSB_0_1)\n"; + print $outputDevScanner "#error \"configure script error: Both WITH_LIBUSB_1_0 and WITH_LIBUSB_0_1 are set\"\n"; + print $outputDevScanner "#endif\n\n"; + print $outputDevScanner "#if WITH_LIBUSB_1_0\n"; + print $outputDevScanner " #include \n"; + print $outputDevScanner "#endif\n"; + print $outputDevScanner "#if WITH_LIBUSB_0_1\n"; + print $outputDevScanner " #include \n"; + print $outputDevScanner " /* simple remap to avoid bloating structures */\n"; + print $outputDevScanner " typedef usb_dev_handle libusb_device_handle;\n"; + print $outputDevScanner "#endif\n"; # vid, pid, driver print $outputDevScanner "typedef struct {\n\tuint16_t\tvendorID;\n\tuint16_t\tproductID;\n\tchar*\tdriver_name;\n} usb_device_id_t;\n\n"; print $outputDevScanner "/* USB IDs device table */\nstatic usb_device_id_t usb_device_table[] = {\n\n"; @@ -127,12 +152,6 @@ sub gen_usb_files # generate the file in alphabetical order (first for VendorID, then for ProductID) foreach my $vendorId (sort { lc $a cmp lc $b } keys %vendorName) { - # HAL vendor header - if ($vendorName{$vendorId}) { - print $outHAL "\n \n"; - } - print $outHAL " \n"; - # Hotplug vendor header if ($vendorName{$vendorId}) { print $outHotplug "\n# ".$vendorName{$vendorId}."\n"; @@ -143,20 +162,17 @@ sub gen_usb_files print $outUdev "\n# ".$vendorName{$vendorId}."\n"; } + # devd vendor header + if ($vendorName{$vendorId}) { + print $out_devd "\n# ".$vendorName{$vendorId}."\n"; + } + + # UPower vendor header flag - $upowerMfrHeaderDone = 0; + my $upowerVendorHasDevices = 0; foreach my $productId (sort { lc $a cmp lc $b } keys %{$vendor{$vendorId}}) { - # HAL device entry - print $outHAL " \n"; - print $outHAL " \n"; - print $outHAL ' battery'."\n"; - print $outHAL ' battery'."\n"; - print $outHAL " hald-addon-".$vendor{$vendorId}{$productId}{"driver"}."\n"; - print $outHAL ' ups'."\n"; - print $outHAL ' '."\n"; - # Hotplug device entry print $outHotplug "# ".$vendor{$vendorId}{$productId}{"comment"}."\n"; print $outHotplug "libhidups 0x0003 ".$vendorId." ".$productId." 0x0000 0x0000 0x00"; @@ -167,55 +183,57 @@ sub gen_usb_files print $outUdev "ATTR{idVendor}==\"".removeHexPrefix($vendorId); print $outUdev "\", ATTR{idProduct}==\"".removeHexPrefix($productId)."\","; print $outUdev ' MODE="664", GROUP="@RUN_AS_GROUP@"'."\n"; - + + # devd device entry + print $out_devd "# ".$vendor{$vendorId}{$productId}{"comment"}.' - '.$vendor{$vendorId}{$productId}{"driver"}."\n"; + print $out_devd "notify 100 {\n\tmatch \"system\"\t\t\"USB\";\n"; + print $out_devd "\tmatch \"subsystem\"\t\"DEVICE\";\n"; + print $out_devd "\tmatch \"type\"\t\t\"ATTACH\";\n"; + print $out_devd "\tmatch \"vendor\"\t\t\"$vendorId\";\n"; + # + print $out_devd "\tmatch \"product\"\t\t\"$productId\";\n"; + print $out_devd "\taction \"chgrp \@RUN_AS_GROUP\@ /dev/\$cdev; chmod g+rw /dev/\$cdev\";\n"; + print $out_devd "};\n"; + # UPower device entry (only for USB/HID devices!) if ($vendor{$vendorId}{$productId}{"driver"} eq "usbhid-ups") { - if ($upowerMfrHeaderDone == 0) - { - # UPower vendor header + if (!$upowerVendorHasDevices) { if ($vendorName{$vendorId}) { - $tmpOutputUPower = $tmpOutputUPower."\n# ".$vendorName{$vendorId}."\n"; + print $outputUPower "\n# ".$vendorName{$vendorId}."\n"; } - print $outputUPower "ATTRS{idVendor}==\"".removeHexPrefix($vendorId)."\", ENV{UPOWER_VENDOR}=\"".$vendorName{$vendorId}."\"\n"; - $upowerMfrHeaderDone = 1; + $upowerVendorHasDevices = 1; } - $tmpOutputUPower = $tmpOutputUPower."ATTRS{idVendor}==\"".removeHexPrefix($vendorId); - $tmpOutputUPower = $tmpOutputUPower."\", ATTRS{idProduct}==\"".removeHexPrefix($productId)."\","; - $tmpOutputUPower = $tmpOutputUPower.' ENV{UPOWER_BATTERY_TYPE}="ups"'."\n"; + print $outputUPower "usb:v".uc(removeHexPrefix($vendorId))."p".uc(removeHexPrefix($productId))."*\n"; } # Device scanner entry print $outputDevScanner "\t{ ".$vendorId.', '.$productId.", \"".$vendor{$vendorId}{$productId}{"driver"}."\" },\n"; } - # HAL vendor footer - print $outHAL " \n"; - } - # HAL footer - print $outHAL " \n"; - print $outHAL " \n"; - print $outHAL "\n"; + if ($upowerVendorHasDevices) { + print $outputUPower " UPOWER_BATTERY_TYPE=ups\n"; + if ($vendorName{$vendorId}) { + print $outputUPower " UPOWER_VENDOR=".$vendorName{$vendorId}."\n"; + } + } + + } # Udev footer print $outUdev "\n".'LABEL="nut-usbups_rules_end"'."\n"; - # UPower... - # ...flush device table - print $outputUPower $tmpOutputUPower; - # ...and print footer - print $outputUPower "\n".'LABEL="up_hid_end"'."\n"; - # Device scanner footer - print $outputDevScanner "\t/* Terminating entry */\n\t{ -1, -1, NULL }\n};\n#endif /* DEVSCAN_USB_H */\n\n"; + print $outputDevScanner "\n\t/* Terminating entry */\n\t{ 0, 0, NULL }\n};\n#endif /* DEVSCAN_USB_H */\n\n"; } sub find_usbdevs { - return $File::Find::prune = 1 if $_ eq '.svn'; + # maybe there's an option to turn off all .* files, but anyway this is stupid + return $File::Find::prune = 1 if ($_ eq '.svn') || ($_ =~ /^\.#/) || ($_ =~ /\.orig$/); my $nameFile=$_; my $lastComment=""; - + open my $file,$nameFile or die "error open file $nameFile"; while(my $line=<$file>) { @@ -225,7 +243,7 @@ sub find_usbdevs $lastComment=$1; } - if($line =~/^\s*\{\s*USB_DEVICE\((.+)\,(.+)\)\s*/) # for example : { USB_DEVICE(MGE_VENDORID, 0x0001)... } + if($line =~/^\s*\{\s*USB_DEVICE\(([^)]+)\,([^)]+)\)\s*/) # for example : { USB_DEVICE(MGE_VENDORID, 0x0001)... } { my $VendorID=trim($1); my $ProductID=trim($2); @@ -242,7 +260,7 @@ sub find_usbdevs { # catch Vendor Name if($data =~/\s*\/\*(.+)\*\/\s*$/) - { + { $VendorName=$1; } # catch VendorID @@ -267,8 +285,8 @@ sub find_usbdevs } } - # store date (to be optimized) - # and don't overwritte actual vendor names with empty values + # store data (to be optimized) + # and don't overwrite actual vendor names with empty values if( (!$vendorName{$VendorID}) or (($vendorName{$VendorID} eq "") and ($VendorName ne "")) ) { $vendorName{$VendorID}=trim($VendorName); @@ -279,22 +297,19 @@ sub find_usbdevs if($nameFile=~/(.+)-hid\.c/) { $driver="usbhid-ups"; } - # FIXME: make a generic matching rule *.c => * - elsif ($nameFile eq "bcmxcp_usb.c") { - $driver="bcmxcp_usb"; - } - elsif ($nameFile eq "tripplite_usb.c") { - $driver="tripplite_usb"; - } - elsif ($nameFile eq "blazer_usb.c") { - $driver="blazer_usb"; - } - elsif ($nameFile eq "richcomm_usb.c") { - $driver="richcomm_usb"; + # generic matching rule *.c => * + elsif ($nameFile =~ /(.+)\.c$/) { + $driver=$1; } else { die "Unknown driver type: $nameFile"; } + if ($vendor{$VendorID}{$ProductID}{"driver"} && $ENV{"DEBUG"}) { + print STDERR "nut-usbinfo.pl: VendorID=$VendorID ProductID=$ProductID " . + "was already related to driver '" . + $vendor{$VendorID}{$ProductID}{"driver"} . + "' and changing to '$driver'\n"; + } $vendor{$VendorID}{$ProductID}{"driver"}=$driver; } } diff --git a/tools/svn2cl.authors b/tools/svn2cl.authors deleted file mode 100644 index 99086b3..0000000 --- a/tools/svn2cl.authors +++ /dev/null @@ -1,13 +0,0 @@ -esr-guest:Eric S. Raymond -lestat-guest:David Goncalves -agordeev-guest:Alexander Gordeev -emilienkia-guest:Emilien Kia -prachi-guest:Prachi Gandhi -praveenkumar-guest:Praveen Kumar -msoltyspl-guest:Michal Soltys -keyson-guest:Kjell Claesson -chetanagarwal-guest:Chetan Agarwal -fbohe-guest:Frederic Bohe -aquette:Arnaud Quette -clepple-guest:Charles Lepple -adkorte-guest:Arjen de Korte